【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket-解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )
文章目錄
- I . FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 前置操作
- II . FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 流程
- III . FFMPEG 發(fā)送 AVPacket 數(shù)據(jù)包給編解碼器 ( AVPacket->解碼器 )
- IV . FFMPEG AVPacket 內(nèi)存釋放
- V . FFMPEG 初始化 AVFrame 結(jié)構(gòu)體
- VI . FFMPEG 解碼器 AVCodec 接收并解碼 AVPacket 數(shù)據(jù)到 AVFrame 中
- VII . FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 部分代碼示例
I . FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 前置操作
FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 數(shù)據(jù)前置操作 :
① FFMPEG 初始化 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 初始化 ( 網(wǎng)絡初始化 | 打開音視頻 | 查找音視頻流 )
② FFMPEG 獲取 AVStream 音視頻流 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 獲取 AVStream 音視頻流 ( AVFormatContext 結(jié)構(gòu)體 | 獲取音視頻流信息 | 獲取音視頻流個數(shù) | 獲取音視頻流 )
③ FFMPEG 獲取 AVCodec 編解碼器 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 獲取編解碼器 ( 獲取編解碼參數(shù) | 查找編解碼器 | 獲取編解碼器上下文 | 設置上下文參數(shù) | 打開編解碼器 )
④ FFMPEG 讀取音視頻流中的數(shù)據(jù)到 AVPacket : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 讀取音視頻流中的數(shù)據(jù)到 AVPacket ( 初始化 AVPacket 數(shù)據(jù) | 讀取 AVPacket )
II . FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 流程
FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 流程 :
〇 前置操作 : FFMPEG 環(huán)境初始化 , 獲取 AVStream 音視頻流 , 獲取 AVCodec 編解碼器 , 讀取音視頻流中的數(shù)據(jù)到 AVPacket , 然后才能進行下面的操作 ;
① 發(fā)送 AVPacket 數(shù)據(jù)包給編解碼器 : int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
int result_send_packet = avcodec_send_packet(avCodecContext, avPacket);② 釋放 AVPacket 內(nèi)存 : void av_packet_free(AVPacket **pkt) , AVPacket 數(shù)據(jù)包解碼后 , 就沒用了 , 執(zhí)行完該步驟以后 , 馬上將 AVPacket 釋放掉 , 以免占用內(nèi)存 ;
av_packet_free(&avPacket);③ 初始化 AVFrame 結(jié)構(gòu)體 : AVFrame *av_frame_alloc ( void ) , 該結(jié)構(gòu)體用于存儲解碼后的數(shù)據(jù) , 可以直接用于音視頻播放 ;
AVFrame *avFrame = av_frame_alloc();④ 解碼器接收并解碼 AVPacket 數(shù)據(jù)到 AVFrame 中 : int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
int result_receive_frame = avcodec_receive_frame(avCodecContext, avFrame);III . FFMPEG 發(fā)送 AVPacket 數(shù)據(jù)包給編解碼器 ( AVPacket->解碼器 )
1 . 發(fā)送 AVPacket 數(shù)據(jù) : 從 AVStream 音視頻流中取出 AVPacket 數(shù)據(jù)包 , 這個數(shù)據(jù)是經(jīng)過壓縮編碼后的數(shù)據(jù) , 無法直接使用 , 還需要將其發(fā)送到解碼器解碼后 , 才能使用 ; 發(fā)送 AVPacket 數(shù)據(jù)到解碼器的方法是 avcodec_send_packet ( ) ;
2 . avcodec_send_packet ( ) 函數(shù)原型 : 向解碼器發(fā)送未解碼的數(shù)據(jù) , 這些數(shù)據(jù)需要解碼 ;
① AVCodecContext *avctx 參數(shù) : 解碼器上下文 , 從音視頻流中查找編解碼器 , 從編解碼器中獲取編解碼器上下文 , 該參數(shù)中存儲了音視頻流格式相關(guān)信息 , 該參數(shù)是在之前使用 avformat_find_stream_info ( ) 方法獲取的 ;
② const AVPacket *avpkt 參數(shù) : 需要解碼的數(shù)據(jù)包 ;
③ int 返回值 : 返回 0 成功 , 其它失敗 ; 只要失敗 , 直接退出即可 ;
/*** Supply raw packet data as input to a decoder.** Internally, this call will copy relevant AVCodecContext fields, which can* influence decoding per-packet, and apply them when the packet is actually* decoded. (For example AVCodecContext.skip_frame, which might direct the* decoder to drop the frame contained by the packet sent with this function.)** @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE* larger than the actual read bytes because some optimized bitstream* readers read 32 or 64 bits at once and could read over the end.** @warning Do not mix this API with the legacy API (like avcodec_decode_video2())* on the same AVCodecContext. It will return unexpected results now* or in future libavcodec versions.** @note The AVCodecContext MUST have been opened with @ref avcodec_open2()* before packets may be fed to the decoder.** @param avctx codec context* @param[in] avpkt The input AVPacket. Usually, this will be a single video* frame, or several complete audio frames.* Ownership of the packet remains with the caller, and the* decoder will not write to the packet. The decoder may create* a reference to the packet data (or copy it if the packet is* not reference-counted).* Unlike with older APIs, the packet is always fully consumed,* and if it contains multiple frames (e.g. some audio codecs),* will require you to call avcodec_receive_frame() multiple* times afterwards before you can send a new packet.* It can be NULL (or an AVPacket with data set to NULL and* size set to 0); in this case, it is considered a flush* packet, which signals the end of the stream. Sending the* first flush packet will return success. Subsequent ones are* unnecessary and will return AVERROR_EOF. If the decoder* still has frames buffered, it will return them after sending* a flush packet.** @return 0 on success, otherwise negative error code:* AVERROR(EAGAIN): input is not accepted in the current state - user* must read output with avcodec_receive_frame() (once* all output is read, the packet should be resent, and* the call will not fail with EAGAIN).* AVERROR_EOF: the decoder has been flushed, and no new packets can* be sent to it (also returned if more than 1 flush* packet is sent)* AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush* AVERROR(ENOMEM): failed to add packet to internal queue, or similar* other errors: legitimate decoding errors*/ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);3 . FFMPEG 發(fā)送 AVPacket 數(shù)據(jù)包給編解碼器 代碼示例 :
/** ① 發(fā)送數(shù)據(jù)包將數(shù)據(jù)包發(fā)送給解碼器 , 返回 0 成功 , 其它失敗*/ int result_send_packet = avcodec_send_packet(avCodecContext, avPacket);//失敗處理 if(result_send_packet != 0){//TODO 發(fā)送失敗處理 }IV . FFMPEG AVPacket 內(nèi)存釋放
1 . AVPacket 內(nèi)存釋放 : AVPacket 解碼壓縮后的數(shù)據(jù) , 發(fā)送給解碼器之后 , 就沒有用了 , 這里要及時釋放 AVPacket 結(jié)構(gòu)體所占用的內(nèi)存 , 以免出現(xiàn)內(nèi)存泄漏的情況 ;
2 . AVPacket 初始化與釋放 : AVPacket 結(jié)構(gòu)體不管是初始化 , 還是釋放 , 都必須使用 FFMPEG 提供的方法 ;
① AVPacket 初始化 : 調(diào)用 AVPacket *av_packet_alloc(void) 方法 ;
② AVPacket 釋放 : 調(diào)用 void av_packet_free(AVPacket **pkt) 方法 ;
3 . av_packet_free ( ) 函數(shù)原型 : 傳入 AVPacket ** 二維指針參數(shù) , 該結(jié)構(gòu)體釋放后 , 其指針指向也要被修改 ;
① AVPacket **pkt 參數(shù) : 該二維指針指向 AVPacket * 結(jié)構(gòu)體指針 , 在該方法中釋放其內(nèi)存 , 并指向 NULL ;
/*** Free the packet, if the packet is reference counted, it will be* unreferenced first.** @param pkt packet to be freed. The pointer will be set to NULL.* @note passing NULL is a no-op.*/ void av_packet_free(AVPacket **pkt);4 . FFMPEG AVPacket 內(nèi)存釋放 示例代碼 :
//AVPacket* avPacket av_packet_free(&avPacket);V . FFMPEG 初始化 AVFrame 結(jié)構(gòu)體
1 . AVFrame 結(jié)構(gòu)體 : AVFrame 結(jié)構(gòu)體存儲解碼后的數(shù)據(jù) , 該數(shù)據(jù)可以直接用于播放音視頻 ;
2 . AVFrame 結(jié)構(gòu)體使用 : 首先要初始化 AVFrame 結(jié)構(gòu)體 , 該結(jié)構(gòu)體的初始化和釋放 , 同樣也要使用 FFMPEG 提供的相應的方法 ;
① AVFrame 初始化方法 : AVFrame *av_frame_alloc(void)
/*** Allocate an AVFrame and set its fields to default values. The resulting* struct must be freed using av_frame_free().** @return An AVFrame filled with default values or NULL on failure.** @note this only allocates the AVFrame itself, not the data buffers. Those* must be allocated through other means, e.g. with av_frame_get_buffer() or* manually.*/ AVFrame *av_frame_alloc(void);② AVFrame 釋放方法 : void av_frame_free(AVFrame **frame)
/*** Free the frame and any dynamically allocated objects in it,* e.g. extended_data. If the frame is reference counted, it will be* unreferenced first.** @param frame frame to be freed. The pointer will be set to NULL.*/ void av_frame_free(AVFrame **frame);3 . FFMPEG AVFrame 結(jié)構(gòu)體初始化 代碼示例 :
//用于存放解碼后的數(shù)據(jù)包 , 一個 AVFrame 代表一個圖像 AVFrame *avFrame = av_frame_alloc();VI . FFMPEG 解碼器 AVCodec 接收并解碼 AVPacket 數(shù)據(jù)到 AVFrame 中
1 . 前置操作 : 在上面的步驟中 , 將 AVPacket 未解碼的數(shù)據(jù)發(fā)送給了解碼器 , 又初始化了 AVFrame 結(jié)構(gòu)體 ;
2 . 解碼過程 : 在本步驟中 , 將初始化好的 AVFrame 設置給解碼器 , 解碼器解碼完成后 , 將解碼后的音視頻數(shù)據(jù)存放到 AVFrame 結(jié)構(gòu)體中 , 之后就可以進行播放操作了 ;
3 . 解碼方法 : 調(diào)用 int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame) 方法 , 即可將 AVFrame 設置給解碼器 , 用于接收解碼后的數(shù)據(jù)幀 ;
4 . avcodec_receive_frame ( ) 函數(shù)原型 :
① AVCodecContext *avctx 參數(shù) : 解碼器上下文 , 從音視頻流中查找編解碼器 , 從編解碼器中獲取編解碼器上下文 , 該參數(shù)中存儲了音視頻流格式相關(guān)信息 , 該參數(shù)是在之前使用 avformat_find_stream_info ( ) 方法獲取的 ;
② AVFrame *frame : 初始化好的 AVFrame 結(jié)構(gòu)體指針 ;
③ int 返回值 : 返回 0 說明解碼成功 , 否則失敗 ; 返回 AVERROR(EAGAIN) , 當前狀態(tài)沒有輸出 , 需要輸入更多數(shù)據(jù) ; 返回 AVERROR_EOF , 解碼器中沒有數(shù)據(jù) , 已經(jīng)讀取到結(jié)尾 ; 返回 AVERROR(EINVAL) , 解碼器沒有打開 ;
/*** Return decoded output data from a decoder.** @param avctx codec context* @param frame This will be set to a reference-counted video or audio* frame (depending on the decoder type) allocated by the* decoder. Note that the function will always call* av_frame_unref(frame) before doing anything else.** @return* 0: success, a frame was returned* AVERROR(EAGAIN): output is not available in this state - user must try* to send new input* AVERROR_EOF: the decoder has been fully flushed, and there will be* no more output frames* AVERROR(EINVAL): codec not opened, or it is an encoder* other negative values: legitimate decoding errors*/ int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);5 . FFMPEG 解碼器 AVCodec 接收并解碼 AVPacket 數(shù)據(jù)到 AVFrame 代碼示例 :
//解碼器中將數(shù)據(jù)包解碼后 , 存放到 AVFrame * 中 , 這里將其取出并解碼 // 返回 AVERROR(EAGAIN) : 當前狀態(tài)沒有輸出 , 需要輸入更多數(shù)據(jù) // 返回 AVERROR_EOF : 解碼器中沒有數(shù)據(jù) , 已經(jīng)讀取到結(jié)尾 // 返回 AVERROR(EINVAL) : 解碼器沒有打開 int result_receive_frame = avcodec_receive_frame(avCodecContext, avFrame);VII . FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame 部分代碼示例
/** 1 . 發(fā)送數(shù)據(jù)包將數(shù)據(jù)包發(fā)送給解碼器 , 返回 0 成功 , 其它失敗AVERROR(EAGAIN): 說明當前解碼器滿了 , 不能接受新的數(shù)據(jù)包了這里先將解碼器的數(shù)據(jù)都處理了, 才能接收新數(shù)據(jù)其它錯誤處理 : 直接退出循環(huán)*/ int result_send_packet = avcodec_send_packet(avCodecContext, avPacket);//2 . 本次循環(huán)中 , 將 AVPacket 丟到解碼器中解碼完畢后 , 就可以釋放 AVPacket 內(nèi)存了 av_packet_free(&avPacket);if(result_send_packet != 0){//TODO 失敗處理 }//3 . 接收并解碼數(shù)據(jù)包 , 存放在 AVFrame 中 //用于存放解碼后的數(shù)據(jù)包 , 一個 AVFrame 代表一個圖像 AVFrame *avFrame = av_frame_alloc();//4 . 解碼器中將數(shù)據(jù)包解碼后 , 存放到 AVFrame * 中 , 這里將其取出并解碼 // 返回 AVERROR(EAGAIN) : 當前狀態(tài)沒有輸出 , 需要輸入更多數(shù)據(jù) // 返回 AVERROR_EOF : 解碼器中沒有數(shù)據(jù) , 已經(jīng)讀取到結(jié)尾 // 返回 AVERROR(EINVAL) : 解碼器沒有打開 int result_receive_frame = avcodec_receive_frame(avCodecContext, avFrame); //失敗處理 if(result_receive_frame != 0){//TODO 失敗處理 }
總結(jié)
以上是生活随笔為你收集整理的【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket-解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 应用开发】Canvas
- 下一篇: 【Android FFMPEG 开发】F