FFmpeg開發(fā)筆記(四):ffmpeg解碼的基本流程詳解
前言
??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