#pragma once #include <QThread> #include <QImage> class VideoPlayer :public QThread { Q_OBJECT public: VideoPlayer(); ~VideoPlayer(); void run(); private: // 延时函数 void delay(int msec); signals: void sig_GetOneFrame(QImage); };
#include "VideoPlayer.h" #include <QCoreApplication> #include <QTime> #include <iostream> #include <QDebug> // 调用FFmpeg的头文件 extern "C"{ #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" #include "libavutil/pixfmt.h" } using namespace std; VideoPlayer::VideoPlayer() { } VideoPlayer::~VideoPlayer() { } void VideoPlayer::run() { //1.初始化ffmpeg av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器 avformat_network_init(); // 初始化网络模块 //=========================== 创建AVFormatContext结构体 ===============================// //分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行 AVFormatContext *pFormatCtx = avformat_alloc_context(); //==================================== 打开文件 ======================================// char *file_path = "./2.mp4" ;//这里必须使用左斜杠 int ret = avformat_open_input(&pFormatCtx, file_path, NULL, NULL); if (ret != 0) { qDebug() << "open error!"; return; } //循环查找视频中包含的流信息,直到找到视频类型的流 //便将其记录下来 保存到videoStream变量中 int i; int videoStream; //=================================== 获取视频流信息 ===================================// if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { qDebug() << "Could't find stream infomation."; return; } videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; } } //如果videoStream为-1 说明没有找到视频流 if (videoStream == -1) { qDebug() << "Didn't find a video stream."; return; } //================================= 查找解码器 ===================================// AVCodecContext* pCodecCtx = pFormatCtx->streams[videoStream]->codec; pCodecCtx->time_base.num = 1; pCodecCtx->time_base.den = 25; AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { qDebug() << "Codec not found."; return; } //================================ 打开解码器 ===================================// if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)// 具体采用什么解码器ffmpeg经过封装 我们无须知道 { qDebug() << "Could not open codec."; return; } //================================ 设置数据转换参数 ================================// SwsContext * img_convert_ctx; img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, //源地址长宽以及数据格式 pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, //目的地址长宽以及数据格式 SWS_BICUBIC, NULL, NULL, NULL);//算法类型 AV_PIX_FMT_YUVJ420P AV_PIX_FMT_BGR24 //与图片不相同 //img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, //源地址长宽以及数据格式 // pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUVJ420P, //目的地址长宽以及数据格式 // SWS_BICUBIC, NULL, NULL, NULL);//算法类型 AV_PIX_FMT_YUVJ420P AV_PIX_FMT_BGR24 //==================================== 分配空间 ==================================// //一帧图像数据大小 int numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height); //int numBytes = avpicture_get_size(AV_PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);//与图片不相同的 unsigned char *out_buffer; out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char)); AVFrame * pFrame; pFrame = av_frame_alloc(); AVFrame * pFrameRGB; pFrameRGB = av_frame_alloc(); //avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height); avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height); //会将pFrameRGB的数据按RGB格式自动"关联"到buffer 即pFrameRGB中的数据改变了 out_buffer中的数据也会相应的改变 //=========================== 分配AVPacket结构体 ===============================// int y_size = pCodecCtx->width * pCodecCtx->height; AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket)); //分配一个packet av_new_packet(packet, y_size); //分配packet的数据 //打印输入和输出信息:长度 比特率 流格式等 av_dump_format(pFormatCtx, 0, file_path, 0); //输出视频信息Image)),this,SLOT(slotGetOneFrame(QImage))); // //2.=========================== 读取视频信息 ===============================// int index = 0; //读取的是一帧视频 数据存入一个AVPacket的结构中 while (av_read_frame(pFormatCtx, packet) >= 0) { //此时数据存储在packet中 if (packet && packet->stream_index == videoStream) { //3.视频解码函数 解码之后的数据存储在 pFrame中 int got_picture = 0; ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if (ret < 0) { cout << "decode error." << endl; return; } //转换一帧图像 4. YUV->RGB sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, //源 pFrameRGB->data, pFrameRGB->linesize); //目的 QImage tmpImg((uchar *)out_buffer, pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32); QImage image = tmpImg.copy(); emit sig_GetOneFrame(image); //发送信号 delay(100); } } free(packet); av_frame_free(&pFrameRGB); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_free_context(pFormatCtx); } void VideoPlayer::delay(int msec) { QTime dieTime = QTime::currentTime().addMSecs(msec); while (QTime::currentTime() < dieTime) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); }
private:
VideoPlayer *VideoPlayerThread;
private slots:
void slotGetOneFrame(QImage);
VideoPlayerThread = new VideoPlayer();
connect(VideoPlayerThread, SIGNAL(sig_GetOneFrame(QImage)), this, SLOT(slotGetOneFrame(QImage)));
void FFmpegDemo::on_playerBtn_clicked() { if (VideoPlayerThread != NULL) { VideoPlayerThread->start(); } } void FFmpegDemo::slotGetOneFrame(QImage img) { ui.label->setPixmap(QPixmap::fromImage(img)); // 在label上播放视频图片 }
参考:
https://www.cnblogs.com/linuxAndMcu/p/12046600.html
https://mp.weixin.qq.com/s/tEGF6KJKx9xyY475c30ceQ