開發環境:MinGW+QT5.9+FFMPEG20190212+SDL2.0.9
一、開發環境搭建
(1)下載工具
在https://ffmpeg.zeranoe.com/builds/下載對應版本。鏈接方式有三種,
Static:這個版本只包含了ffmpeg.exe、ffplay.exe、ffprobe.exe三個可執行程序,沒有頭文件和庫文件。
Shared:這個版本包含了ffmpeg.exe、ffplay.exe、ffprobe.exe三個可執行程序和相關動態庫文件。
Dev:開發版,這個包含了頭文件和庫文件。
我們需要下載Shared和Dev兩個版本,Dev有我們程序開發需要的頭文件和庫文件,這里面包含的庫是動態調用的,所依賴的動態庫在Shared這個版本里面,所以兩個版本都要下載。
在http://www.libsdl.org/download-2.0.php下載SDL庫,選擇
(2)添加庫
將下載的文件解壓縮,然后新建一個QT工程,在pro添加lib目錄和include目錄的路徑。
INCLUDEPATH +="E:\\Lib\\ffmpeg\\include" INCLUDEPATH +="E:\\Lib\\SDL2-2.0.9\\include" LIBS += -LE:\Lib\ffmpeg\lib -lavutil -lavformat -lavcodec -lavdevice -lavfilter -lpostproc -lswresample -lswscale LIBS += -LE:\Lib\SDL2-2.0.9\lib\x86 -lSDL2
然后將ffmpeg的dll和SDL2.dll放到exe目錄下。
二、代碼實現:
在QT界面上放置一個widget和一個按鈕,點擊按鈕時實現下面功能:
extern "C"{ #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libavutil/imgutils.h> #include <SDL.h> } void MainWindow::on_btnPlay_clicked() { AVFormatContext *pFormatCtx; int i, videoindex; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame, *pFrameYUV; unsigned char *out_buffer; AVPacket *packet; int ret, got_picture; struct SwsContext *img_convert_ctx; char filepath[] = "E:\\media\\1.avi"; //初始化編解碼庫 av_register_all(); //已經無需使用的函數? //avformat_network_init(); //創建AVFormatContext對象,與碼流相關的結構。 pFormatCtx = avformat_alloc_context(); //初始化pFormatCtx結構 if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0){ printf("Couldn't open input stream.\n"); return ; } //獲取音視頻流數據信息 if (avformat_find_stream_info(pFormatCtx, NULL) < 0){ printf("Couldn't find stream information.\n"); return ; } videoindex = -1; //nb_streams視音頻流的個數,這里當查找到視頻流時就中斷了。 for (i = 0; i < pFormatCtx->nb_streams; i++) if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){ videoindex = i; break; } if (videoindex == -1){ printf("Didn't find a video stream.\n"); return ; } //獲取視頻流編碼結構 pCodecCtx = pFormatCtx->streams[videoindex]->codec; //查找解碼器 pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL){ printf("Codec not found.\n"); return ; } //用於初始化pCodecCtx結構 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){ printf("Could not open codec.\n"); return ; } //創建幀結構,此函數僅分配基本結構空間,圖像數據空間需通過av_malloc分配 pFrame = av_frame_alloc(); pFrameYUV = av_frame_alloc(); //創建動態內存,創建存儲圖像數據的空間 //av_image_get_buffer_size獲取一幀圖像需要的大小 out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, ui->widget->width(), ui->widget->height(), 1)); av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer, AV_PIX_FMT_YUV420P, ui->widget->width(), ui->widget->height(), 1); packet = (AVPacket *)av_malloc(sizeof(AVPacket)); //Output Info----------------------------- printf("--------------- File Information ----------------\n"); //此函數打印輸入或輸出的詳細信息 av_dump_format(pFormatCtx, 0, filepath, 0); printf("-------------------------------------------------\n"); //初始化img_convert_ctx結構 img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, ui->widget->width(), ui->widget->height(), AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //SDL--------------------------- SDL_Window *screen; SDL_Renderer* sdlRenderer; SDL_Texture* sdlTexture; SDL_Rect sdlRect; if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf("Could not initialize SDL - %s\n", SDL_GetError()); return; } screen = SDL_CreateWindowFrom((void *)ui->widget->winId()); if(screen==NULL) { printf("Could not create window - %s\n", SDL_GetError()); return; } sdlRenderer = SDL_CreateRenderer(screen, -1, 0); sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height); sdlRect.x = 0; sdlRect.y = 0; sdlRect.w = pCodecCtx->width; sdlRect.h = pCodecCtx->height; //end SDL----------------------- //av_read_frame讀取一幀未解碼的數據 while (av_read_frame(pFormatCtx, packet) >= 0){ //如果是視頻數據 if (packet->stream_index == videoindex){ //解碼一幀視頻數據 ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if (ret < 0){ printf("Decode Error.\n"); return ; } if (got_picture){ sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0], pFrameYUV->data[1], pFrameYUV->linesize[1], pFrameYUV->data[2], pFrameYUV->linesize[2]); SDL_RenderClear(sdlRenderer); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect); SDL_RenderPresent(sdlRenderer); SDL_Delay(40); } } av_free_packet(packet); } sws_freeContext(img_convert_ctx); av_frame_free(&pFrameYUV); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); }