summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-08-24 17:45:28 +0200
committerwm4 <wm4@nowhere>2014-08-25 00:46:26 +0200
commit758f8f7bd46388cba9330ad47701c0ebacc2d963 (patch)
tree5fcecbf5218d13888af60251c8f8fd821c983cfd
parentef1c6e9295f40a4633685eeae3c12706f8290bb0 (diff)
downloadmpv-758f8f7bd46388cba9330ad47701c0ebacc2d963.tar.bz2
mpv-758f8f7bd46388cba9330ad47701c0ebacc2d963.tar.xz
demux: always use AVPacket
This is a simplification, because it lets us use the AVPacket functions, instead of handling the details manually. It also allows the libavcodec rawvideo decoder to use reference counting, so it doesn't have to memcpy() the full image data. The change in av_common.c enables this. This change is somewhat risky, because we rely on the following AVPacket implementation details and assumptions: - av_packet_ref() doesn't access the input padding, and just copies the data. By the API, AVPacket is always padded, and we violate this. The lavc implementation would have to go out of its way to make this a real problem, though. - We hope that the way we make the AVPacket refcountable in av_common.c is actually supported API-usage. It's hard to tell whether it is. Of course we still use our own "old" demux_packet struct, just so that libav* API usage is somewhat isolated.
-rw-r--r--common/av_common.c2
-rw-r--r--demux/demux_lavf.c23
-rw-r--r--demux/packet.c75
-rw-r--r--demux/packet.h6
4 files changed, 41 insertions, 65 deletions
diff --git a/common/av_common.c b/common/av_common.c
index e0a5da7726..cced1c0694 100644
--- a/common/av_common.c
+++ b/common/av_common.c
@@ -117,6 +117,8 @@ void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb)
if (mpkt && mpkt->avpacket) {
dst->side_data = mpkt->avpacket->side_data;
dst->side_data_elems = mpkt->avpacket->side_data_elems;
+ if (dst->data == mpkt->avpacket->data)
+ dst->buf = mpkt->avpacket->buf;
}
if (mpkt && tb && tb->num > 0 && tb->den > 0)
dst->duration = mpkt->duration / av_q2d(*tb);
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index d8d177e27e..16a06829d4 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -780,23 +780,16 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
return 0;
}
-static void destroy_avpacket(void *pkt)
-{
- av_free_packet(pkt);
-}
-
static int demux_lavf_fill_buffer(demuxer_t *demux)
{
lavf_priv_t *priv = demux->priv;
- demux_packet_t *dp;
- AVPacket *pkt = talloc(NULL, AVPacket);
+ AVPacket *pkt = &(AVPacket){0};
int r = av_read_frame(priv->avfc, pkt);
if (r < 0) {
- talloc_free(pkt);
+ av_free_packet(pkt);
return r == AVERROR(EAGAIN) ? 1 : -1; // eof
}
- talloc_set_destructor(pkt, destroy_avpacket);
add_new_streams(demux);
update_metadata(demux, pkt);
@@ -806,18 +799,11 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
AVStream *st = priv->avfc->streams[pkt->stream_index];
if (!demux_stream_is_selected(stream)) {
- talloc_free(pkt);
+ av_free_packet(pkt);
return 1; // don't signal EOF if skipping a packet
}
- // If the packet has pointers to temporary fields that could be
- // overwritten/freed by next av_read_frame(), copy them to persistent
- // allocations so we can safely queue the packet for any length of time.
- if (av_dup_packet(pkt) < 0)
- abort();
-
- dp = new_demux_packet_fromdata(pkt->data, pkt->size);
- dp->avpacket = talloc_steal(dp, pkt);
+ struct demux_packet *dp = new_demux_packet_from_avpacket(pkt);
if (pkt->pts != AV_NOPTS_VALUE)
dp->pts = pkt->pts * av_q2d(st->time_base);
@@ -833,6 +819,7 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
} else if (dp->dts != MP_NOPTS_VALUE) {
priv->last_pts = dp->dts * AV_TIME_BASE;
}
+ av_free_packet(pkt);
demux_add_packet(stream, dp);
return 1;
}
diff --git a/demux/packet.c b/demux/packet.c
index 77ea78f7cc..2008113600 100644
--- a/demux/packet.c
+++ b/demux/packet.c
@@ -29,55 +29,58 @@
static void packet_destroy(void *ptr)
{
struct demux_packet *dp = ptr;
- talloc_free(dp->avpacket);
- av_free(dp->allocation);
+ av_packet_unref(dp->avpacket);
}
-static struct demux_packet *create_packet(size_t len)
+// This actually preserves only data and side data, not PTS/DTS/pos/etc.
+// It also allows avpkt->data==NULL with avpkt->size!=0 - the libavcodec API
+// does not allow it, but we do it to simplify new_demux_packet().
+struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt)
{
- if (len > 1000000000) {
+ if (avpkt->size > 1000000000) {
fprintf(stderr, "Attempt to allocate demux packet over 1 GB!\n");
abort();
}
struct demux_packet *dp = talloc(NULL, struct demux_packet);
talloc_set_destructor(dp, packet_destroy);
*dp = (struct demux_packet) {
- .len = len,
.pts = MP_NOPTS_VALUE,
.dts = MP_NOPTS_VALUE,
.duration = -1,
.pos = -1,
.stream = -1,
+ .avpacket = talloc_zero(dp, AVPacket),
};
- return dp;
-}
-
-struct demux_packet *new_demux_packet(size_t len)
-{
- struct demux_packet *dp = create_packet(len);
- dp->buffer = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!dp->buffer) {
- fprintf(stderr, "Memory allocation failure!\n");
+ av_init_packet(dp->avpacket);
+ int r = -1;
+ if (avpkt->data) {
+ // We hope that this function won't need/access AVPacket input padding,
+ // because otherwise new_demux_packet_from() wouldn't work.
+ r = av_packet_ref(dp->avpacket, avpkt);
+ } else {
+ r = av_new_packet(dp->avpacket, avpkt->size);
+ }
+ if (r < 0) {
+ fprintf(stderr, "Out of memory when referencing packet.\n");
abort();
}
- memset(dp->buffer + len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
- dp->allocation = dp->buffer;
+ dp->buffer = dp->avpacket->data;
+ dp->len = dp->avpacket->size;
return dp;
}
-// data must already have suitable padding, and does not copy the data
-struct demux_packet *new_demux_packet_fromdata(void *data, size_t len)
+// Input data doesn't need to be padded.
+struct demux_packet *new_demux_packet_from(void *data, size_t len)
{
- struct demux_packet *dp = create_packet(len);
- dp->buffer = data;
- return dp;
+ AVPacket pkt = { .data = data, .size = len };
+ return new_demux_packet_from_avpacket(&pkt);
}
-struct demux_packet *new_demux_packet_from(void *data, size_t len)
+struct demux_packet *new_demux_packet(size_t len)
{
- struct demux_packet *dp = new_demux_packet(len);
- memcpy(dp->buffer, data, len);
- return dp;
+ assert(len <= INT_MAX);
+ AVPacket pkt = { .data = NULL, .size = len };
+ return new_demux_packet_from_avpacket(&pkt);
}
void demux_packet_shorten(struct demux_packet *dp, size_t len)
@@ -92,28 +95,14 @@ void free_demux_packet(struct demux_packet *dp)
talloc_free(dp);
}
-static void destroy_avpacket(void *pkt)
-{
- av_free_packet(pkt);
-}
-
struct demux_packet *demux_copy_packet(struct demux_packet *dp)
{
struct demux_packet *new = NULL;
if (dp->avpacket) {
- assert(dp->buffer == dp->avpacket->data);
- assert(dp->len == dp->avpacket->size);
- AVPacket *newavp = talloc_zero(NULL, AVPacket);
- talloc_set_destructor(newavp, destroy_avpacket);
- av_init_packet(newavp);
- if (av_packet_ref(newavp, dp->avpacket) < 0)
- abort();
- new = new_demux_packet_fromdata(newavp->data, newavp->size);
- new->avpacket = newavp;
- }
- if (!new) {
- new = new_demux_packet(dp->len);
- memcpy(new->buffer, dp->buffer, new->len);
+ new = new_demux_packet_from_avpacket(dp->avpacket);
+ } else {
+ // Some packets might be not created by new_demux_packet*().
+ new = new_demux_packet_from(dp->buffer, dp->len);
}
new->pts = dp->pts;
new->dts = dp->dts;
diff --git a/demux/packet.h b/demux/packet.h
index 21c0a6dc8f..9c4e560e05 100644
--- a/demux/packet.h
+++ b/demux/packet.h
@@ -34,13 +34,11 @@ typedef struct demux_packet {
bool keyframe;
int stream; // source stream index
struct demux_packet *next;
- void *allocation;
- struct AVPacket *avpacket; // original libavformat packet (demux_lavf)
+ struct AVPacket *avpacket; // keep the buffer allocation
} demux_packet_t;
struct demux_packet *new_demux_packet(size_t len);
-// data must already have suitable padding
-struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
+struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt);
struct demux_packet *new_demux_packet_from(void *data, size_t len);
void demux_packet_shorten(struct demux_packet *dp, size_t len);
void free_demux_packet(struct demux_packet *dp);