最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

FFmpeg開發(fā)筆記(四):ffmpeg解碼的基本流程詳解

2020-09-14 13:37 作者:紅胖子_AAA紅模仿  | 我要投稿

前言

??ffmpeg涉及了很多,循序漸進(jìn),本篇描述基本的解碼流程。

Demo

??

ffmpeg解碼流程

??ffmpeg的解碼和編碼都遵循其基本的執(zhí)行流程。
??基本流程如下:
??

步驟一:注冊(cè):

??使用ffmpeg對(duì)應(yīng)的庫,都需要進(jìn)行注冊(cè),可以注冊(cè)子項(xiàng)也可以注冊(cè)全部。

步驟二:打開文件:

??打開文件,根據(jù)文件名信息獲取對(duì)應(yīng)的ffmpeg全局上下文。

步驟三:探測(cè)流信息:

??一定要探測(cè)流信息,拿到流編碼的編碼格式,不探測(cè)流信息則其流編碼器拿到的編碼類型可能為空,后續(xù)進(jìn)行數(shù)據(jù)轉(zhuǎn)換的時(shí)候就無法知曉原始格式,導(dǎo)致錯(cuò)誤。

步驟四:查找對(duì)應(yīng)的解碼器

??依據(jù)流的格式查找解碼器,軟解碼還是硬解碼是在此處決定的,但是特別注意是否支持硬件,需要自己查找本地的硬件解碼器對(duì)應(yīng)的標(biāo)識(shí),并查詢其是否支持。普遍操作是,枚舉支持文件后綴解碼的所有解碼器進(jìn)行查找,查找到了就是可以硬解了(此處,不做過多的討論,對(duì)應(yīng)硬解碼后續(xù)會(huì)有文章進(jìn)行進(jìn)一步研究)。
??(注意:解碼時(shí)查找解碼器,編碼時(shí)查找編碼器,兩者函數(shù)不同,不要弄錯(cuò)了,否則后續(xù)能打開但是數(shù)據(jù)是錯(cuò)的)

步驟五:打開解碼器

??打開獲取到的解碼器。

步驟六:申請(qǐng)縮放數(shù)據(jù)格式轉(zhuǎn)換結(jié)構(gòu)體

??此處特別注意,基本上解碼的數(shù)據(jù)都是yuv系列格式,但是我們顯示的數(shù)據(jù)是rgb等相關(guān)顏色空間的數(shù)據(jù),所以此處轉(zhuǎn)換結(jié)構(gòu)體就是進(jìn)行轉(zhuǎn)換前到轉(zhuǎn)換后的描述,給后續(xù)轉(zhuǎn)換函數(shù)提供轉(zhuǎn)碼依據(jù),是很關(guān)鍵并且非常常用的結(jié)構(gòu)體。

步驟七:申請(qǐng)緩存區(qū)

??申請(qǐng)一個(gè)緩存區(qū)outBuffer,fill到我們目標(biāo)幀數(shù)據(jù)的data上,比如rgb數(shù)據(jù),QAVFrame的data上存是有指定格式的數(shù)據(jù),且存儲(chǔ)有規(guī)則,而fill到outBuffer(自己申請(qǐng)的目標(biāo)格式一幀緩存區(qū)),則是我們需要的數(shù)據(jù)格式存儲(chǔ)順序。
??舉個(gè)例子,解碼轉(zhuǎn)換后的數(shù)據(jù)為rgb888,實(shí)際直接用data數(shù)據(jù)是錯(cuò)誤的,但是用outBuffer就是對(duì)的,所以此處應(yīng)該是ffmpeg的fill函數(shù)做了一些轉(zhuǎn)換。
進(jìn)入循環(huán)解碼:

步驟八:獲取一幀packet

??拿取封裝的一個(gè)packet,判斷packet數(shù)據(jù)的類型進(jìn)行解碼拿到存儲(chǔ)的編碼數(shù)據(jù)

步驟九:數(shù)據(jù)轉(zhuǎn)換

??使用轉(zhuǎn)換函數(shù)結(jié)合轉(zhuǎn)換結(jié)構(gòu)體對(duì)編碼的數(shù)據(jù)進(jìn)行轉(zhuǎn)換,那拿到需要的目標(biāo)寬度、高度和指定存儲(chǔ)格式的原始數(shù)據(jù)。

步驟十:自行處理

??拿到了原始數(shù)據(jù)自行處理。
??不斷循環(huán),直到拿取pakcet函數(shù)成功,但是無法got一幀數(shù)據(jù),則代表文件解碼已經(jīng)完成。
??幀率需要自己控制循環(huán),此處只是循環(huán)拿取,可加延遲等。

步驟十一:釋放QAVPacket

??此處要單獨(dú)列出是因?yàn)?,其?shí)很多網(wǎng)上和開發(fā)者的代碼:
??在進(jìn)入循環(huán)解碼前進(jìn)行了av_new_packet,循環(huán)中未av_free_packet,造成內(nèi)存溢出;
??在進(jìn)入循環(huán)解碼前進(jìn)行了av_new_packet,循環(huán)中進(jìn)行av_free_pakcet,那么一次new對(duì)應(yīng)無數(shù)次free,在編碼器上是不符合前后一一對(duì)應(yīng)規(guī)范的。
??查看源代碼,其實(shí)可以發(fā)現(xiàn)av_read_frame時(shí),自動(dòng)進(jìn)行了av_new_packet(),那么其實(shí)對(duì)于packet,只需要進(jìn)行一次av_packet_alloc()即可,解碼完后av_free_packet。
??執(zhí)行完后,返回執(zhí)行“步驟八:獲取一幀packet”,一次循環(huán)結(jié)束。

步驟十二:釋放轉(zhuǎn)換結(jié)構(gòu)體

??全部解碼完成后,安裝申請(qǐng)順序,進(jìn)行對(duì)應(yīng)資源的釋放。

步驟十三:關(guān)閉解碼/編碼器

??關(guān)閉之前打開的解碼/編碼器。

步驟十四:關(guān)閉上下文

??關(guān)閉文件上下文后,要對(duì)之前申請(qǐng)的變量按照申請(qǐng)的順序,依次釋放。
??另附上完成的詳細(xì)解碼流程圖:
??

本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108573195

ffmpeg解碼相關(guān)變量

AVFormatContext

??AVFormatContext描述了一個(gè)媒體文件或媒體流的構(gòu)成和基本信息,位于avformat.h文件中。

AVInputFormat

??AVInputFormat 是類似COM 接口的數(shù)據(jù)結(jié)構(gòu),表示輸入文件容器格式,著重于功能函數(shù),一種文件容器格式對(duì)應(yīng)一個(gè)AVInputFormat 結(jié)構(gòu),在程序運(yùn)行時(shí)有多個(gè)實(shí)例,位于avoformat.h文件中。

AVDictionary

??AVDictionary 是一個(gè)字典集合,鍵值對(duì),用于配置相關(guān)信息。

AVCodecContext

??AVCodecContext是一個(gè)描述編解碼器上下文的數(shù)據(jù)結(jié)構(gòu),包含了眾多編解碼器需要的參數(shù)信息,位于avcodec.h文件中。

AVPacket

??AVPacket是FFmpeg中很重要的一個(gè)數(shù)據(jù)結(jié)構(gòu),它保存了解復(fù)用(demuxer)之后,解碼(decode)之前的數(shù)據(jù)(仍然是壓縮后的數(shù)據(jù))和關(guān)于這些數(shù)據(jù)的一些附加的信息,如顯示時(shí)間戳(pts),解碼時(shí)間戳(dts),數(shù)據(jù)時(shí)長(duration),所在流媒體的索引(stream_index)等等。
??使用前,使用av_packet_alloc()分配,

AVCodec

??AVCodec是存儲(chǔ)編解碼器信息的結(jié)構(gòu)體,位于avcodec.h文件中。

AVFrame

??AVFrame中存儲(chǔ)的是經(jīng)過解碼后的原始數(shù)據(jù)。在解碼中,AVFrame是解碼器的輸出;在編碼中,AVFrame是編碼器的輸入。
??使用前,使用av_frame_alloc()進(jìn)行分配。

struct SwsContext

??使用前,使用sws_getContext()進(jìn)行獲取,主要用于視頻圖像的轉(zhuǎn)換。

ffmpeg解碼流程相關(guān)函數(shù)原型

av_register_all

void av_register_all(void);

??初始化libavformat并注冊(cè)所有muxer、demuxer和協(xié)議。如果不調(diào)用此函數(shù),則可以選擇想要指定注冊(cè)支持的哪種格式,通過av_register_input_format()、av_register_output_format()。

avformat_open_input

int avformat_open_input(AVFormatContext **ps, ? ? ? ? ? ? ? ? ? ? ? ?const char *url, ? ? ? ? ? ? ? ? ? ? ? ?AVInputFormat *fmt, ? ? ? ? ? ? ? ? ? ? ? ?AVDictionary **options);

??打開輸入流并讀取標(biāo)頭。編解碼器未打開。流必須使用avformat_close_input()關(guān)閉,返回0-成功,<0-失敗錯(cuò)誤碼。

  • 參數(shù)一:指向用戶提供的AVFormatContext(由avformat_alloc_context分配)的指針。

  • 參數(shù)二:要打開的流的url

  • 參數(shù)三:fmt如果非空,則此參數(shù)強(qiáng)制使用特定的輸入格式。否則將自動(dòng)檢測(cè)格式。

  • 參數(shù)四:包含AVFormatContext和demuxer私有選項(xiàng)的字典。返回時(shí),此參數(shù)將被銷毀并替換為包含找不到的選項(xiàng)。都有效則返回為空。

avformat_find_stream_info

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
讀取檢查媒體文件的數(shù)據(jù)包以獲取具體的流信息,如媒體存入的編碼格式。

  • 參數(shù)一:媒體文件上下文。

  • 參數(shù)二:字典,一些配置選項(xiàng)。

avcodec_find_decoder

AVCodec *avcodec_find_decoder(enum AVCodecID id);

??查找具有匹配編解碼器ID的已注冊(cè)解碼器,解碼時(shí),已經(jīng)獲取到了,注冊(cè)的解碼器可以通過枚舉查看,枚舉太多,略。

avcodec_open2

int avcodec_open2(AVCodecContext *avctx, ? ? ? ? ? ? ? ? ?const AVCodec *codec, ? ? ? ? ? ? ? ? ?AVDictionary **options);

??初始化AVCodeContext以使用給定的AVCodec。

sws_getContext

struct SwsContext *sws_getContext(int srcW, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int srcH, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?enum AVPixelFormat srcFormat, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int dstW, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int dstH, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?enum AVPixelFormat dstFormat, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int flags, SwsFilter *srcFilter, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?SwsFilter *dstFilter, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?const double *param);

??分配并返回一個(gè)SwsContext。需要它來執(zhí)行sws_scale()進(jìn)行縮放/轉(zhuǎn)換操作。

avpicture_get_size

int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height);

??返回存儲(chǔ)具有給定參數(shù)的圖像的緩存區(qū)域大小。

  • 參數(shù)一:圖像的像素格式

  • 參數(shù)二:圖像的像素寬度

  • 參數(shù)三:圖像的像素高度

avpicture_fill

int avpicture_fill(AVPicture *picture, ? ? ? ? ? ? ?const uint8_t *ptr, ? ? ? ? ? ? ?enum AVPixelFormat pix_fmt, ? ? ? ? ? ? ?int width, ? ? ? ? ? ? ?int height);

??根據(jù)指定的圖像、提供的數(shù)組設(shè)置數(shù)據(jù)指針和線條大小參數(shù)。

  • 參數(shù)一:輸入AVFrame指針,強(qiáng)制轉(zhuǎn)換為AVPciture即可。

  • 參數(shù)二:映射到的緩存區(qū),開發(fā)者自己申請(qǐng)的存放圖像數(shù)據(jù)的緩存區(qū)。

  • 參數(shù)三:圖像數(shù)據(jù)的編碼格式。

  • 參數(shù)四:圖像像素寬度。

  • 參數(shù)五:圖像像素高度。

av_read_frame

int av_read_frame(AVFormatContext *s, AVPacket *pkt);

??返回流的下一幀。此函數(shù)返回存儲(chǔ)在文件中的內(nèi)容,不對(duì)有效的幀進(jìn)行驗(yàn)證。獲取存儲(chǔ)在文件中的幀中,并為每個(gè)調(diào)用返回一個(gè)。不會(huì)的省略有效幀之間的無效數(shù)據(jù),以便給解碼器最大可用于解碼的信息。
??返回0是成功,小于0則是錯(cuò)誤,大于0則是文件末尾,所以大于等于0是返回成功。

avcodec_decode_video2

int avcodec_decode_video2(AVCodecContext *avctx, ? ? ? ? ? ? ? ? ? ? ? ? ?AVFrame *picture, ? ? ? ? ? ? ? ? ? ? ? ? ?int *got_picture_ptr, ? ? ? ? ? ? ? ? ? ? ? ? ?const AVPacket *avpkt);

??將大小為avpkt->size from avpkt->data的視頻幀解碼為圖片。一些解碼器可以支持單個(gè)avpkg包中的多個(gè)幀,解碼器將只解碼第一幀。出錯(cuò)時(shí)返回負(fù)值,否則返回字節(jié)數(shù),如果沒有幀可以解壓縮,則為0。

  • 參數(shù)一:編解碼器上下文。

  • 參數(shù)二:將解碼視頻幀存儲(chǔ)在AVFrame中。

  • 參數(shù)三:輸入緩沖區(qū)的AVPacket。

  • 參數(shù)四:如果沒有幀可以解壓,那么得到的圖片是0,否則,它是非零的。

sws_scale

int sws_scale(struct SwsContext *c, ? ? ? ? ? ? ?const uint8_t *const srcSlice[], ? ? ? ? ? ? ?const int srcStride[], ? ? ? ? ? ? ?int srcSliceY, ? ? ? ? ? ? ?int srcSliceH, ? ? ? ? ? ? ?uint8_t *const dst[], ? ? ? ? ? ? ?const int dstStride[]);

??在srcSlice中縮放圖像切片并將結(jié)果縮放在dst中切片圖像。切片是連續(xù)的序列圖像中的行。

  • 參數(shù)一:以前用創(chuàng)建的縮放上下文*sws_getContext()。

  • 參數(shù)二:包含指向源片段,就是AVFrame的data。

  • 參數(shù)三:包含每個(gè)平面的跨步的數(shù)組,其實(shí)就是AVFrame的linesize。

  • 參數(shù)四:切片在源圖像中的位置,從開始計(jì)數(shù)0對(duì)應(yīng)切片第一行的圖像,所以直接填0即可。

  • 參數(shù)五:源切片的像素高度。

  • 參數(shù)六:目標(biāo)數(shù)據(jù)地址映像,是目標(biāo)AVFrame的data。

  • 參數(shù)七:目標(biāo)每個(gè)平面的跨步的數(shù)組,就是linesize。

av_free_packet

void av_free_packet(AVPacket *pkt);

??釋放一個(gè)包。

avcodec_close

int avcodec_close(AVCodecContext *avctx);

??關(guān)閉給定的avcodeContext并釋放與之關(guān)聯(lián)的所有數(shù)據(jù)(但不是AVCodecContext本身)。

avformat_close_input

void avformat_close_input(AVFormatContext **s);

??關(guān)閉打開的輸入AVFormatContext。釋放它和它的所有內(nèi)容并將*s設(shè)置為空。

Demo源碼

void FFmpegManager::testDecode() { // ? ?QString fileName = "test/1.avi"; ? ?QString fileName = "test/1.mp4"; ? ?// ffmpeg相關(guān)變量預(yù)先定義與分配 ? ?AVFormatContext *pAVFormatContext = 0; ? ? ? ? ?// ffmpeg的全局上下文,所有ffmpeg操作都需要 ? ?AVInputFormat *pAVInputFormat = 0; ? ? ? ? ? ? ?// ffmpeg的輸入格式結(jié)構(gòu)體 ? ?AVDictionary *pAVDictionary = 0; ? ? ? ? ? ? ? ?// ffmpeg的字典option,各種參數(shù)給格式編解碼配置參數(shù)的 ? ?AVCodecContext *pAVCodecContext = 0; ? ? ? ? ? ?// ffmpeg編碼上下文 ? ?AVCodec *pAVCodec = 0; ? ? ? ? ? ? ? ? ? ? ? ? ?// ffmpeg編碼器 ? ?AVPacket *pAVPacket = 0; ? ? ? ? ? ? ? ? ? ? ? ?// ffmpag單幀數(shù)據(jù)包 ? ?AVFrame *pAVFrame = 0; ? ? ? ? ? ? ? ? ? ? ? ? ?// ffmpeg單幀緩存 ? ?AVFrame *pAVFrameRGB32 = 0; ? ? ? ? ? ? ? ? ? ? // ffmpeg單幀緩存轉(zhuǎn)換顏色空間后的緩存 ? ?struct SwsContext *pSwsContext = 0; ? ? ? ? ? ? // ffmpag編碼數(shù)據(jù)格式轉(zhuǎn)換 ? ?int ret = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 函數(shù)執(zhí)行結(jié)果 ? ?int videoIndex = -1; ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 音頻流所在的序號(hào) ? ?int gotPicture = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 解碼時(shí)數(shù)據(jù)是否解碼成功 ? ?int numBytes = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 解碼后的數(shù)據(jù)長度 ? ?uchar *outBuffer = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? // 解碼后的數(shù)據(jù)存放緩存區(qū) ? ?pAVFormatContext = avformat_alloc_context(); ? ? // 分配 ? ?pAVPacket = av_packet_alloc(); ? ? ? ? ? ? ? ? ?// 分配 ? ?pAVFrame = av_frame_alloc(); ? ? ? ? ? ? ? ? ? // 分配 ? ?pAVFrameRGB32 = av_frame_alloc(); ? ? ? ? ? ? // 分配 ? ?if(!pAVFormatContext || !pAVPacket || !pAVFrame || !pAVFrameRGB32) ? ?{ ? ? ? ?LOG << "Failed to alloc"; ? ? ? ?goto END; ? ?} ? ?// 步驟一:注冊(cè)所有容器和編解碼器(也可以只注冊(cè)一類,如注冊(cè)容器、注冊(cè)編碼器等) ? ?av_register_all(); ? ?// 步驟二:打開文件(ffmpeg成功則返回0) ? ?LOG << "文件:" << fileName << ",是否存在:" << QFile::exists(fileName); ? ?ret = avformat_open_input(&pAVFormatContext, fileName.toUtf8().data(), pAVInputFormat, 0); ? ?if(ret) ? ?{ ? ? ? ?LOG << "Failed"; ? ? ? ?goto END; ? ?} ? ?// 步驟三:探測(cè)流媒體信息 ? ?// Assertion desc failed at libswscale/swscale_internal.h:668 ? ?// 入坑:因?yàn)閜ix_fmt為空,需要對(duì)編碼器上下文進(jìn)一步探測(cè) ? ?ret = avformat_find_stream_info(pAVFormatContext, 0); ? ?if(ret < 0) ? ?{ ? ? ? ?LOG << "Failed to avformat_find_stream_info(pAVCodecContext, 0)"; ? ? ? ?goto END; ? ?} ? ?// 打印文件信息 ? ?LOG << "視頻文件包含流信息的數(shù)量:" << pAVFormatContext->nb_streams; ? ?// 在Qt中av_dump_format不會(huì)進(jìn)行命令行輸出 // ? ?av_dump_format(pAVFormatContext, 1, fileName.toUtf8().data(), 0); ? ?// 步驟三:提取流信息,提取視頻信息 ? ?for(int index = 0; index < pAVFormatContext->nb_streams; index++) ? ?{ ? ? ? ?pAVCodecContext = pAVFormatContext->streams[index]->codec; ? ? ? ?switch (pAVCodecContext->codec_type) ? ? ? ?{ ? ? ? ?case AVMEDIA_TYPE_UNKNOWN: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_UNKNOWN"; ? ? ? ? ? ?break; ? ? ? ?case AVMEDIA_TYPE_VIDEO: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_VIDEO"; ? ? ? ? ? ?videoIndex = index; ? ? ? ? ? ?LOG; ? ? ? ? ? ?break; ? ? ? ?case AVMEDIA_TYPE_AUDIO: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_AUDIO"; ? ? ? ? ? ?break; ? ? ? ?case AVMEDIA_TYPE_DATA: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_DATA"; ? ? ? ? ? ?break; ? ? ? ?case AVMEDIA_TYPE_SUBTITLE: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_SUBTITLE"; ? ? ? ? ? ?break; ? ? ? ?case AVMEDIA_TYPE_ATTACHMENT: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_ATTACHMENT"; ? ? ? ? ? ?break; ? ? ? ?case AVMEDIA_TYPE_NB: ? ? ? ? ? ?LOG << "流序號(hào):" << index << "類型為:" << "AVMEDIA_TYPE_NB"; ? ? ? ? ? ?break; ? ? ? ?default: ? ? ? ? ? ?break; ? ? ? ?} ? ? ? ?// 已經(jīng)找打視頻品流 ? ? ? ?if(videoIndex != -1) ? ? ? ?{ ? ? ? ? ? ?break; ? ? ? ?} ? ?} ? ?if(videoIndex == -1 || !pAVCodecContext) ? ?{ ? ? ? ?LOG << "Failed to find video stream"; ? ? ? ?goto END; ? ?} ? ?// 步驟四:對(duì)找到的視頻流尋解碼器 ? ?pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id); ? ?if(!pAVCodec) ? ?{ ? ? ? ?LOG << "Fialed to avcodec_find_decoder(pAVCodecContext->codec_id):" ? ? ? ? ? ?<< pAVCodecContext->codec_id; ? ? ? ?goto END; ? ?} ? ?// 步驟五:打開解碼器 ? ?ret = avcodec_open2(pAVCodecContext, pAVCodec, NULL); ? ?if(ret) ? ?{ ? ? ? ?LOG << "Failed to avcodec_open2(pAVCodecContext, pAVCodec, pAVDictionary)"; ? ? ? ?goto END; ? ?} ? ?LOG << pAVCodecContext->width << "x" << pAVCodecContext->height; ? ?// 步驟六:對(duì)拿到的原始數(shù)據(jù)格式進(jìn)行縮放轉(zhuǎn)換為指定的格式高寬大小 ? ?// Assertion desc failed at libswscale/swscale_internal.h:668 ? ?// 入坑:因?yàn)閜ix_fmt為空,需要對(duì)編碼器上下文進(jìn)一步探測(cè) ? ?pSwsContext = sws_getContext(pAVCodecContext->width, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pAVCodecContext->height, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pAVCodecContext->pix_fmt, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pAVCodecContext->width, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pAVCodecContext->height, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AV_PIX_FMT_RGBA, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SWS_FAST_BILINEAR, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0); ? ?numBytes = avpicture_get_size(AV_PIX_FMT_RGBA, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?pAVCodecContext->width, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?pAVCodecContext->height); ? ?outBuffer = (uchar *)av_malloc(numBytes); ? ?// pAVFrame32的data指針指向了outBuffer ? ?avpicture_fill((AVPicture *)pAVFrameRGB32, ? ? ? ? ? ? ? ? ? outBuffer, ? ? ? ? ? ? ? ? ? AV_PIX_FMT_RGBA, ? ? ? ? ? ? ? ? ? pAVCodecContext->width, ? ? ? ? ? ? ? ? ? pAVCodecContext->height); ? ?// 此處無需分配 ? ?// av_read_frame時(shí)他會(huì)分配,av_new_packet多此一舉,正好解釋了一次new和多次free的問題 // ? ?av_new_packet(pAVPacket, pAVCodecContext->width * pAVCodecContext->height); ? ?// 步驟七:讀取一幀數(shù)據(jù)的數(shù)據(jù)包 ? ?while(av_read_frame(pAVFormatContext, pAVPacket) >= 0) ? ?{ ? ? ? ?if(pAVPacket->stream_index == videoIndex) ? ? ? ?{ ? ? ? ? ? ?// 步驟八:對(duì)讀取的數(shù)據(jù)包進(jìn)行解碼 ? ? ? ? ? ?ret = avcodec_decode_video2(pAVCodecContext, pAVFrame, &gotPicture, pAVPacket); ? ? ? ? ? ?if(ret < 0) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?LOG << "Failed to avcodec_decode_video2(pAVFormatContext, pAVFrame, &gotPicture, pAVPacket)"; ? ? ? ? ? ? ? ?break; ? ? ? ? ? ?} ? ? ? ? ? ?// 等于0代表拿到了解碼的幀數(shù)據(jù) ? ? ? ? ? ?if(!gotPicture) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?LOG << "no data"; ? ? ? ? ? ? ? ?break; ? ? ? ? ? ?}else{ ? ? ? ? ? ? ? ?sws_scale(pSwsContext, ? ? ? ? ? ? ? ? ? ? ? ? ?(const uint8_t * const *)pAVFrame->data, ? ? ? ? ? ? ? ? ? ? ? ? ?pAVFrame->linesize, ? ? ? ? ? ? ? ? ? ? ? ? ?0, ? ? ? ? ? ? ? ? ? ? ? ? ?pAVCodecContext->height, ? ? ? ? ? ? ? ? ? ? ? ? ?pAVFrameRGB32->data, ? ? ? ? ? ? ? ? ? ? ? ? ?pAVFrameRGB32->linesize); ? ? ? ? ? ? ? ?QImage imageTemp((uchar *)outBuffer, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pAVCodecContext->width, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pAVCodecContext->height, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? QImage::Format_RGBA8888); ? ? ? ? ? ? ? ?QImage image = imageTemp.copy(); ? ? ? ? ? ? ? ?LOG << image.save("1.jpg"); ? ? ? ? ? ?} ? ? ? ? ? ?av_free_packet(pAVPacket); ? ? ? ?} ? ? ? ?QThread::msleep(100); ? ?} END: ? ?LOG << "釋放回收資源"; ? ?if(outBuffer) ? ?{ ? ? ? ?av_free(outBuffer); ? ? ? ?outBuffer = 0; ? ?} ? ?if(pSwsContext) ? ?{ ? ? ? ?sws_freeContext(pSwsContext); ? ? ? ?pSwsContext = 0; ? ? ? ?LOG << "sws_freeContext(pSwsContext)"; ? ?} ? ?if(pAVFrameRGB32) ? ?{ ? ? ? ?av_frame_free(&pAVFrameRGB32); ? ? ? ?pAVFrame = 0; ? ? ? ?LOG << "av_frame_free(pAVFrameRGB888)"; ? ?} ? ?if(pAVFrame) ? ?{ ? ? ? ?av_frame_free(&pAVFrame); ? ? ? ?pAVFrame = 0; ? ? ? ?LOG << "av_frame_free(pAVFrame)"; ? ?} ? ?if(pAVPacket) ? ?{ ? ? ? ?av_free_packet(pAVPacket); ? ? ? ?pAVPacket = 0; ? ? ? ?LOG << "av_free_packet(pAVPacket)"; ? ?} ? ?if(pAVCodecContext) ? ?{ ? ? ? ?avcodec_close(pAVCodecContext); ? ? ? ?pAVCodecContext = 0; ? ? ? ?LOG << "avcodec_close(pAVCodecContext);"; ? ?} ? ?if(pAVFormatContext) ? ?{ ? ? ? ?avformat_free_context(pAVFormatContext); ? ? ? ?pAVFormatContext = 0; ? ? ? ?LOG << "avformat_free_context(pAVFormatContext)"; ? ?} }

工程模板v1.1.0

??對(duì)應(yīng)工程模板v1.1.0

上一篇:《FFmpeg開發(fā)筆記(三):ffmpeg介紹、windows編譯以及開發(fā)環(huán)境搭建》
下一篇:敬請(qǐng)期待

原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導(dǎo)航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108573195


FFmpeg開發(fā)筆記(四):ffmpeg解碼的基本流程詳解的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
武安市| 尖扎县| 九龙城区| 和田县| 堆龙德庆县| 阳谷县| 兴山县| 呈贡县| 富蕴县| 沅陵县| 黄陵县| 融水| 敦化市| 恩平市| 萍乡市| 辽中县| 长治市| 西平县| 蓬溪县| 娱乐| 荣昌县| 威宁| 绵阳市| 通海县| 阿勒泰市| 临猗县| 迁西县| 甘孜| 山阳县| 息烽县| 陆川县| 肇庆市| 兰溪市| 光泽县| 溧阳市| 富宁县| 诸城市| 黔南| 兴宁市| 临漳县| 临夏市|