FFmpeg簡易播放器的實現-音視頻播放

本文爲做者原創,轉載請註明出處:http://www.javashuo.com/article/p-olbhytch-gp.htmlhtml

基於FFmpeg和SDL實現的簡易視頻播放器,主要分爲讀取視頻文件解碼和調用SDL播放兩大部分。
前面三個實驗分別實現了最簡播放、視頻播放和音頻播放,本次實驗將視頻播放和音頻播放結合在一塊兒。git

FFmpeg簡易播放器系列文章以下:
[1]. FFmpeg簡易播放器的實現-最簡版
[2]. FFmpeg簡易播放器的實現-視頻播放
[3]. FFmpeg簡易播放器的實現-音頻播放
[4]. FFmpeg簡易播放器的實現-音視頻播放
[5]. FFmpeg簡易播放器的實現-音視頻同步github

1. 視頻播放器基本原理

下圖引用自「雷霄驊,視音頻編解碼技術零基礎學習方法」,因原圖過小,看不太清楚,故從新制做了一張圖片。
播放器基本原理示意圖
以下內容引用自「雷霄驊,視音頻編解碼技術零基礎學習方法」:shell

解協議
將流媒體協議的數據,解析爲標準的相應的封裝格式數據。視音頻在網絡上傳播的時候,經常採用各類流媒體協議,例如HTTP,RTMP,或是MMS等等。這些協議在傳輸視音頻數據的同時,也會傳輸一些信令數據。這些信令數據包括對播放的控制(播放,暫停,中止),或者對網絡狀態的描述等。解協議的過程當中會去除掉信令數據而只保留視音頻數據。例如,採用RTMP協議傳輸的數據,通過解協議操做後,輸出FLV格式的數據。網絡

解封裝
將輸入的封裝格式的數據,分離成爲音頻流壓縮編碼數據和視頻流壓縮編碼數據。封裝格式種類不少,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的做用就是將已經壓縮編碼的視頻數據和音頻數據按照必定的格式放到一塊兒。例如,FLV格式的數據,通過解封裝操做後,輸出H.264編碼的視頻碼流和AAC編碼的音頻碼流。數據結構

解碼
將視頻/音頻壓縮編碼數據,解碼成爲非壓縮的視頻/音頻原始數據。音頻的壓縮編碼標準包含AAC,MP3,AC-3等等,視頻的壓縮編碼標準則包含H.264,MPEG2,VC-1等等。解碼是整個系統中最重要也是最複雜的一個環節。經過解碼,壓縮編碼的視頻數據輸出成爲非壓縮的顏色數據,例如YUV420P,RGB等等;壓縮編碼的音頻數據輸出成爲非壓縮的音頻抽樣數據,例如PCM數據。ide

音視頻同步
根據解封裝模塊處理過程當中獲取到的參數信息,同步解碼出來的視頻和音頻數據,並將視頻音頻數據送至系統的顯卡和聲卡播放出來。函數

2. 簡易播放器的實現-音視頻播放

2.1 實驗平臺

實驗平臺:openSUSE Leap 42.3
FFmpeg版本:4.1
SDL版本:2.0.9
FFmpeg開發環境搭建可參考「FFmpeg開發環境構建學習

2.2 源碼清單

代碼已經變得挺長了,不貼完整源碼了,源碼參考:
https://github.com/leichn/exercises/blob/master/source/ffmpeg/player_avideo/ffplayer.c測試

源碼清單中涉及的一些概念簡述以下:
container:
對應數據結構AVFormatContext
封裝器,將流數據封裝爲指定格式的文件,文件格式如AVI、MP4等。
FFmpeg可識別五種流類型:視頻video(v)、音頻audio(a)、attachment(t)、數據data(d)、字幕subtitle。

codec:
對應數據結構AVCodec
編解碼器。編碼器將未壓縮的原始圖像或音頻數據編碼爲壓縮數據。解碼器與之相反。

codec context:
對應數據結構AVCodecContext
編解碼器上下文。此爲很是重要的一個數據結構,後文分析。各API大量使用AVCodecContext來引用編解碼器。

codec par:
對應數據結構AVCodecParameters
編解碼器參數。新版本增長的字段。新版本建議使用AVStream->codepar替代AVStream->codec。

packet:
對應數據結構AVPacket
通過編碼的數據。經過av_read_frame()從媒體文件中獲取獲得的一個packet可能包含多個(整數個)音頻幀或單個
視頻幀,或者其餘類型的流數據。

frame:
對應數據結構AVFrame
解碼後的原始數據。解碼器將packet解碼後生成frame。

plane:
如YUV有Y、U、V三個plane,RGB有R、G、B三個plane

slice:
圖像中一片連續的行,必須是連續的,順序由頂部到底部或由底部到頂部

stride/pitch:
一行圖像所佔的字節數,Stride = BytesPerPixel × Width,x字節對齊[待確認]

sdl window:
對應數據結構SDL_Window
播放視頻時彈出的窗口。在SDL1.x版本中,只能夠建立一個窗口。在SDL2.0版本中,能夠建立多個窗口。

sdl texture:
對應數據結構SDL_Texture
一個SDL_Texture對應一幀解碼後的圖像數據。

sdl renderer:
對應數據結構SDL_Renderer
渲染器。將SDL_Texture渲染至SDL_Window。

sdl rect:
對應數據結構SDL_Rect
SDL_Rect用於肯定SDL_Texture顯示的位置。一個SDL_Window上能夠顯示多個SDL_Rect。這樣能夠實現同一窗口的分屏顯示。

2.3 源碼流程分析

參考以下:
FFmpeg簡易播放器-音頻播放流程圖

2.4 解複用線程

解複用線程就是main()函數所在的主線程。main()函數做一些必要的初始化工做後,建立音頻處理線程和視頻處理線程。
而後main()函數進入主循環,從輸入文件中讀取packet,並根據packet類型,將之放入視頻packet隊列或音頻packet隊列。

2.5 音頻處理線程

音頻處理線程是SDL庫內建線程。用戶提供回調函數供音頻處理線程調用。實現過程參考:
FFmpeg簡易播放器的實現-音頻播放

2.6 視頻處理線程

視頻處理線程實現視頻解碼及播放。實現過程參考:
FFmpeg簡易播放器的實現-視頻播放

3. 編譯與驗證

3.1 編譯

gcc -o ffplayer ffplayer.c -lavutil -lavformat -lavcodec -lavutil -lswscale -lswresample -lSDL2

3.2 驗證

選用clock.avi測試文件,測試文件下載:clock.avi
查看視頻文件格式信息:

ffprobe clock.avi

打印視頻文件信息以下:

[avi @ 0x9286c0] non-interleaved AVI
Input #0, avi, from 'clock.avi':
  Duration: 00:00:12.00, start: 0.000000, bitrate: 42 kb/s
    Stream #0:0: Video: msrle ([1][0][0][0] / 0x0001), pal8, 320x320, 1 fps, 1 tbr, 1 tbn, 1 tbc
    Stream #0:1: Audio: truespeech ([34][0][0][0] / 0x0022), 8000 Hz, mono, s16, 8 kb/s

運行測試命令:

./ffplayer clock.avi

能夠聽到每隔1秒播放一次「嘀」聲,聲音播放12次。時針每隔1秒跳動一格,跳動12次。聲音播放正常,畫面播放也正常,可是聲音和畫面不能對應,由於沒有考慮音視頻同步。下一次實驗研究音視頻同步問題。

4. 參考資料

[1] 雷霄驊,視音頻編解碼技術零基礎學習方法
[2] 雷霄驊,最簡單的基於FFMPEG+SDL的視頻播放器ver2(採用SDL2.0)
[3] SDL WIKI, https://wiki.libsdl.org/
[4] Martin Bohme, An ffmpeg and SDL Tutorial, Tutorial 03: Playing Sound

5. 修改記錄

2018-12-06 V1.0 初稿

相關文章
相關標籤/搜索