文章目錄
I . FFMPEG 獲取 AVPacket 數據前置操作
FFMPEG 獲取 AVPacket 數據前置操作 :
① FFMPEG 初始化 : 參考博客 【Android FFMPEG 開發】FFMPEG 初始化 ( 網絡初始化 | 打開音視頻 | 查找音視頻流 )
② FFMPEG 獲取 AVStream 音視頻流 : 參考博客 【Android FFMPEG 開發】FFMPEG 獲取 AVStream 音視頻流 ( AVFormatContext 結構體 | 獲取音視頻流信息 | 獲取音視頻流個數 | 獲取音視頻流 )
③ FFMPEG 獲取 AVCodec 編解碼器 : 參考博客 【Android FFMPEG 開發】FFMPEG 獲取編解碼器 ( 獲取編解碼參數 | 查找編解碼器 | 獲取編解碼器上下文 | 設置上下文參數 | 打開編解碼器 )
II . FFMPEG 獲取 AVPacket 數據流程
FFMPEG 獲取 AVPacket 數據流程 :
〇 前置操作 : FFMPEG 環境初始化 , 獲取 AVStream 音視頻流 , 獲取 AVCodec 編解碼器 , 然後才能進行下面的操作 ;
① 初始化 AVPacket 空數據包 : av_packet_alloc ( )
AVPacket *avPacket = av_packet_alloc();
② 讀取 AVPacket 數據 : av_read_frame ( AVFormatContext *s , AVPacket *pkt )
int read_frame_result = av_read_frame(formatContext, avPacket);
III . FFMPEG AVPacket 結構體
1 . AVPacket 結構體 : 該結構體用於封裝被編碼壓縮的數據 , 不能直接使用 , 需要解碼後才能進行音頻視頻播放 ;
typedef struct AVPacket {
...
} AVPacket;
2 . AVPacket 存儲數據 : AVPacket 存放編碼後的音視頻數據的 , 獲取該數據包後 , 需要對該數據進行解碼 , 解碼後將數據存放在 AVFrame 中 ;
3 . 編碼前後數據存放 : AVPacket 是編碼後的數據 , AVFrame 是編碼前的數據 ;
IV . AVPacket 數據讀取流程
1 . 讀取音視頻流數據到 AVPacket 中 : 首先要在外部聲明 AVPacket * 結構體指針 , 併爲其初始化 , 然後調用 av_read_frame ( ) 方法 , 將已經初始化好內存的 AVPacket * 結構體指針 傳給上述方法 , FFMPEG 將在 av_read_frame ( ) 方法中讀取數據 , 並存儲到堆內存中的 AVPacket 結構體中 ;
2 . AVPacket 的內存初始化和釋放 :
① AVPacket 初始化 : 調用 av_packet_alloc ( ) 方法初始化內存 ;
② AVPacket 釋放 : 調用 av_packet_free ( ) 釋放內存 ;
V . FFMPEG 初始化 AVPacket 數據包 av_packet_alloc ( )
1 . av_packet_alloc ( ) 函數原型 : 在堆內存中爲 AVPacket 分配內存 , 併爲 AVPacket 結構體各個字段設置默認值 ;
① 返回值 : 返回一個 AVPacket * 結構體指針 , 如果內存分配失敗 , 就會返回 NULL ;
/**
* Allocate an AVPacket and set its fields to default values. The resulting
* struct must be freed using av_packet_free().
*
* @return An AVPacket filled with default values or NULL on failure.
*
* @note this only allocates the AVPacket itself, not the data buffers. Those
* must be allocated through other means such as av_new_packet.
*
* @see av_new_packet
*/
AVPacket *av_packet_alloc(void);
2 . 代碼示例 :
//讀取數據包
// AVPacket 存放編碼後的音視頻數據的 , 獲取該數據包後 , 需要對該數據進行解碼 , 解碼後將數據存放在 AVFrame 中
// AVPacket 是編碼後的數據 , AVFrame 是編碼前的數據
//創建 AVPacket 空數據包
AVPacket *avPacket = av_packet_alloc();
VI . FFMPEG 讀取 AVPacket 數據 av_read_frame ( )
1 . av_read_frame ( ) 函數原型 : 獲取音視頻流的下一幀數據 ;
① AVFormatContext *s 參數 : 該參數中存儲了音視頻流格式相關信息 , 該參數是在之前使用 avformat_find_stream_info ( ) 方法獲取的 ;
② AVPacket *pkt 參數 : 傳入該結構體指針 , 在方法中會按照 AVFormatContext *s 信息讀取一幀音視頻數據 , 並將該數據存儲到 AVPacket 結構體中 ;
③ int 返回值 : 返回 0 代表讀取一幀數據 ( 音頻 / 視頻 ) 成功 , < 0 說明獲取數據失敗 ;
/**
* Return the next frame of a stream.
* This function returns what is stored in the file, and does not validate
* that what is there are valid frames for the decoder. It will split what is
* stored in the file into frames and return one for each call. It will not
* omit invalid data between valid frames so as to give the decoder the maximum
* information possible for decoding.
*
* If pkt->buf is NULL, then the packet is valid until the next
* av_read_frame() or until avformat_close_input(). Otherwise the packet
* is valid indefinitely. In both cases the packet must be freed with
* av_packet_unref when it is no longer needed. For video, the packet contains
* exactly one frame. For audio, it contains an integer number of frames if each
* frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
* have a variable size (e.g. MPEG audio), then it contains one frame.
*
* pkt->pts, pkt->dts and pkt->duration are always set to correct
* values in AVStream.time_base units (and guessed if the format cannot
* provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
* has B-frames, so it is better to rely on pkt->dts if you do not
* decompress the payload.
*
* @return 0 if OK, < 0 on error or end of file
*/
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
2 . FFMPEG 讀取 AVPacket 數據 代碼示例 :
/*
讀取數據包 , 並存儲到 AVPacket 數據包中
參數分析 : 一維指針 與 二維指針 參數分析
① 注意 : 第二個參數是 AVPacket * 類型的 , 那麼傳入 AVPacket *avPacket 變量
不能修改 avPacket 指針的指向 , 即該指針指向的結構體不能改變
只能修改 avPacket 指向的結構體中的元素的值
因此 , 傳入的 avPacket 結構體指針必須先進行初始化 , 然後再傳入
av_read_frame 函數內 , 沒有修改 AVPacket *avPacket 的值 , 但是修改了結構體中元素的值
② 與此相對應的是 avformat_open_input 方法 , 傳入 AVFormatContext ** 二維指針
傳入的的 AVFormatContext ** 是沒有經過初始化的 , 連內存都沒有分配
在 avformat_open_input 方法中創建並初始化 AVFormatContext * 結構體指針
然後將該指針地址賦值給 AVFormatContext **
avformat_open_input 函數內修改了 AVFormatContext ** 參數的值
返回值 0 說明讀取成功 , 小於 0 說明讀取失敗 , 或者 讀取完畢
*/
int read_frame_result = av_read_frame(formatContext, avPacket);
VII . FFMPEG 獲取 AVPacket 數據流程 代碼示例
//讀取數據包
// AVPacket 存放編碼後的音視頻數據的 , 獲取該數據包後 , 需要對該數據進行解碼 , 解碼後將數據存放在 AVFrame 中
// AVPacket 是編碼後的數據 , AVFrame 是編碼前的數據
//創建 AVPacket 空數據包
AVPacket *avPacket = av_packet_alloc();
/*
讀取數據包 , 並存儲到 AVPacket 數據包中
參數分析 : 一維指針 與 二維指針 參數分析
① 注意 : 第二個參數是 AVPacket * 類型的 , 那麼傳入 AVPacket *avPacket 變量
不能修改 avPacket 指針的指向 , 即該指針指向的結構體不能改變
只能修改 avPacket 指向的結構體中的元素的值
因此 , 傳入的 avPacket 結構體指針必須先進行初始化 , 然後再傳入
av_read_frame 函數內 , 沒有修改 AVPacket *avPacket 的值 , 但是修改了結構體中元素的值
② 與此相對應的是 avformat_open_input 方法 , 傳入 AVFormatContext ** 二維指針
傳入的的 AVFormatContext ** 是沒有經過初始化的 , 連內存都沒有分配
在 avformat_open_input 方法中創建並初始化 AVFormatContext * 結構體指針
然後將該指針地址賦值給 AVFormatContext **
avformat_open_input 函數內修改了 AVFormatContext ** 參數的值
返回值 0 說明讀取成功 , 小於 0 說明讀取失敗 , 或者 讀取完畢
*/
int read_frame_result = av_read_frame(formatContext, avPacket);