Skip to content

Commit

Permalink
avpacket: use AVBuffer to allow refcounting the packets.
Browse files Browse the repository at this point in the history
This will allow us to avoid copying the packets in many cases.

This breaks ABI.
  • Loading branch information
elenril committed Mar 8, 2013
1 parent 1cec062 commit 1afddbe
Show file tree
Hide file tree
Showing 22 changed files with 324 additions and 136 deletions.
42 changes: 36 additions & 6 deletions libavcodec/avcodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <errno.h>
#include "libavutil/samplefmt.h"
#include "libavutil/avutil.h"
#include "libavutil/buffer.h"
#include "libavutil/cpu.h"
#include "libavutil/dict.h"
#include "libavutil/log.h"
Expand Down Expand Up @@ -892,17 +893,23 @@ enum AVPacketSideDataType {
* ABI. Thus it may be allocated on stack and no new fields can be added to it
* without libavcodec and libavformat major bump.
*
* The semantics of data ownership depends on the destruct field.
* If it is set, the packet data is dynamically allocated and is valid
* indefinitely until av_free_packet() is called (which in turn calls the
* destruct callback to free the data). If destruct is not set, the packet data
* is typically backed by some static buffer somewhere and is only valid for a
* limited time (e.g. until the next read call when demuxing).
* The semantics of data ownership depends on the buf or destruct (deprecated)
* fields. If either is set, the packet data is dynamically allocated and is
* valid indefinitely until av_free_packet() is called (which in turn calls
* av_buffer_unref()/the destruct callback to free the data). If neither is set,
* the packet data is typically backed by some static buffer somewhere and is
* only valid for a limited time (e.g. until the next read call when demuxing).
*
* The side data is always allocated with av_malloc() and is freed in
* av_free_packet().
*/
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;
/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
Expand Down Expand Up @@ -942,8 +949,12 @@ typedef struct AVPacket {
* Equals next_pts - this_pts in presentation order.
*/
int duration;
#if FF_API_DESTRUCT_PACKET
attribute_deprecated
void (*destruct)(struct AVPacket *);
attribute_deprecated
void *priv;
#endif
int64_t pos; ///< byte position in stream, -1 if unknown

/**
Expand Down Expand Up @@ -3413,10 +3424,14 @@ void avsubtitle_free(AVSubtitle *sub);
* @{
*/

#if FF_API_DESTRUCT_PACKET
/**
* Default packet destructor.
* @deprecated use the AVBuffer API instead
*/
attribute_deprecated
void av_destruct_packet(AVPacket *pkt);
#endif

/**
* Initialize optional fields of a packet with default values.
Expand Down Expand Up @@ -3454,6 +3469,21 @@ void av_shrink_packet(AVPacket *pkt, int size);
*/
int av_grow_packet(AVPacket *pkt, int grow_by);

/**
* Initialize a reference-counted packet from av_malloc()ed data.
*
* @param pkt packet to be initialized. This function will set the data, size,
* buf and destruct fields, all others are left untouched.
* @param data Data allocated by av_malloc() to be used as packet data. If this
* function returns successfully, the data is owned by the underlying AVBuffer.
* The caller may not access the data through other means.
* @param size size of data in bytes, without the padding. I.e. the full buffer
* size is assumed to be size + FF_INPUT_BUFFER_PADDING_SIZE.
*
* @return 0 on success, a negative AVERROR on error
*/
int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size);

/**
* @warning This is a hack - the packet memory allocation stuff is broken. The
* packet is allocated if it was not really allocated.
Expand Down
121 changes: 94 additions & 27 deletions libavcodec/avpacket.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,26 @@
#include <string.h>

#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "avcodec.h"

#if FF_API_DESTRUCT_PACKET
void av_destruct_packet(AVPacket *pkt)
{
av_free(pkt->data);
pkt->data = NULL;
pkt->size = 0;
}

/* a dummy destruct callback for the callers that assume AVPacket.destruct ==
* NULL => static data */
static void dummy_destruct_packet(AVPacket *pkt)
{
av_assert0(0);
}
#endif

void av_init_packet(AVPacket *pkt)
{
pkt->pts = AV_NOPTS_VALUE;
Expand All @@ -41,27 +51,35 @@ void av_init_packet(AVPacket *pkt)
pkt->convergence_duration = 0;
pkt->flags = 0;
pkt->stream_index = 0;
#if FF_API_DESTRUCT_PACKET
pkt->destruct = NULL;
#endif
pkt->buf = NULL;
pkt->side_data = NULL;
pkt->side_data_elems = 0;
}

int av_new_packet(AVPacket *pkt, int size)
{
uint8_t *data = NULL;
if ((unsigned)size < (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
if (data) {
memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
} else
size = 0;
AVBufferRef *buf = NULL;

if ((unsigned)size >= (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(EINVAL);

av_buffer_realloc(&buf, size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!buf)
return AVERROR(ENOMEM);

memset(buf->data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);

av_init_packet(pkt);
pkt->data = data;
pkt->buf = buf;
pkt->data = buf->data;
pkt->size = size;
pkt->destruct = av_destruct_packet;
if (!data)
return AVERROR(ENOMEM);
#if FF_API_DESTRUCT_PACKET
pkt->destruct = dummy_destruct_packet;
#endif

return 0;
}

Expand All @@ -75,33 +93,71 @@ void av_shrink_packet(AVPacket *pkt, int size)

int av_grow_packet(AVPacket *pkt, int grow_by)
{
void *new_ptr;
int new_size;
av_assert0((unsigned)pkt->size <= INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE);
if (!pkt->size)
return av_new_packet(pkt, grow_by);
if ((unsigned)grow_by >
INT_MAX - (pkt->size + FF_INPUT_BUFFER_PADDING_SIZE))
return -1;
new_ptr = av_realloc(pkt->data,
pkt->size + grow_by + FF_INPUT_BUFFER_PADDING_SIZE);
if (!new_ptr)
return AVERROR(ENOMEM);
pkt->data = new_ptr;

new_size = pkt->size + grow_by + FF_INPUT_BUFFER_PADDING_SIZE;
if (pkt->buf) {
int ret = av_buffer_realloc(&pkt->buf, new_size);
if (ret < 0)
return ret;
} else {
pkt->buf = av_buffer_alloc(new_size);
if (!pkt->buf)
return AVERROR(ENOMEM);
memcpy(pkt->buf->data, pkt->data, FFMIN(pkt->size, pkt->size + grow_by));
#if FF_API_DESTRUCT_PACKET
pkt->destruct = dummy_destruct_packet;
#endif
}
pkt->data = pkt->buf->data;
pkt->size += grow_by;
memset(pkt->data + pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);

return 0;
}

#define DUP_DATA(dst, src, size, padding) \
int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size)
{
if (size >= INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(EINVAL);

pkt->buf = av_buffer_create(data, size + FF_INPUT_BUFFER_PADDING_SIZE,
av_buffer_default_free, NULL, 0);
if (!pkt->buf)
return AVERROR(ENOMEM);

pkt->data = data;
pkt->size = size;
#if FF_API_DESTRUCT_PACKET
pkt->destruct = dummy_destruct_packet;
#endif

return 0;
}

#define ALLOC_MALLOC(data, size) data = av_malloc(size)
#define ALLOC_BUF(data, size) \
do { \
av_buffer_realloc(&pkt->buf, size); \
data = pkt->buf ? pkt->buf->data : NULL; \
} while (0)

#define DUP_DATA(dst, src, size, padding, ALLOC) \
do { \
void *data; \
if (padding) { \
if ((unsigned)(size) > \
(unsigned)(size) + FF_INPUT_BUFFER_PADDING_SIZE) \
goto failed_alloc; \
data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); \
ALLOC(data, size + FF_INPUT_BUFFER_PADDING_SIZE); \
} else { \
data = av_malloc(size); \
ALLOC(data, size); \
} \
if (!data) \
goto failed_alloc; \
Expand All @@ -116,30 +172,36 @@ int av_dup_packet(AVPacket *pkt)
{
AVPacket tmp_pkt;

if (pkt->destruct == NULL && pkt->data) {
if (!pkt->buf && pkt->data
#if FF_API_DESTRUCT_PACKET
&& !pkt->destruct
#endif
) {
tmp_pkt = *pkt;

pkt->data = NULL;
pkt->side_data = NULL;
DUP_DATA(pkt->data, tmp_pkt.data, pkt->size, 1);
pkt->destruct = av_destruct_packet;
DUP_DATA(pkt->data, tmp_pkt.data, pkt->size, 1, ALLOC_BUF);
#if FF_API_DESTRUCT_PACKET
pkt->destruct = dummy_destruct_packet;
#endif

if (pkt->side_data_elems) {
int i;

DUP_DATA(pkt->side_data, tmp_pkt.side_data,
pkt->side_data_elems * sizeof(*pkt->side_data), 0);
pkt->side_data_elems * sizeof(*pkt->side_data), 0, ALLOC_MALLOC);
memset(pkt->side_data, 0,
pkt->side_data_elems * sizeof(*pkt->side_data));
for (i = 0; i < pkt->side_data_elems; i++)
DUP_DATA(pkt->side_data[i].data, tmp_pkt.side_data[i].data,
tmp_pkt.side_data[i].size, 1);
tmp_pkt.side_data[i].size, 1, ALLOC_MALLOC);
}
}
return 0;

failed_alloc:
av_destruct_packet(pkt);
av_free_packet(pkt);
return AVERROR(ENOMEM);
}

Expand All @@ -148,8 +210,13 @@ void av_free_packet(AVPacket *pkt)
if (pkt) {
int i;

if (pkt->destruct)
if (pkt->buf)
av_buffer_unref(&pkt->buf);
#if FF_API_DESTRUCT_PACKET
else if (pkt->destruct)
pkt->destruct(pkt);
pkt->destruct = NULL;
#endif
pkt->data = NULL;
pkt->size = 0;

Expand Down
18 changes: 12 additions & 6 deletions libavcodec/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -887,13 +887,19 @@ int ff_alloc_packet(AVPacket *avpkt, int size)
return AVERROR(EINVAL);

if (avpkt->data) {
AVBufferRef *buf = avpkt->buf;
#if FF_API_DESTRUCT_PACKET
void *destruct = avpkt->destruct;
#endif

if (avpkt->size < size)
return AVERROR(EINVAL);

av_init_packet(avpkt);
#if FF_API_DESTRUCT_PACKET
avpkt->destruct = destruct;
#endif
avpkt->buf = buf;
avpkt->size = size;
return 0;
} else {
Expand Down Expand Up @@ -1020,9 +1026,9 @@ int attribute_align_arg avcodec_encode_audio2(AVCodecContext *avctx,
}

if (!user_packet && avpkt->size) {
uint8_t *new_data = av_realloc(avpkt->data, avpkt->size);
if (new_data)
avpkt->data = new_data;
ret = av_buffer_realloc(&avpkt->buf, avpkt->size);
if (ret >= 0)
avpkt->data = avpkt->buf->data;
}

avctx->frame_number++;
Expand Down Expand Up @@ -1197,9 +1203,9 @@ int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx,
avpkt->pts = avpkt->dts = frame->pts;

if (!user_packet && avpkt->size) {
uint8_t *new_data = av_realloc(avpkt->data, avpkt->size);
if (new_data)
avpkt->data = new_data;
ret = av_buffer_realloc(&avpkt->buf, avpkt->size);
if (ret >= 0)
avpkt->data = avpkt->buf->data;
}

avctx->frame_number++;
Expand Down
3 changes: 3 additions & 0 deletions libavcodec/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,8 @@
#ifndef FF_API_DEINTERLACE
#define FF_API_DEINTERLACE (LIBAVCODEC_VERSION_MAJOR < 56)
#endif
#ifndef FF_API_DESTRUCT_PACKET
#define FF_API_DESTRUCT_PACKET (LIBAVCODEC_VERSION_MAJOR < 56)
#endif

#endif /* AVCODEC_VERSION_H */
Loading

0 comments on commit 1afddbe

Please sign in to comment.