1. 程式人生 > >FFmpeg從入門到出家(FFmpeg簡析)

FFmpeg從入門到出家(FFmpeg簡析)

 FFmpeg簡析

FFmpeg從無到有,發展至今,功能日益強大,程式碼也越來越多,很多初學者都被其眾多的原始檔、龐大的結構體和複雜的演算法打消了繼續學習的念頭。本章節將從總體對FFmpeg進行簡單的解析,教您如何閱讀FFmpeg原始碼。

FFmpeg包含如下類庫:

libavformat - 用於各種音視訊封裝格式的生成和解析,包括獲取解碼所需資訊、讀取音視訊資料等功能。各種流媒體協議程式碼(如rtmpproto.c等)以及音視訊格式的(解)複用程式碼(如flvdec.c、flvenc.c等)都位於該目錄下。

libavcodec - 音視訊各種格式的編解碼。各種格式的編解碼程式碼(如aacenc.c、aacdec.c等)都位於該目錄下。

libavutil - 包含一些公共的工具函式的使用庫,包括算數運算,字元操作等。

libswscale - 提供原始視訊的比例縮放、色彩對映轉換、影象顏色空間或格式轉換的功能。

libswresample - 提供音訊重取樣,取樣格式轉換和混合等功能。

libpostproc - 用於後期效果處理,如影象的去塊效應等。

libavdevice - 用於硬體的音視訊採集、加速和顯示。

如果您之前沒有閱讀FFmpeg程式碼的經驗,建議優先閱讀libavformat、libavcodec以及libavutil下面的程式碼,它們提供了音視訊開發的最基本功能,應用範圍也是最廣的。

FFmpeg裡面最常用的資料結構,按功能可大致分為以下幾類(以下程式碼行數,以branch: origin/release/3.4為準):

1. 封裝格式

AVFormatContext - 描述了媒體檔案的構成及基本資訊,是統領全域性的基本結構體,貫穿程式始終,很多函式都要用它作為引數;

AVInputFormat - 解複用器物件,每種作為輸入的封裝格式(例如FLV、MP4、TS等)對應一個該結構體,如libavformat/flvdec.c的ff_flv_demuxer

AVOutputFormat - 複用器物件,每種作為輸出的封裝格式(例如FLV, MP4、TS等)對應一個該結構體,如libavformat/flvenc.c的

ff_flv_muxer

AVStream - 用於描述一個視訊/音訊流的相關資料資訊。

2.編解碼

AVCodecContext - 描述編解碼器上下文的資料結構,包含了眾多編解碼器需要的引數資訊;

AVCodec - 編解碼器物件,每種編解碼格式(例如H.264、AAC等)對應一個該結構體,如libavcodec/aacdec.c的ff_aac_decoder。每個AVCodecContext中含有一個AVCodec;

AVCodecParameters - 編解碼引數,每個AVStream中都含有一個AVCodecParameters,用來存放當前流的編解碼引數。

3. 網路協議

URLProtocol - 描述了音視訊資料傳輸所使用的協議,每種傳輸協議(例如HTTP、RTMP)等,都會對應一個URLProtocol結構,如libavformat/http.c中的ff_http_protocol

URLContext - 封裝了協議物件及協議操作物件。

[if !supportLists]4. [endif]資料存放

AVPacket - 存放編碼後、解碼前的壓縮資料,即ES資料;

AVFrame - 存放編碼前、解碼後的原始資料,如YUV格式的視訊資料或PCM格式的音訊資料等;

上述結構體的關係圖如下所示(箭頭表示派生出):

圖2. FFmpeg結構體關係圖

下面這段程式碼完成了讀取媒體檔案中音視訊資料的基本功能,本節以此為例,分析FFmpeg內部程式碼的呼叫邏輯。

char *url = "http://192.168.1.105/test.flv";

AVPacket pkt;

int ret = 0;

//註冊複用器、編碼器等

av_register_all();

avformat_network_init();

//開啟檔案

AVFormatContext *fmtCtx = avformat_alloc_context();

ret = avformat_open_input(&fmtCtx, url, NULL, NULL);

ret = avformat_find_stream_info(fmtCtx, NULL);

//讀取音視訊資料

while(ret >= 0)

{

  ret = av_read_frame(s, &pkt);

}

av_register_all函式的作用是註冊一系列的(解)複用器、編/解碼器等。它在所有基於FFmpeg的應用程式中幾乎都是第一個被呼叫的,只有呼叫了該函式,才能使用複用器、編碼器等。

static void register_all(void)

{

avcodec_register_all();

    /* (de)muxers */

    ……

    REGISTER_MUXDEMUX(FLV,              flv);

    ……

}

編/解碼其註冊過程相同,此處不再贅述。

FFmpeg讀取媒體資料的過程始於avformat_open_input,該方法中完成了媒體檔案的開啟和格式探測的功能。但FFmpeg是如何找到正確的流媒體協議和解複用器呢?可以看到avformat_open_input方法中呼叫了init_input函式,在這裡面完成了查詢流媒體協議和解複用器的工作。

static intinit_input(AVFormatContext *s, const char *filename,

AVDictionary **options)

{

int ret;

    ……

    if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)

        return ret;

    if (s->iformat)

        return 0;

    return av_probe_input_buffer2(s->pb, &s->iformat, filename,

                                 s, 0, s->format_probesize);

}

[if !supportLists]1. [endif]s->io_open實際上呼叫的就是io_open_default,它最終呼叫到url_find_protocol方法。

static conststructURLProtocol *url_find_protocol(const char *filename)

{

constURLProtocol **protocols;

    ……

    protocols = ffurl_get_protocols(NULL, NULL);

    if (!protocols)

        return NULL;

    for (i = 0; protocols[i]; i++) {

constURLProtocol *up = protocols[i];

        if (!strcmp(proto_str, up->name)) {

av_freep(&protocols);

            return up;

        }

        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&

!strcmp(proto_nested, up->name)) {

av_freep(&protocols);

            return up;

        }

    }

av_freep(&protocols);

    return NULL;

}

ffurl_get_protocols可以得到當前編譯的FFmpeg支援的所有流媒體協議,通過url的scheme和protocol->name相比較,得到正確的protocol。例如本例中URLProtocol最終指向了libavformat/http.c中的ff_http_protocol

[if !supportLists]1. [endif]av_probe_input_buffer2最終呼叫到av_probe_input_format3,該方法遍歷所有的解複用器,即first_iformat連結串列中的所有節點,呼叫它們的read_probe()函式計算匹配得分,函式最終返回計算找到的最匹配的解複用器。本例中AVInputFormat最終指向了libavformat/flvdec.c中的ff_flv_demuxer

av_read_frame作用是讀取媒體資料中的每個音視訊幀,該方法中最關鍵的地方就是呼叫了AVInputFormat的read_packet()方法。AVInputFormat的read_packet()是一個函式指標,指向當前的AVInputFormat的讀取資料的函式。在本例中,AVInputFormat為ff_flv_demuxer,也就是說read_packet最終指向了flv_read_packet

相關推薦

FFmpeg入門出家FFmpeg

 FFmpeg簡析 FFmpeg從無到有,發展至今,功能日益強大,程式碼也越來越多,很多初學者都被其眾多的原始檔、龐大的結構體和複雜的演算法打消了繼續學習的念頭。本章節將從總體對FFmpeg進行簡單的解析,教您如何閱讀FFmpeg原始碼。 FFmpeg包含如下類庫:

FFmpeg入門出家HEVC在RTMP中的擴展

log 將在 設置 else 基礎 .com wrong mage 相關 由金山雲視頻雲技術團隊提供:FFmpeg從入門到出家第三季; 為推進HEVC視頻編碼格式在直播方案中的落地,經過CDN聯盟討論,並和主流雲服務廠商達成一致,規範了HEVC在RTMP/FLV中的擴展,具

FFmpeg入門到精通——進階篇,SEI那些事兒

uid ffffff nco 生成 角色 根據 開發工程師 實踐 流過濾 前言在直播應用的開發過程中,如果把主播端消息事件傳遞到觀眾端,一般會以Instant Messaging(即時通訊)的方式傳遞過去,但因為消息分發通道和直播通道是分開的,因此消息與直播音視頻數據的同步

MyBatis源碼解析之數據源含數據庫連接池

概述 myba 源碼 conn java 初始 對象狀態 lis 為什麽 一.概述: 常見的數據源組件都實現了javax.sql.DataSource接口; MyBatis不但要能集成第三方的數據源組件,自身也提供了數據源的實現; 一般情況下,數據源的初始化過程參數較多,比

MyBatis原始碼解析之資料來源含資料庫連線池

一.概述: 常見的資料來源元件都實現了javax.sql.DataSource介面; MyBatis不但要能整合第三方的資料來源元件,自身也提供了資料來源的實現; 一般情況下,資料來源的初始化過程引數較多,比較複雜; 二.設計模式: 為什麼要使用工廠模式     資料來

自學自用 = 網易雲課堂細說Linux-入門到精通視頻教程

lin date upd kcon size route zip rep 系統 視頻地址 https://study.163.com/course/courseMain.htm?courseId=983014 介紹 本篇博客,旨在記錄視頻學習的要點,所以格式隨意,且沒有文字

讓react用起來更得心應手——react-router原理

讓react用起來更得心應手系列文章: 前端路由和後臺路由 在剛入行的時候一直明白什麼單頁面應用是什麼,說白了就是混淆了前臺路由和後臺路由,現在來縷縷它們: 前臺路由:頁面的顯示由前臺js控制,在url的路徑中輸入雜湊值是不會往後臺傳送請求的,所以前臺可以通過將雜湊和頁

學習Qt之基礎篇——入門開始1

Qt 簡介         Qt是一個跨平臺的C++應用程式開發框架,被廣泛用於開發GUI程式。 Qt有豐富的 API且面向物件。Qt是自由且開放原始碼的軟體,在GNU較寬鬆公共許可證條款下發布。支援廣泛的編譯器,包括GCC的C++編譯器和Visual Studio。 Q

EditText無法獲取焦點 獲取焦點無法編輯android:descendantFocusability用法

android:descendantFocusability用法簡析 開發中很常見的一個問題,專案中的listview不僅僅是簡單的文字,常常需要自己定義listview,自己的Adapter去繼承BaseAdapter,在adapter中按照需求進行編寫,問題就出現了,可

FFMPEG的小總結自己看的

這篇文章是我自己看的,你未必能看懂,當做我自己的備份。 FFmpeg是複雜的開源系統,剛開始玩只知道用命令ffmpeg -i test.mp4 這樣的。其實普通使用者能有用這個命令ffmpeg和FFmpeg框架是有區別的。 其實ffmpeg這個命令是一個程式,程式碼是FF

加速度校準分析Pixhawk加速度校準演算法

1. 加速度計上電校準零偏是否可行? 答案是否,以PX4飛控為例,在NED系下,水平放置時,機體測量加速度的資料應該是[0 0 -g],所以,除非你確定你的飛控校準加速度零偏的時候是絕對的水平,否則只會加大誤差,更別談每次上電自動校準,減零偏了,試想一下,如果我就是要飛

input只能輸入數字正則

專案中有醬紫一個需求,輸入金額的input,只能輸入數字(正,負,零),最開始我天真的用了h5的新屬性‘type=number’解決,結果呢,,,obviously,,,不相容!!!首先火狐就不相容。只好另擇它路。 既然這樣子以本人愚見,有兩個路子。1、可以在

宋寶華- KVM最初的2小時KVM入門到入不了門

接著《Docker最初的2小時(Docker從入門到入門)》繼續聊,再花10個小時寫出《KVM最初的2小時(KVM從入門到入不了門)》。坦白講,由於KVM遠遠比Docker要複雜,還是要2小時愛上KVM,這絕非難事,所以很可能入不了門。原則上,我們繼續迭代學習,

GAN網路入門教程之GAN原理

在一篇部落格[GAN網路從入門教程(一)之GAN網路介紹](https://www.cnblogs.com/xiaohuiduan/p/13237486.html)中,簡單的對GAN網路進行了一些介紹,介紹了其是什麼,然後大概的流程是什麼。 在這篇部落格中,主要是介紹其數學公式,以及其演算法流程。當然數學公

SpringBoot入門-1Hello Word Boot

eas hand running 頁面 .cn tpm 2.3 src size   1、創建一個Maven項目,添加一個parent,代碼如下 <parent> <groupId>org.springframework.boot

【資源下載】分享個嵌入式開發的入門教程包含視頻

gpt water term href jsb sdn ast csdn gravity 基於ARM A17的嵌入式開發的入門開發教程,有興趣的朋友可下載或者在線觀看 開發教程:http://wiki.t-firefly.com 視頻教程:https://pan.baid

JavaScript最佳新手入門系列大話變量

left col 新手入門 mcs com cti http href coo 533e讀2歡q侖誥4chttp://www.zcool.com.cn/collection/ZMTgzMjExMTI=.html 726n共4520xhttp://www.zcool.com.

Scala入門3特質線性化

相同 object 完成 可能 args scala 結構 放置 mutable   嘗試設計一套特質,靈活的改動整數隊列。隊列有兩種操作:put把整數放入隊列,get從尾部取出它們。隊列是先進先出的,get應該依照入隊列的順序取數據。提示:可以用mutable.Array

小程序入門心得不談api

用戶信息 項目 信息 app.js 準備 neu 技術分享 圖片 微信公眾 小程序入門 一、準備 首先先去微信公眾平臺註冊一個小程序賬號,去拿到一個AppID(沒AppID也可以開發,只是有些功能會受限),註冊成功後到開發設置獲取自己的AppID,即使有AppID有些功能還

小程序開發快速入門教程附源碼

五分鐘上手-微信小程序 1:用沒有註冊過微信公眾平臺的郵箱註冊一個微信公眾號, 申請帳號 ,點擊 https://mp.weixin.qq.com/wxopen/waregister?action=step1 根據指引填寫信息和提交相應的資料,就可以擁有自己的小程序帳號。註冊完成之後開始登