基於FFmpeg和Qt實現簡易視訊播放器

2023-11-25 21:01:22

VideoPlay001

記得一鍵三連哦

  • 使用qt+ffmpeg開發簡單的視訊播放器,無聲音
  • 視訊解碼使用的是軟解碼即只用CPU進行QPainter繪製每一幀影象,CPU佔用過高
  • 簡單易學,適合小白入門學習FFMpeg視訊解析的基本API

遺留問題

  • 視訊播放時間的處理,基勻速播放的實現原理

專案程式碼

videoPlay001分支

專案警告

  • 注:博主本人學習過程的分享,參照他人的文章皆會標註原作者
  • 注:點名某初生DN,未來是屬於開源的
  • 注:本人文章非盈利性質,若有侵權請聯絡我刪除
  • 注:聯絡方式Q:2950319782
  • 注:博主本人很菜,文章基本是二次創作,大佬請忽略我的隨筆
  • 注:我會一步步分享實現的細節,若仍有問題請留言,還可以問ChatGPT

專案參照

問題解決

開發環境

  • 系統:Win10
  • Qt:5.14.2
  • 編譯器:qtcreator 4.11.1, minGW64
  • ffmpeg: 5.12

實現功能

  • 使用ffmpeg音視訊庫軟解碼實現視訊播放器
  • 支援開啟多種本地視訊檔(如mp4,mov,avi等)
  • 支援解析多種網路視訊流(如rtsp,rtmp,http等)
  • 支援視訊勻速播放
  • 採用QPainter進行影象顯示,支援自適應視窗縮放
  • 視訊播放支援實時開始,暫停,繼續播放
  • 採用模組化程式設計,視訊解碼,執行緒控制,影象顯示各功能分離,低耦合
  • 多執行緒程式設計

實現邏輯

  • 程式主邏輯
  • ffmpeg軟解碼流程

專案實現

專案結構

專案思路

頁面搭建

  • 需要實現視訊的播放,那麼要先要有個頁面,使用qtcreator設計介面
  • 不是很複雜的頁面我們直接使用自帶的ui介面即可
  • 博主使用的是minGW64編譯
  • 我們要有一個combo box來獲取網路視訊流的URL或者本地視訊流的地址
  • 還需要一個開啟檔案的按鈕,來打卡檔案對話方塊,選擇視訊檔
  • 還需要一個開始播放的按鈕和一個暫停播放的按鈕
  • 至於QPainter繪製解碼出來的每一幀圖片我們自定義一個PlayImage控制元件,來顯示視訊
  • PlayImage繼承自Widget,記得把ui檔案中的PlayImage控制元件提升為自定義的控制元件
  • 最終的ui圖

自定義控制元件

  • 一步步來,我們先實現自定義的控制元件來解決視訊的功能
  • 先建立一個CPP類PlayImage繼承自QWidget
  • 為什麼不用QLabel顯示圖片呢,因為顯示靜態的圖片還可以,但是如果像視訊這樣頻繁的更新圖片,會使程式變得異常卡頓,因此使用QPainter重繪每一幀影象來實現視訊播放的功能
  • 這個Demo圖片資料量比較小,完全可以實現,基本的邏輯就是不斷更新影象路徑,一直重繪,直到沒有影象傳進來
  • 其實這裡自定義控制元件應該是單獨封裝的,作為外部檔案引入,方便複用,但這裡為了簡單,還是直接寫入程式碼吧
  • 在這裡封裝了兩個主要的方法updateImage和updatePixmap,解碼出的每一幀圖片只需要呼叫對應的影象更新方法就可以實現視訊顯示

視訊解碼實現

  • 到這裡,已經有視訊播放的功能了,現在需要把視訊的解碼完成出來
  • 到這裡呢,其實建議把視訊解碼封裝成一個單獨的功能,使用pri引入即可
  • 這裡呢,直接建立一個不帶ui檔案的pri就可以,我是直接在原始檔目錄下新建了一個play檔案了,新建一個videoplay.pri檔案,再修改一下主工程的pro檔案,引入即可
  • 在這個子工程中,新增一個純cpp類,videoDocode,實現視訊解碼主要功能
  • 視訊解碼按照上面的流程圖實現即可,當然需要先引入FFMpeg的l相關檔案,這裡建議以外部檔案引入,我是放在videoplay.pri目錄下,新建一個ffmpeg資料夾,再更改一下pri檔案,引入即可
視訊解碼
  • 引入avformat解封裝模組,先來把封裝的格式剝去
  • 這裡呢因為使用了FFmpeg,建議還是寫c風格的程式碼,省的報錯
  • 先定義一個通用的處理錯誤的函數errHandle
  • 然後在open函數中實現解析視訊流,剝去封裝格式,然後讀取視訊流獲取資訊
  • 然後我們發現需要手動釋放一些資源,自己定義一個free函數,先釋放解封裝上下文,後續還需要釋放什麼資源自己新增到free函數裡就可以了
  • 下面的小邏輯都在程式碼裡以註釋的形式表示了,不寫了,太累了
  • 這裡完了之後,其實就是把視訊解碼的功能封裝好,供下面的讀取執行緒呼叫,因為都寫在子執行緒的run函數裡太複雜了,所以這裡單獨處理視訊解碼
視訊解碼執行緒
  • 這裡為什麼需要有一個執行緒類呢,因為在qt設計中,視窗的控制和各種功能的後臺實現應該是不同執行緒處理的,否則都給視窗執行緒處理,這個程式會變得異常卡頓
  • 直接建立一個readThread類繼承自QThread類,這裡基本是按照流程圖呼叫視訊解碼的函數即可,並返回給視窗執行緒相應的值,下面要定義幾個public的介面供視窗執行緒呼叫
  • 主要是處理重寫run函數,在cpp檔案中引入videoDocode,並範例化物件,呼叫視訊解碼功能
  • 先呼叫videoDocode的open函數,
  • 需要一個自定義訊號playState來與視窗執行緒傳遞資訊,處理播放狀態
  • open成功後開始呼叫videoDocode的read函數
  • 還需要處理視訊暫停pause的功能,這裡需要先實現sleep延時操作
  • 視訊解碼是一個相對耗時的操作,不能影響視窗執行緒,因此解碼執行緒應該是非阻塞延時
  • 成功open和read後,需要關閉執行緒close了
  • 下面我們來處理視窗執行緒

視窗執行緒

  • 在ui檔案中繫結控制元件的槽函數
  • 通過將獲取的檔案路徑顯示在combox上,其他控制元件通過文字值呼叫,其實是不安全的
  • 頁面的具體狀態通過自定義的訊號與槽與視訊解碼執行緒互動