QT+FFMPEG+SDL2.0實現視頻播放


開發環境:MinGW+QT5.9+FFMPEG20190212+SDL2.0.9

一、開發環境搭建

(1)下載工具

https://ffmpeg.zeranoe.com/builds/下載對應版本。鏈接方式有三種,

Static:這個版本只包含了ffmpeg.exeffplay.exeffprobe.exe三個可執行程序,沒有頭文件和庫文件。

Shared:這個版本包含了ffmpeg.exeffplay.exeffprobe.exe三個可執行程序和相關動態庫文件。

Dev:開發版,這個包含了頭文件和庫文件。

我們需要下載SharedDev兩個版本,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);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM