ffmpeg第五彈:Qt+SDL+ffmpeg視頻播放演示

2021-03-02 txp玩Linux
一、前言

在前幾篇文章當中,有提到過用源碼去搭建ffmpeg的命令環境開發,為啥要這樣去搭建環境,為什麼不用直接用下面這個命令在ubuntu下安裝多快,簡單又方便:

sudo apt install ffmpeg

今天分享ffmepg第五彈:ffmpeg+qt+SDL的真正開發環境,就要用源碼安裝的方式去在qt裡面調用ffmpeg相關的庫;還記得之前源碼搭建創建的三個文件夾不:

bin   ffmpeg_sources    ffmpeg_build

bin文件夾下是編譯得到的二進位文件

txp@txp-virtual-machine:~/bin$ ls
ffmpeg  ffplay  ffprobe  lame  nasm  
ndisasm  x264

ffmpeg_sources文件下是下載的各種庫的源碼:

txp@txp-virtual-machine:~/ffmpeg_sources$ ls
fdk-aac                  lame-3.100.tar.gz     SDL2-2.0.14.tar.gz
ffmpeg                   libvpx                SVT-AV1
ffmpeg-4.2.1             nasm-2.14.02          x264
ffmpeg-4.2.1.tar.bz2     nasm-2.14.02.tar.bz2  x265_git
ffmpeg-snapshot.tar.bz2  opus
lame-3.100               SDL2-2.0.14

ffmpeg_build文件夾主要是ffmpeg的一些庫文件,等下下面演示的模板就要調用ffmpeg相關的庫:

txp@txp-virtual-machine:~/ffmpeg_build/lib$ ls
cmake          libmp3lame.a           libSDL2.la       libswscale.a
libavcodec.a   libmp3lame.la          libSDL2main.a    libvpx.a
libavdevice.a  libopus.a              libSDL2main.la   libx264.a
libavfilter.a  libopus.la             libSDL2.so       libx265.a
libavformat.a  libpostproc.a          libSDL2_test.a   pkgconfig
libavutil.a    libSDL2-2.0.so.0       libSDL2_test.la
libfdk-aac.a   libSDL2-2.0.so.0.14.0  libSvtAv1Enc.a
libfdk-aac.la  libSDL2.a              libswresample.a

因為我已經搭建好了開發環境,從現在來看的話,如果你直接用命令去安裝ffmpeg的話,到時候我們在qt的環境中去調用ffmpeg的庫,至少到目前為止我暫時不知道去如何配置相關路徑來調用ffmpeg的庫;所以我們明白了這點,那麼就擼起袖子肝就是。

二、 qt環境搭建

玩過qt的朋友,對於這塊應該比我熟悉多了;不過有可能有一些朋友可能沒有接觸過qt的話,為此我還是簡單演示一下qt的安裝步驟:

首先我們要去qt的官網下載linux版本的源碼安裝包:
版本下載地址:
https://download.qt.io/archive/qt/5.12/5.12.10/

這裡我直接把qt的源碼包下載到samba服務共享文件下,當然你也可以直接在ubuntu下載:

然後我在share目錄創建一個qt文件夾,用存放qt安裝的地方,然後直接運行這個源碼文件:

接著就會出現qt的安裝界面:

最終qt就安裝完成了,但是如果你運行qt執行失敗的話(注意qtcreator的所放在的路線):

txp@txp-virtual-machine:~/share/qt/Qt5.12.10/Tools/QtCreator/bin$ sudo ./qtcreator

Got keys from plugin meta data ("xcb")
QFactoryLoader::QFactoryLoader() checking directory path "/home/txp/share/qt/Qt5.12.10/Tools/QtCreator/bin/platforms" ...
loaded library "/home/txp/share/qt/Qt5.12.10/Tools/QtCreator/lib/Qt/plugins/platforms/libqxcb.so"
loaded library "Xcursor"
Segmentation fault (core dumped)

在這個腳本裡面添加一句話:

然後接著再安裝相關插件:

sudo apt install --reinstall libxcb-xinerama0

然後我們執行一下,就可以成功打開qt了:

還有一種方法直接打開qt,因為按照上面的這種方式打開的話,每次都要跑到這個目錄去執行這條語句才行:

txp@txp-virtual-machine:~/share/qt/Qt5.12.10/Tools/QtCreator/bin$ 
sudo ./qtcreator

現在我們只要執行下面這條語句就不用這麼麻煩了:

sudo chown -R txp:txp ~/.config/

然後你就可以像在windows環境下去直接打開這個軟體就行:

三、牛刀小試,調用ffmpeg庫 3.1 示例模板,顯示列印ffmpeg的版本:

現在我們打開剛才安裝好的qt軟體,來創建一個工程:

最終一個工程項目就建立好了:

我們可以看到兩個文件,一個是以.pro結尾的qt工程管理配置文件,一個主源碼文件,現在我們就簡單使用ffmpeg庫來列印ffmpeg的版本號:

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += \
        main.c

現在我們要加入ffmpeg_build目錄下的ffmpeg庫文件:

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += \
        main.c
INCLUDEPATH += /home/txp/ffmpeg_build/include
#LIBS += /home/txp/ffmpeg_build/lib/libSDL2.so

LIBS += /home/txp/ffmpeg_build/lib/libavcodec.a \
        /home/txp/ffmpeg_build/lib/libavdevice.a \
        /home/txp/ffmpeg_build/lib/libavfilter.a \
        /home/txp/ffmpeg_build/lib/libavformat.a \
        /home/txp/ffmpeg_build/lib/libavutil.a \
        /home/txp/ffmpeg_build/lib/libswresample.a \
        /home/txp/ffmpeg_build/lib/libswscale.a


#include <stdio.h>
//包含ffmpeg頭⽂件
#include "libavutil/avutil.h"
int main()
{
    printf("Hello FFMPEG, version is %s\n", av_version_info());
    return 0;
}

最終結果,顯示ffmpeg的版本為 4.2.1:

20:01:19: Starting /home/txp/share/qt/workspace/build-linux_1-ffmpeg-Desktop_Qt_5_12_10_GCC_64bit-Debug/linux_1-ffmpeg ...
Hello FFMPEG, version is 4.2.1
20:01:19: /home/txp/share/qt/workspace/build-linux_1-ffmpeg-Desktop_Qt_5_12_10_GCC_64bit-Debug/linux_1-ffmpeg exited with code 0

注意:我們的ubuntu運行環境一定要是64位的,不然安裝不了這個版本的qt;還有一點,其實我們在.pro文件裡面,按鍵盤上的ctrl鍵然後把滑鼠放到庫文件路徑上,是可以打開裡面的:

3.2、搭建SDL,然後播放yuv格式的視頻文件:

關於什麼是SDL,這裡我就不造輪子了,可以參考雷神的文章介紹:

https://blog.csdn.net/leixiaohua1020/article/details/11954039

下面我們去SDL的官網下源碼包就行安裝:

https://www.libsdl.org/download-2.0.php

開始安裝,先把這個源碼包放到ffmpeg_sources目錄下去,然後進行解壓:
txp@txp-virtual-machine:~/ffmpeg_sources$ tar zxf SDL2-2.0.14.tar.gz 
txp@txp-virtual-machine:~/ffmpeg_sources$ ls
fdk-aac                  lame-3.100.tar.gz     SDL2-2.0.14.tar.gz
ffmpeg                   libvpx                SVT-AV1
ffmpeg-4.2.1             nasm-2.14.02          x264
ffmpeg-4.2.1.tar.bz2     nasm-2.14.02.tar.bz2  x265_git
ffmpeg-snapshot.tar.bz2  opus
lame-3.100               SDL2-2.0.14

然後執行:

txp@txp-virtual-machine:~/ffmpeg_sources/SDL2-2.0.14$ ./autogen.sh 
Generating build information using autoconf
This may take a while ...
Now you are ready to run ./configure

這裡提示了你直接運行 ./configure:

 ./configure --prefix=/home/txp/ffmpeg_build --bindir=/home/txp/bin 

txp@txp-virtual-machine:~/ffmpeg_sources/SDL2-2.0.14$ 
make -j4

最後再執行sudo make install就行,SDL就安裝成功了:
txp@txp-virtual-machine:~/ffmpeg_sources/SDL2-2.0.14$ 
sudo make install

下面我再創建一個工程,具體過程我就再寫了,和第一個工程創建是一樣的:這裡我的main.c文件裡面的原始碼,大家先不用管代碼具體啥意思:

#include <stdio.h>
#include <string.h>
#include "SDL2/SDL.h"//包含SDL動態庫文件

//自定義消息類型
#define REFRESH_EVENT   (SDL_USEREVENT + 1)     // 請求畫面刷新事件
#define QUIT_EVENT      (SDL_USEREVENT + 2)     // 退出事件

//定義解析度
// YUV像素解析度
#define YUV_WIDTH   320
#define YUV_HEIGHT  240
//定義YUV格式
#define YUV_FORMAT  SDL_PIXELFORMAT_IYUV

int s_thread_exit = 0;  // 退出標誌 = 1則退出

int refresh_video_timer(void *data)
{
    while (!s_thread_exit)
    {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }

    s_thread_exit = 0;

    //push quit event
    SDL_Event event;
    event.type = QUIT_EVENT;
    SDL_PushEvent(&event);

    return 0;
}
#undef main
int main(int argc, char* argv[])
{
    //初始化 SDL
    if(SDL_Init(SDL_INIT_VIDEO))
    {
        fprintf( stderr, "Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }

    // SDL
    SDL_Event event;                            // 事件
    SDL_Rect rect;                              // 矩形
    SDL_Window *window = NULL;                  // 窗口
    SDL_Renderer *renderer = NULL;              // 渲染
    SDL_Texture *texture = NULL;                // 紋理
    SDL_Thread *timer_thread = NULL;            // 請求刷新線程
    uint32_t pixformat = YUV_FORMAT;            // YUV420P,即是SDL_PIXELFORMAT_IYUV

    // 解析度
    // 1. YUV的解析度
    int video_width = YUV_WIDTH;
    int video_height = YUV_HEIGHT;
    // 2.顯示窗口的解析度
    int win_width = YUV_WIDTH;
    int win_height = YUV_WIDTH;

    // YUV文件句柄
    FILE *video_fd = NULL;
    const char *yuv_path = "yuv420p_320x240.yuv";

    size_t video_buff_len = 0;

    uint8_t *video_buf = NULL; //讀取數據後先把放到buffer裡面

    // 我們測試的文件是YUV420P格式
    uint32_t y_frame_len = video_width * video_height;
    uint32_t u_frame_len = video_width * video_height / 4;
    uint32_t v_frame_len = video_width * video_height / 4;
    uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;

    //創建窗口
    window = SDL_CreateWindow("Simplest YUV Player",
                           SDL_WINDOWPOS_UNDEFINED,
                           SDL_WINDOWPOS_UNDEFINED,
                           video_width, video_height,
                           SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
    if(!window)
    {
        fprintf(stderr, "SDL: could not create window, err:%s\n",SDL_GetError());
        goto _FAIL;
    }
    // 基於窗口創建渲染器
    renderer = SDL_CreateRenderer(window, -1, 0);
    // 基於渲染器創建紋理
    texture = SDL_CreateTexture(renderer,
                                pixformat,
                                SDL_TEXTUREACCESS_STREAMING,
                                video_width,
                                video_height);

    // 分配空間
    video_buf = (uint8_t*)malloc(yuv_frame_len);
    if(!video_buf)
    {
        fprintf(stderr, "Failed to alloce yuv frame space!\n");
        goto _FAIL;
    }

    // 打開YUV文件
    video_fd = fopen(yuv_path, "rb");
    if( !video_fd )
    {
        fprintf(stderr, "Failed to open yuv file\n");
        goto _FAIL;
    }
    // 創建請求刷新線程
    timer_thread = SDL_CreateThread(refresh_video_timer,
                                    NULL,
                                    NULL);

    while (1)
    {
        // 收取SDL系統裡面的事件
        SDL_WaitEvent(&event);

        if(event.type == REFRESH_EVENT) // 畫面刷新事件
        {
            video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);
            if(video_buff_len <= 0)
            {
                fprintf(stderr, "Failed to read data from yuv file!\n");
                goto _FAIL;
            }
            // 設置紋理的數據 video_width = 320, plane
            SDL_UpdateTexture(texture, NULL, video_buf, video_width);

            // 顯示區域,可以通過修改w和h進行縮放
            rect.x = 0;
            rect.y = 0;
            float w_ratio = win_width * 1.0 /video_width;
            float h_ratio = win_height * 1.0 /video_height;
            // 320x240 怎麼保持原視頻的寬高比例
            rect.w = video_width * w_ratio;
            rect.h = video_height * h_ratio;
//            rect.w = video_width * 0.5;
//            rect.h = video_height * 0.5;

            // 清除當前顯示
            SDL_RenderClear(renderer);
            // 將紋理的數據拷貝給渲染器
            SDL_RenderCopy(renderer, texture, NULL, &rect);
            // 顯示
            SDL_RenderPresent(renderer);
        }
        else if(event.type == SDL_WINDOWEVENT)
        {
            //If Resize
            SDL_GetWindowSize(window, &win_width, &win_height);
            printf("SDL_WINDOWEVENT win_width:%d, win_height:%d\n",win_width,
                   win_height );
        }
        else if(event.type == SDL_QUIT) //退出事件
        {
            s_thread_exit = 1;
        }
        else if(event.type == QUIT_EVENT)
        {
            break;
        }
    }

_FAIL:
    s_thread_exit = 1;      // 保證線程能夠退出
    // 釋放資源
    if(timer_thread)
        SDL_WaitThread(timer_thread, NULL); // 等待線程退出
    if(video_buf)
        free(video_buf);
    if(video_fd)
        fclose(video_fd);
    if(texture)
        SDL_DestroyTexture(texture);
    if(renderer)
        SDL_DestroyRenderer(renderer);
    if(window)
        SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;

}

.pro文件配置成:

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += \
        main.c
INCLUDEPATH += /home/txp/ffmpeg_build/include
LIBS += /home/txp/ffmpeg_build/lib/libSDL2.so

最終運行結果就可以看到在播放一個yuv格式的視頻文件了:

註:這裡的播放yuv格式的視頻文件,我是事先已經準備好的:

同時要注意我們要把播放的視頻文件放到工程目錄下,不然播放是不會成功的:

四、總結:

現在ffmpeg真正的開發環境已經搭建完了,其實上面搭建環境蠻折騰人的,特別是源碼安裝ffmpeg。好了今天的文章就分享到這裡了,如果你在看完文章後,有不懂的地方可以後臺私聊我。

我是txp,一個半路出家的程序猿打工仔,我們下期見!

站在巨人的肩膀上:

https://blog.csdn.net/u012768805/article/details/98756925

https://ke.qq.com/webcourse/index.html#cid=468797&term_id=100561187&taid=4217090949457725&vid=5285890812708218640

https://www.libsdl.org/download-2.0.php

相關焦點

  • ffmpeg常用命令集錦
    在實際工作中,通常需要ffmpeg作為工具來驗證一個問題,比如播放一個視頻,提取一個碼流,轉碼視頻,轉封格式等,用的時候才發現忘記了相關命令,Google
  • ffmpeg數據結構簡介
    iformat:輸入視頻的AVInputFormatnb_streams :輸入視頻的AVStream 個數streams :輸入視頻的AVStream []數組duration :輸入視頻的時長(以微秒為單位)bit_rate :輸入視頻的碼率2、 AVInputFormat 每種封裝格式(例如FLV, MKV,
  • FFmpeg 時間戳詳解
    視頻中由於 B 幀需要雙向預測,B 幀依賴於其前和其後的幀,因此含 B 幀的視頻解碼順序與顯示順序不同,即 DTS 與 PTS 不同。當然,不含 B 幀的視頻,其 DTS 和 PTS 是相同的。下圖以一個開放式 GOP 示意圖為例,說明視頻流的解碼順序和顯示順序。
  • 教你如何用youtube-dl下載YouTube視頻
    第四步,安裝ffmpeg。這個ffmpeg啊,功能十分強大,改天我寫個入門專題~說遠了。youtube-dl依賴於ffmpeg對下載視頻進行編輯操作。在這裡下載windows版ffmpeg:https://ffmpeg.zeranoe.com/builds/ ,下載完成後是一個zip壓縮包,解壓之,把它放在合適的目錄裡。比如我放到了這。
  • Linux 下最好的圖片截取和視頻截錄工具 | Linux 中國
    除了視頻錄製功能之外,你還可以切換到 Studio 模式,不藉助其他軟體進行視頻編輯。要在你的 Linux 系統中安裝 OBS,你必須確保你的電腦已安裝 FFmpeg。ubuntu 14.04 或更早的版本安裝 FFmpeg 可以使用如下命令:sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-nextsudo apt-get update && sudo apt-get install ffmpegubuntu 15.04 以及之後的版本
  • 遙測視頻傳輸與實時解析
    轉換後的傳輸流以「超採」的方式均勻地放置在 PCM 格式格柵中( 如圖 2 所示的 25 × 27 的 PCM主幀中就包含黃色、紅色和棕色等 3 路遙測視頻信息) ,均勻的編碼方式保證了各路視頻之間的同步性。但是, 這種單位時間內固定傳輸帶寬的編碼策略,也為那些動態變化較大的視頻解析和播放帶來了較大挑戰。
  • FFmpeg 作者法布裡斯•貝拉:我只是在做我感興趣的事
    FFMPEG分割成幾個部分,由 libavcodec和libformat 構成,Libavcodec收集音頻和視頻編解碼庫,Libavformat提供音頻和視頻容器復用及解復用庫,這兩個模塊結合起來提供了解析和在不同格式之間進行轉換的各種方法。這個項目無比強大,我們今天所熟知的視頻播放軟體,如暴風影音、QQ影音、YouTube、VLC等都使用了FFmpeg的編解碼函數庫。
  • Mockplus演示和分享原型設計的8種方式
    操作方式:主菜單,「導出」,「導出HTML演示」。演示支持環境:瀏覽器。由於演示包內含了Mockplus的支持環境,因此,演示時可以最大程度保證演示效果和設計效果完全一致,不受各種瀏覽器的兼容問題影響。操作方式:主菜單,「導出」,「導出演示包」。演示支持環境:不需要其它軟體支持。
  • 【推薦裝備】MP5K快拔套掛載系統(帶演示視頻)
    產品演示視頻一產品演示視頻二產品演示視頻三  操作注意事項該款快拔套實測適配HQ工業、錦明、高爾基的MP5K。手機淘寶淘口令連結地址:【水彈玩具MP5K快拔套掛載系統】,復ず■淛這句話₴bJKvYFLByRB₴後咑閞淘宀┡ē如若連結失效,可以淘寶搜索「店鋪」【飛刀僱傭兵工作室】諮詢客服購買。【完】
  • Enlighten引擎Demo演示第二彈 完美支持PS4打造深度動態光照
    不過昨日的Demo演示應該是第一部分,因為現在外媒公布了最新的Enlighten引擎渲染視頻。如果昨日還沒有盡興,那就再看看Enlighten引擎技術Demo的第二部分吧。Enlighten引擎Demo演示第二彈:蘋果用戶視頻入口Enlighten宣稱是世界上首個即時全局光照技術,曾被用在《戰地3》,《極品飛車16:亡命狂飆》,《EVE OL》,《榮譽勳章:戰士》和《龍騰世紀3》等遊戲的開發上。
  • 分析PPTV視頻真實播放地址全過程(Java版)
    經過訓練的人會想自己公司的 App 有哪些視頻是掛上騰訊或者其它地方的,廣告多不多,視頻質量如何。假如把視頻上傳到 PPTV 上,自己再把視頻地址分析出來,再做個播放器,那麼廣告問題、視頻質量問題、帶寬問題是不是都解決了啊。有一些影音 App,基本上都是用磁鏈搜索 + 迅雷 Mini 庫來實現邊下邊播,技術痛點在哪裡啊?迅雷有版權限制,大多數視頻播放不了,Seed 少播放起來也很卡。
  • 德軍拍攝MG5機槍教學視頻 演示正確操作方法 提高部隊換裝效率
    近日,德國聯邦國防軍第9坦克教導旅在靶場上進行MG5機槍的演示。