3.AVPacket使用

1.使用注意api

  •    AVPacket須要用戶經過av_packet_allc()建立好空間後.才能供給fimpeg進行獲取解碼前幀數據,因爲解碼前幀數據大小是不固定的(好比I幀數據量最大)因此ffmpeg會在AVPacket的成員裏動態進行建立空間.
  •   而且咱們每一次使用完AVPacket後(再次調用av_read_frame()讀取新幀以前),必需要經過av_packet_unref()引用技術對AVPacket裏的成員來手動清理.
  •   解碼完成或者退出播放後,還要調用av_packet_free()來釋放AVPacket自己.

 

2.結構體以下:緩存

typedef struct AVPacket{
/**
* A reference to the reference-counted buffer where the packet data is
* stored.
* May be NULL, then the packet data is not reference-counted.
*/
AVBufferRef *buf;    
//用來管理data指針引用的數據緩存,經過av_packet_ref() 和 av_packet_unref()
//來使buf->buffer->refcount成員引用計數+-,若是引用計數爲0,則釋放buffer. 
//而buf->buffer存儲的則是ffmpeg讀取出來的未解碼數據

/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
* pts MUST be larger or equal to dts as presentation cannot happen before
* decompression, unless one wants to view hex dumps. Some formats misuse
* the terms dts and pts/cts to mean something different. Such timestamps
* must be converted to true pts/dts before they are stored in AVPacket.
*/
int64_t pts;    
//顯示時間戳,須要所屬媒體流AVStream的time_base時基來換算出當前顯示的標準時間(時分秒)
//好比dpts = av_q2d(AVStream->time_base) * AVPacket->pts;

int64_t dts;       //解碼時間戳,須要所屬媒體流AVStream的time_base時基來換算出當前顯示的標準時間(時分秒)
uint8_t *data;    //指向未解碼數據(實際指向buf->buffer所指向的地址) 

int size; //data的大小
int stream_index; //標識該AVPacket所屬的視頻/音頻流
int flags; //標識,結合AV_PKT_FLAG使用,好比:
//#define AV_PKT_FLAG_KEY 0x0001 關鍵幀
//#define AV_PKT_FLAG_CORRUPT 0x0002 損壞的數據
//#define AV_PKT_FLAG_DISCARD 0x0004 丟棄的數據

/**
* Additional packet data that can be provided by the container.
* Packet can contain several types of side information.
*/
AVPacketSideData *side_data; //容器提供的一些附加數據
int side_data_elems; //邊緣數據元數個數
/**
* Duration of this packet in AVStream->time_base units, 0 if unknown.
* Equals next_pts - this_pts in presentation order.
*/
int64_t duration;
//數據的時長,須要所屬媒體流AVStream的time_base時基來換算出當前的標準時間,未知則值爲默認值0
int64_t pos;
//數據在流媒體中的位置,未知則值爲默認值-1

}AVPacket;

 

3.AVPacket經常使用函數以下:app

  • av_packet_alloc(): 初始化
  • av_packet_unref(): 引用減1.若爲0則釋放壓縮數據
  • av_packet_free():釋放AVPacket自己
  • av_packet_ref(): 從src複製到一個初始化好的dst中,並引用+1
  • av_packet_clone(): 建立並返回一個複製好的AVPacket(在音視頻同步處理中用到該函數)
  • av_packet_from_data(AVPacket *pkt, uint8_t *data, int size): 經過壓縮數據來初始化一個AVPacket(pkt必須是建立好的),通常在讀取流媒體時使用,由於解碼函數的參數必須是AVPacket.

 

4.AVPacket解碼示例:less

AVPacket *packet = av_packet_alloc(); // 建立一個packet

while(av_read_frame(pFormatCtx,packet))
{
  if(packet->stream_index == audio_index) { ... } else if(packet->stream_index == video_index) { ... } av_packet_unref(packet); // 引用計數-1,若是爲0,則釋放壓縮數據所在的空間 } av_packet_free(packet); //釋放packet,若是還想使用,則須要從新alloc

 

5.AVPacket函數分析ide

av_packet_alloc():初始化函數

AVPacket *av_packet_alloc(void)
{
   AVPacket *pkt = av_mallocz(sizeof(AVPacket));
   if (!pkt)
    return pkt;

   av_packet_unref(pkt);

   return pkt;
}

建立一個AVPacket的實例,但該函數並不會爲數據分配空間,其指向數據域的指針爲NULL。ui

 

av_packet_unref():引用減1.若爲0則釋放壓縮數據this

void av_packet_unref(AVPacket *pkt)
{
  av_packet_free_side_data(pkt);
  av_buffer_unref(&pkt->buf); av_init_packet(pkt); pkt->data = NULL; pkt->size = 0; }

將AVPacket->buf->buffer->refcount成員減1(數據域的引用技術減爲0時會自動釋放),替代了舊api(av_free_packet)spa

 

av_packet_free():釋放AVPacket自己指針

void av_packet_free(AVPacket **pkt)
{
  if (!pkt || !*pkt)
    return;

  av_packet_unref(*pkt);
  av_freep(pkt);
}

首先將AVPacket->buf->buffer->refcount成員減1(數據域的引用技術減爲0時會自動釋放),而後再釋放爲AVPacket分配的空間。

 

av_packet_ref():從src複製到一個初始化好的dst中,並引用+1

int av_packet_ref(AVPacket *dst, const AVPacket *src)
{
  int ret;

  ret = av_packet_copy_props(dst, src); //複製部分紅員(好比:pts,dts,pos,duration,side_data)到dst
  if (ret < 0)
    return ret;

  if (!src->buf) { //若是src->buf爲空,則爲dst新分配一個數據域,並將src->data複製到dst->buf->data
    ret = packet_alloc(&dst->buf, src->size);
  if (ret < 0)
   goto fail;
  av_assert1(!src->size || src->data);
  if (src->size)
    memcpy(dst->buf->data, src->data, src->size);

  dst->data = dst->buf->data;
 } else { //不爲空,則調用av_buffer_ref()來使引用+1,並將dst->buf指向src->buf,
  dst->buf = av_buffer_ref(src->buf);
  if (!dst->buf) {
  ret = AVERROR(ENOMEM);
  goto fail;
 }
  dst->data = src->data;    //而後將src->data複製到dst->data中
 }

  dst->size = src->size;

  return 0;
  fail:
    av_packet_free_side_data(dst);
  return ret;
}

av_packet_ref將src內容複製到一個建立好的dst中.須要注意: dst必須提早已經註冊好

 

av_packet_clone():建立並返回一個複製好的AVPacket(在音視頻同步處理中用到該函數)

AVPacket *av_packet_clone(const AVPacket *src)
{
AVPacket *ret = av_packet_alloc();

if (!ret)
return ret;

if (av_packet_ref(ret, src))
av_packet_free(&ret);

return ret;
}

 

av_packet_from_data(AVPacket *pkt, uint8_t *data, int size): 經過壓縮數據來初始化一個AVPacket(pkt必須是建立好的),通常在讀取流媒體時使用,由於解碼函數的參數必須是AVPacket.

int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size)
{
    if (size >= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
        return AVERROR(EINVAL);

    pkt->buf = av_buffer_create(data, size + AV_INPUT_BUFFER_PADDING_SIZE,
                                av_buffer_default_free, NULL, 0); //建立新的AVBufferRef,並初始化
    if (!pkt->buf)
        return AVERROR(ENOMEM);

    pkt->data = data;
    pkt->size = size;

    return 0;
}
相關文章
相關標籤/搜索