1.使用注意
- 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常用函數如下:
- 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解碼示例:
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函數分析
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。
av_packet_unref():引用減1.若為0則釋放壓縮數據
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)
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; }