【Android FFMPEG 開發】FFMPEG 讀取音視頻流中的數據到 AVPacket ( 初始化 AVPacket 數據 | 讀取 AVPacket )



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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章