FFmpeg學習2:解碼數據結構及函數總結

在上一篇文章中,對FFmpeg的視頻解碼過程做了一個總結。由于才接觸FFmpeg,還是挺陌生的,這里就解碼過程再做一個總結。
本文的總結分為以下兩個部分:

  • 數據讀取,主要關注在解碼過程中所用到的FFmpeg中的結構體。
  • 解碼過程中所調用的函數

在學習的過程主要參考的是dranger tutorial,所以跟著教程在本文的最后使用SDL2.0將解碼后的數據輸出到屏幕上。

數據的讀取

一個多媒體文件包含有多個流(視頻流 video stream,音頻流 audio stream,字幕等);流是一種抽象的概念,表示一連串的數據元素;
流中的數據元素稱為幀Frame。也就是說多媒體文件中,主要有兩種數據:流Stream 及其數據元素 幀Frame,在FFmpeg自然有與這兩種數據相對應的抽象:AVStream和AVPacket

使用FFmpeg的解碼,數據的傳遞過程可歸納如下:

  1. 調用avformat_open_input打開流,將信息填充到AVFormatContext
  2. 調用av_read_frame從流中讀取數據幀到 AVPacketAVPacket保存仍然是未解碼的數據
  3. 調用avcodec_decode_video2AVPacket的數據解碼,并將解碼后的數據填充到AVFrame中,AVFrame中保存的是解碼后的原始數據

上述過程可以使用下圖表示:

結構體的存儲空間的分配與釋放

FFmpeg并沒有垃圾回收機制,所分配的空間都需要自己維護。而由于視頻處理過程中數據量是非常大,對于動態內存的使用更要謹慎。
本小節主要介紹解碼過程使用到的結構體存儲空間的分配與釋放。

  • AVFormatContext 在FFmpeg中有很重要的作用,描述一個多媒體文件的構成及其基本信息,存放了視頻編解碼過程中的大部分信息。通常該結構體由avformat_open_input分配
    存儲空間,在最后調用avformat_input_close關閉。

  • AVStream 描述一個媒體流,在解碼的過程中,作為AVFormatContext的一個字段存在,不需要單獨的處理。
  • AVpacket 用來存放解碼之前的數據,它只是一個容器,其data成員指向實際的數據緩沖區,在解碼的過程中可有av_read_frame創建和填充AVPacket中的數據緩沖區,
    當數據緩沖區不再使用的時候可以調用av_free_apcket釋放這塊緩沖區。
  • AVFrame 存放從AVPacket中解碼出來的原始數據,其必須通過av_frame_alloc來創建,通過av_frame_free來釋放。和AVPacket類似,AVFrame中也有一塊數據緩存空間,
    在調用av_frame_alloc的時候并不會為這塊緩存區域分配空間,需要使用其他的方法。在解碼的過程使用了兩個AVFrame,這兩個AVFrame分配緩存空間的方法也不相同
    • 一個AVFrame用來存放從AVPacket中解碼出來的原始數據,這個AVFrame的數據緩存空間通過調avcodec_decode_video分配和填充。
    • 另一個AVFrame用來存放將解碼出來的原始數據變換為需要的數據格式(例如RGB,RGBA)的數據,這個AVFrame需要手動的分配數據緩存空間。代碼如下:
AVFrame* pFrameYUV;
pFrameYUV = av_frame_alloc();
// 手動為 pFrameYUV分配數據緩存空間
int numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P,pCodecCtx->widht,pCodecCtx->width);
uint8_t* buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
// 將分配的數據緩存空間和AVFrame關聯起來
avpicture_fill((AVPicture *)pFrameYUV, buffer, AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height)

首先計算需要緩存空間大小,調用av_malloc分配緩存空間,最后調用avpicture_fill將分配的緩存空間和AVFrame關聯起來。
調用av_frame_free來釋放AVFrame,該函數不止釋放AVFrame本身的空間,還會釋放掉包含在其內的其他對象動態申請的空間,例如上面的緩存空間。

  • av_malloc和av_free,FFmpeg并沒有提供垃圾回收機制,所有的內存管理都要手動進行。av_malloc只是在申請內存空間的時候會考慮到內存對齊(2字節,4字節對齊),
    其申請的空間要調用av_free釋放。

調用的函數

  • av_register_all 這個函數不用多說了,注冊庫所支持的容器格式及其對應的CODEC。
  • avformat_open_input 打開多媒體文件流,并讀取文件的頭,將讀取到的信息填充到AVFormatContext結構體中。在使用結束后,要調用avformat_close_input關閉打開的流
  • avformat_find_stream_info 上面提到,avformat_open_input只是讀取文件的頭來得到多媒體文件的信息,但是有些文件沒有文件頭或者文件頭的格式不正確,這就造成只調用
    avformat_open_input可能得不到解碼所需要的必要信息,需要調用avformat_find_stream_info進一步得到流的信息。

通過上面的三個函數已經獲取了對多媒體文件進行解碼的所需要信息,下面要做的就是根據這些信息得到相應的解碼器。
結構體AVCodecContext描述了編解碼器的上下文信息,包含了流中所使用的關于編解碼器的所有信息,可以通過 AVFormatContext->AVStream->AVCodecContext來得到,在有了AVCodecContext后,可以通過codec_id來找到相應的解碼器,具體代碼如下:

AVCodec* pCodec = nullptr;
pCodecCtxOrg = pFormatCtx->streams[videoStream]->codec; // codec context
// 找到video stream的 decoder
pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);  
  • avcodec_find_decoder 可以通過codec_id或者名稱來找到相應的解碼器,返回值是一個AVCodec的指針。
  • avcodec_open2 打開相應的編解碼器
  • av_read_frame 從流中讀取數據幀暫存到AVPacket中
  • avcodec_decode_video2 從AVPacket中解碼數據到AVFrame中

經過以上的過程,AVFrame中的數據緩存中存放的就是解碼后的原始數據了。整個流程梳理如下:

使用SDL2.0顯示視頻

使用SDL2.0,dranger tutorial中的顯示視頻部分的代碼就不是很適用了,需要做一些修改。不過,SDL2.0顯示圖像還是挺簡單的。

    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);SDL_Window* window = SDL_CreateWindow("FFmpeg Decode", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_OPENGL | SDL_WINDOW_MAXIMIZED);SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);SDL_Texture* bmp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width, pCodecCtx->height);SDL_Rect rect;rect.x = 0;rect.y = 0;rect.w = pCodecCtx->width;rect.h = pCodecCtx->height;SDL_Event event;  

上述代碼為初始化后SDL顯示圖像所需要的環境,在使用FFmpeg解碼數據后

    int frameFinished = 0;avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);if (frameFinished){sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0,pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);SDL_UpdateTexture(bmp, &rect, pFrameRGB->data[0], pFrameRGB->linesize[0]);SDL_RenderClear(renderer);SDL_RenderCopy(renderer, bmp, &rect, &rect);SDL_RenderPresent(renderer);}  

上面代碼就將解碼得到的圖像幀使用SDL顯示了出來。不過,這里真的只是顯示而已,以能夠解碼速度快速的將整個視頻的圖像幀顯示一遍。

本節示例代碼 FFmpeg1.cpp

轉載于:https://www.cnblogs.com/laughingQing/p/5901675.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372282.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372282.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372282.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python1~10階乘while_Python3基礎 while 階乘

?python : 3.7.0OS : Ubuntu 18.04.1 LTSIDE : PyCharm 2018.2.4conda : 4.5.11type setting : Markdown?code"""Author : 行初心Date : 18-9-24Blog : www.cnblogs.com/xingchuxinGitHub : github.com/GratefulHeartCoder"""def main():count…

JavaFX 2 GameTutorial第4部分

介紹 這是與JavaFX 2游戲教程相關的六個部分系列的第四部分。 如果您錯過了第1部分 , 第2部分或第3部分 ,我建議您在開始本教程之前仔細閱讀它們。 回顧一下,在第3部分中,我為您提供了許多經典街機風格游戲和所使用的不同輸入設備…

關于ListView的作業

原生布局并未多做修改 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android" xmlns:tools"http://schemas.android.com/tools" android:id"id/activity_m…

Java 7的類型推斷

每個優秀的程序員都喜歡編寫簡潔但有效且經過優化的代碼。 類型推斷是JDK 7中引入的一種方法&#xff0c;它肯定會為您帶來更少鍵入的好處。 您以以下方式使用Java代碼已有很長時間了。 但是&#xff0c;在初始化Collections的特定實現時&#xff0c;您是否曾經想到過代碼重復&…

python實現膠囊網絡_膠囊網絡 -- Capsule Networks

膠囊網絡是 vector in vector out的結構&#xff0c;最后對每個不同的類別&#xff0c;輸出不一個向量&#xff0c;向量的模長表示屬于該類別的概率。例如&#xff0c;在數字識別中&#xff0c;兩個數字雖然重疊在一起&#xff0c;Capsule中的兩個向量能完整表達兩個數字的特征…

基變換與過渡矩陣

取定線性空間的一組基&#xff0c;任何一組向量可以表示為基向量的線性組合&#xff0c;且是同構映射。兩個線性空間是同構。 不同的基向量&#xff0c;基向量之間的過渡矩陣 取線性空間的兩組基任一向量可以表示為這兩組向量的線性組合將一組基向量表示為另外基向量的線性組合…

bootstrap的滾動監聽

<!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1,maximum-scale1, user-scalableno"><title>下拉菜單和滾動監…

java構造函數_JAVA的構造函數是怎么寫的。萬分感謝。路過的請不要嘲笑%_%

展開全部JAVA的構造函數是&#xff1a;SetLocal EnableDelayedExpansionset classpath.for %%c in (lib\*.jar) do set classpath!32313133353236313431303231363533e59b9ee7ad9431333431363030classpath!;%%cset classpath%classpath%;./classes;java com.ham.server.Server。…

在Spring中使用Redis

隨著NoSQL解決方案在許多問題上越來越受歡迎&#xff0c;現代項目越來越多地考慮使用一些&#xff08;或幾種&#xff09;NoSQL代替&#xff08;或并排&#xff09;傳統RDBMS。 我已經在本 &#xff0c; 本和本文章中介紹了我在MongoDB上的經驗。 在本文中&#xff0c;我想對Re…

C# 中winform的一些屬性設置

1 窗體的大小固定住&#xff0c;不能調整其大小 窗體FormBorderStyle 屬性設置為 FixedSingle; MaximizeBox 屬性設置為false; MinimizeBox 屬性設置為 false; 2. 在狀態欄中無圖標顯示 設置為fase即可。 3. 設置窗體的啟動位置 方法1&#xff0c; 用代碼控制 this.Location …

LiveBos---按鈕成下拉

轉載于:https://www.cnblogs.com/luhanzhen/p/6802779.html

Solr:創建拼寫檢查器

在上一篇文章中&#xff0c;我談到了Solr Spellchecker的工作原理&#xff0c;然后向您展示了其性能的一些測試結果。 現在&#xff0c;我們將看到另一種拼寫檢查方法。 與其他方法一樣&#xff0c;此方法使用兩步過程。 相當快速的“候選單詞”選擇&#xff0c;然后對這些單詞…

linux修改機器名稱

1 使用hostname命令&#xff1a;hostname 新機器名稱 2 修改vi /etc/sysconfig/network # cat /etc/sysconfig/network NETWORKINGyes HOSTNAMElocalhost.localdomain 注意&#xff1a;左側都必須大寫&#xff0c;等號附件沒有空格。 查看機器名稱使用hostname命令 轉載于:h…

java property_property在Java中的用法

展開全部在項目中經常用到各種配置文件62616964757a686964616fe78988e69d8331333337623561&#xff0c;有.properties的&#xff0c;也有.xml格式的都可以通過java.utils.Property類進行處理。1. 讀取.properties文件File pFile new File("test.properties");FileIn…

Django 和 html

下面是對應的形式&#xff0c;自定義的forms 轉載于:https://www.cnblogs.com/kilen/p/6804047.html

Grails動態下拉菜單

最近&#xff0c;我有一個UI要求&#xff0c;客戶希望從兩個單獨的下拉列表中選擇值。 第一個下拉列表的值實質上過濾了第二個下拉列表的值。 鑒于我們支持的財務項目對UI的要求并不嚴格&#xff0c;因此我不得不進行一些初步的學習和實驗&#xff0c;以實現良好的實施。 這篇博…

【Java大系】Java快速教程

感謝原作者&#xff1a;Vamei 出處&#xff1a;http://www.cnblogs.com/vamei Java是面向對象語言。這門語言其實相當年輕&#xff0c;于1995年才出現&#xff0c;由Sun公司出品。James Gosling領導了Java的項目小組。該項目的最初只想為家電設計一門容易移植的語言。然而&am…

[轉]前端構建工具gulpjs的使用介紹及技巧

本文轉自&#xff1a;http://www.cnblogs.com/2050/p/4198792.html gulpjs是一個前端構建工具&#xff0c;與gruntjs相比&#xff0c;gulpjs無需寫一大堆繁雜的配置參數&#xff0c;API也非常簡單&#xff0c;學習起來很容易&#xff0c;而且gulpjs使用的是nodejs中stream來讀取…

Eclipse側邊欄Outline設置字體

Eclipse的Outline&#xff0c;Project Explorer&#xff0c;Call Hierarchy等小窗口是很方便的功能&#xff0c;但是遇到函數名或文件名很長的情況&#xff0c;就只能顯示前半段。盡管Eclipse的自定義程度很高&#xff0c;但是卻找不到這些窗口的字體設置。 經過一番摸索后&…

AOP的簡單介紹

為什么使用AOP&#xff0c;一個簡單的回答這個問題的方法是顯示一個橫切關注點的實現而不使用AOP。 考慮一個簡單的服務及其實現&#xff1a; public interface InventoryService {public Inventory create(Inventory inventory);public List<inventory> list();public I…