基于FFmpeg的Qt视频播放器
ffmpeg作為開源庫,具備跨平臺性,被廣泛使用于各大視頻軟件和網站,在視音頻開發中占有極其重要的地位。
Qt同樣支持跨平臺,因此結合qt+ffmpeg制作跨平臺視頻播放器是比較合適的做法。
?一、環境介紹
操作系統: win10 64位
QT版本: ?QT5.14.0
編譯器: ?MinGW 64
FFMPEG版本: 4.2.2?
二、FFMPEG解碼步驟
?(1)首先定義需要用到的結構體,在本客戶端中,主要用到了以下結構體。
? ? ? ? AVFormatContext 保存需要讀入的文件的格式信息,比如流的個數以及流數據等
? ? ? ? AVCodecContext ?保存了相應流的詳細編碼信息,比如視頻的寬、高,編碼類型等。
? ? ? ? AVCodec 真正的編解碼器,其中有編解碼需要調用的函數
? ? ? ? AVFrame用于保存數據幀的數據結構
? ? ? ? AVFrame 用于保存轉換之后的幀
? ? ? ? SwsContext 轉換器,用于將YUV420P類型的圖片轉換為RGB類型
? ? ? ? AVPacket 解析文件時會將音/視頻幀讀入到packet中
?(2)注冊解碼器,并且初始化自定義的AVIOContext,目的是在主機內存中申請內存空間,并將AVFormatContext的pb指針指向它。在使用avformat_open_input()打開媒體數據的時候,就可以不指定文件的URL了,即其第2個參數為NULL,讀取的數據是由read_buffer()提供,read_buffer是回調函數,需要自定義read_buffer使其在視頻解碼時得到對應的數據。
? ? ? ? av_register_all();
? ? ? ? in_fmt_ctx = avformat_alloc_context();
? ? ? ? unsigned char *aviobuffer=(unsigned char *)av_malloc(1024*15);
? ? ? ? AVIOContext*avio=avio_alloc_context(aviobuffer,1024*15 ,0,NULL,read_buffer, NU L L,NULL);
? ? ? ? in_fmt_ctx->pb=avio;
? ? ? ? int read_buffer(void *opaque, uint8_t *buf, int buf_size){......}
(3)讀取接收到的數據的基本信息,用于設置解碼器類型。avformat_open_input函數只是讀取接收到的數據頭,并不會填充流信息,因此我們需要接下來調用avformat_find_stream_info獲取流信息,并確定所有的流信息。根據讀取到的信息設置解碼器的解碼類型和解碼的寬高度,使用avcodec_open2初始化解碼器。
? ? ? if(avformat_open_input(&in_fmt_ctx,NULL,NULL,NULL)!=0){......}
? ? ? if(avformat_find_stream_info(in_fmt_ctx,NULL)<0){......}
? ? ? videoindex=-1;
? ? ? for(i=0; i<in_fmt_ctx->nb_streams; i++) ? {.....}//在這里查找視頻流
? ? ? pCodecCtx=in_fmt_ctx->streams[videoindex]->codec;
? ? ? pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
? ? ? if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){......}
(4)使用av_read_frame可以將從內存中讀取的一幀數據存入packet中。通過avcodec_decode_video2調用相應的解碼器,將解碼后的數據存入AVFrame 中。解碼之后存入AVFrame中數數據是YUV420P類型的,Y、U、V分別存放在pFrame->data[0]、pFrame->data[1]、pFrame->data[2]中。主要用到的函數為:
? ? ? ?if(av_read_frame(in_fmt_ctx, packet)>=0){......}
? ? ? ?avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
三、部分代碼及ui
?
//使用FFmpeg播放視頻 int MainWindow::playVideo(char* videoPath) {unsigned char* buf;int isVideo = -1;int ret, gotPicture;unsigned int i, streamIndex = 0;AVCodec *pCodec;AVPacket *pAVpkt;AVCodecContext *pAVctx;AVFrame *pAVframe, *pAVframeRGB;AVFormatContext* pFormatCtx;struct SwsContext* pSwsCtx;//創建AVFormatContextpFormatCtx = avformat_alloc_context();//初始化pFormatCtxif (avformat_open_input(&pFormatCtx, videoPath, NULL, NULL) != 0){qDebug("avformat_open_input err.");return -1;}//獲取音視頻流數據信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0){avformat_close_input(&pFormatCtx);qDebug("avformat_find_stream_info err.");return -2;}//找到視頻流的索引for (i = 0; i < pFormatCtx->nb_streams; i++){if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){streamIndex = i;isVideo = 0;break;}}//沒有視頻流就退出if (isVideo == -1){avformat_close_input(&pFormatCtx);qDebug("nb_streams err.");return -3;}//獲取視頻流編碼pAVctx = avcodec_alloc_context3(NULL);;//查找解碼器avcodec_parameters_to_context(pAVctx, pFormatCtx->streams[streamIndex]->codecpar);pCodec = avcodec_find_decoder(pAVctx->codec_id);if (pCodec == NULL){avcodec_close(pAVctx);avformat_close_input(&pFormatCtx);qDebug("avcodec_find_decoder err.");return -4;}//初始化pAVctxif (avcodec_open2(pAVctx, pCodec, NULL) < 0){avcodec_close(pAVctx);avformat_close_input(&pFormatCtx);qDebug("avcodec_open2 err.");return -5;}//初始化pAVpktpAVpkt = (AVPacket *)av_malloc(sizeof(AVPacket));//初始化數據幀空間pAVframe = av_frame_alloc();pAVframeRGB = av_frame_alloc();//創建圖像數據存儲buf//av_image_get_buffer_size一幀大小buf = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1));av_image_fill_arrays(pAVframeRGB->data, pAVframeRGB->linesize, buf, AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1);//根據視頻寬高重新調整窗口大小 視頻寬高 pAVctx->width, pAVctx->height//resizeWindow(pAVctx->width, pAVctx->height);//初始化pSwsCtxpSwsCtx = sws_getContext(pAVctx->width, pAVctx->height, pAVctx->pix_fmt, pAVctx->width, pAVctx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);//循環讀取視頻數據for(;;){if(mVideoPlaySta == Video_Playing)//正在播放{if(av_read_frame(pFormatCtx, pAVpkt) >= 0)//讀取一幀未解碼的數據{//如果是視頻數據if (pAVpkt->stream_index == (int)streamIndex){//解碼一幀視頻數據ret = avcodec_decode_video2(pAVctx, pAVframe, &gotPicture, pAVpkt);if(ret < 0){qDebug("Decode Error.\n");continue;}if(gotPicture){sws_scale(pSwsCtx, (const unsigned char* const*)pAVframe->data, pAVframe->linesize, 0, pAVctx->height, pAVframeRGB->data, pAVframeRGB->linesize);QImage img((uchar*)pAVframeRGB->data[0], pAVctx->width, pAVctx->height, QImage::Format_RGB32);ui->labelVideo->setPixmap(QPixmap::fromImage(img.scaled(ui->labelVideo->width(),ui->labelVideo->height())));delay(mPlaySpdVal);//播放延時}}av_packet_unref(pAVpkt);}else{break;}}else if(mVideoPlaySta == Video_PlayFinish)//播放結束{break;}else//暫停{delay(300);}}//釋放資源sws_freeContext(pSwsCtx);av_frame_free(&pAVframeRGB);av_frame_free(&pAVframe);avcodec_close(pAVctx);avformat_close_input(&pFormatCtx);mVideoPlaySta = Video_PlayFinish;qDebug()<<"play finish!";return 0; }?
void VideoPlayer::ffmpeg_init() {int i, numBytes;int ret;avformat_network_init(); //初始化FFmpeg網絡模塊av_register_all(); //初始化FFMPEG 調用了這個才能正常適用編碼器和解碼器//Allocate an AVFormatContext.in_fmt_ctx = avformat_alloc_context();AVDictionary *avdic=NULL;char option_key[]="rtsp_transport";char option_value[]="tcp";av_dict_set(&avdic,option_key,option_value,0);char option_key2[]="max_delay";char option_value2[]="100";av_dict_set(&avdic,option_key2,option_value2,0);qDebug() << "VideoPlayer init urlAddr ="<<urlAddr;if (avformat_open_input(&in_fmt_ctx, urlAddr, NULL, &avdic) != 0) {printf("can't open the file. \n");return;}qDebug() << "VideoPlayer init 2";if (avformat_find_stream_info(in_fmt_ctx, NULL) < 0) {printf("Could't find stream infomation.\n");return;}video_index = -1;//循環查找視頻中包含的流信息,直到找到視頻類型的流//便將其記錄下來 保存到video_index變量中//這里我們現在只處理視頻流 音頻流先不管他for (i = 0; i < in_fmt_ctx->nb_streams; i++) {if (in_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {video_index = i;}}//如果video_index為-1 說明沒有找到視頻流if (video_index == -1) {printf("Didn't find a video stream.\n");return;}//查找解碼器pCodecCtx = in_fmt_ctx->streams[video_index]->codec;if(pCodecCtx->codec_id == AV_CODEC_ID_H264){qDebug() <<"h264 編碼的視頻流";}pCodec = avcodec_find_decoder(pCodecCtx->codec_id);pCodecCtx->bit_rate =0; //初始化為0pCodecCtx->time_base.num=1; //下面兩行:一秒鐘25幀pCodecCtx->time_base.den=25;pCodecCtx->frame_number=1; //每包一個視頻幀if (pCodec == NULL) {printf("Codec not found.\n");return;}//打開解碼器if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {printf("Could not open codec.\n");return;}/*******************************************************************************************************/pFrame = av_frame_alloc();pFrameRGB = av_frame_alloc();//這里我們改成了 將解碼后的YUV數據轉換成RGB32img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,pCodecCtx->width, pCodecCtx->height);packet = av_packet_alloc();pauseFlag = false;stopFlag = false;qDebug() << "VideoPlayer init end"; }?ui界面
?四、演示效果
遠程播放效果
本地播放效果
?
?
?
總結
以上是生活随笔為你收集整理的基于FFmpeg的Qt视频播放器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mac终端命令大全介绍
- 下一篇: css半透明渐变过渡效果