From bc495bad3d8cbae71c82142906f7d36baf25a848 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Wed, 18 Jan 2012 13:54:16 +0400 Subject: [PATCH 01/25] rtsp: Remove a leftover, currently pointless check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This check isn't relevant in the way the code currently works. Also change a case of if (x == 0) into if (!x). Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 2858a9a806..77aeb63a5a 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -1158,16 +1158,14 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, } /* first try in specified port range */ - if (RTSP_RTP_PORT_MIN != 0) { - while (j <= RTSP_RTP_PORT_MAX) { - ff_url_join(buf, sizeof(buf), "rtp", NULL, host, -1, - "?localport=%d", j); - /* we will use two ports per rtp stream (rtp and rtcp) */ - j += 2; - if (ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE, - &s->interrupt_callback, NULL) == 0) - goto rtp_opened; - } + while (j <= RTSP_RTP_PORT_MAX) { + ff_url_join(buf, sizeof(buf), "rtp", NULL, host, -1, + "?localport=%d", j); + /* we will use two ports per rtp stream (rtp and rtcp) */ + j += 2; + if (!ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, NULL)) + goto rtp_opened; } av_log(s, AV_LOG_ERROR, "Unable to open an input RTP port\n"); From dbb06b8c0d7020d4a95ef8b2831aa58ec00145c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Sat, 21 Jan 2012 23:28:11 +0200 Subject: [PATCH 02/25] rtsp: Allow specifying the UDP port range via AVOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 13 +++++++++++-- libavformat/rtsp.h | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 77aeb63a5a..0e56d37a1f 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -81,6 +81,8 @@ const AVOption ff_rtsp_options[] = { { "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {(1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" }, RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"), RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"), + { "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC }, + { "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC }, { NULL }, }; @@ -1121,7 +1123,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, /* XXX: we assume the same server is used for the control of each * RTSP stream */ - for (j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) { + for (j = rt->rtp_port_min, i = 0; i < rt->nb_rtsp_streams; ++i) { char transport[2048]; /* @@ -1158,7 +1160,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, } /* first try in specified port range */ - while (j <= RTSP_RTP_PORT_MAX) { + while (j <= rt->rtp_port_max) { ff_url_join(buf, sizeof(buf), "rtp", NULL, host, -1, "?localport=%d", j); /* we will use two ports per rtp stream (rtp and rtcp) */ @@ -1356,6 +1358,13 @@ int ff_rtsp_connect(AVFormatContext *s) struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); + if (rt->rtp_port_max < rt->rtp_port_min) { + av_log(s, AV_LOG_ERROR, "Invalid UDP port range, max port %d less " + "than min port %d\n", rt->rtp_port_max, + rt->rtp_port_min); + return AVERROR(EINVAL); + } + if (!ff_network_init()) return AVERROR(EIO); diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 9d05289a91..6872a51a24 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -359,6 +359,11 @@ typedef struct RTSPState { * Mask of all requested media types */ int media_type_mask; + + /** + * Minimum and maximum local UDP ports. + */ + int rtp_port_min, rtp_port_max; } RTSPState; #define RTSP_FLAG_FILTER_SRC 0x1 /**< Filter incoming UDP packets - From 58f0978581bf736ac6888fe3013f0c5ac88bfbdc Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Wed, 18 Jan 2012 13:46:37 +0400 Subject: [PATCH 03/25] rtsp: Use a random offset for trying to open UDP ports for RTP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids (for all practical cases) the issue of reusing the same UDP port as for an earlier connection. If the remote doesn't know the previous session was closed, he might keep on sending packets to that port. If we always start off trying to open the same UDP port, we might get those packets intermixed with the new ones. This is occasionally an issue when testing RTSP stuff with DSS, perhaps also with other servers. Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 0e56d37a1f..487e910c17 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -1105,7 +1105,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, int lower_transport, const char *real_challenge) { RTSPState *rt = s->priv_data; - int rtx = 0, j, i, err, interleave = 0; + int rtx = 0, j, i, err, interleave = 0, port_off; RTSPStream *rtsp_st; RTSPMessageHeader reply1, *reply = &reply1; char cmd[2048]; @@ -1123,7 +1123,14 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, /* XXX: we assume the same server is used for the control of each * RTSP stream */ - for (j = rt->rtp_port_min, i = 0; i < rt->nb_rtsp_streams; ++i) { + /* Choose a random starting offset within the first half of the + * port range, to allow for a number of ports to try even if the offset + * happens to be at the end of the random range. */ + port_off = av_get_random_seed() % ((rt->rtp_port_max - rt->rtp_port_min)/2); + /* even random offset */ + port_off -= port_off & 0x01; + + for (j = rt->rtp_port_min + port_off, i = 0; i < rt->nb_rtsp_streams; ++i) { char transport[2048]; /* From b263bf66b7dff93b98932065fc379686f83ef751 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Thu, 12 Jan 2012 09:51:23 +0100 Subject: [PATCH 04/25] avserver: fix build after the next bump. Now that 0.8 is out we can reapply this commit. It breaks shared avserver builds due to avserver using internal libavformat symbols, which are now hidden, so this commit also disables avserver with --enable-shared. --- avserver.c | 22 ++++++++++++---------- configure | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/avserver.c b/avserver.c index 15fa64c144..f3ac319ef9 100644 --- a/avserver.c +++ b/avserver.c @@ -26,13 +26,16 @@ #include #include #include "libavformat/avformat.h" +// FIXME those are internal headers, avserver _really_ shouldn't use them #include "libavformat/ffm.h" #include "libavformat/network.h" #include "libavformat/os_support.h" #include "libavformat/rtpdec.h" #include "libavformat/rtsp.h" -// XXX for ffio_open_dyn_packet_buffer, to be removed #include "libavformat/avio_internal.h" +#include "libavformat/internal.h" +#include "libavformat/url.h" + #include "libavutil/avstring.h" #include "libavutil/lfg.h" #include "libavutil/dict.h" @@ -867,7 +870,7 @@ static void close_connection(HTTPContext *c) } h = c->rtp_handles[i]; if (h) - url_close(h); + ffurl_close(h); } ctx = &c->fmt_ctx; @@ -2248,7 +2251,6 @@ static int http_prepare_data(HTTPContext *c) * Default value from Libav * Try to set it use configuration option */ - c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE); c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE); if (avformat_write_header(&c->fmt_ctx, NULL) < 0) { @@ -2367,7 +2369,7 @@ static int http_prepare_data(HTTPContext *c) if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; else - max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]); + max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size; ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size); } else { ret = avio_open_dyn_buf(&ctx->pb); @@ -2520,8 +2522,8 @@ static int http_send_data(HTTPContext *c) } else { /* send RTP packet directly in UDP */ c->buffer_ptr += 4; - url_write(c->rtp_handles[c->packet_stream_index], - c->buffer_ptr, len); + ffurl_write(c->rtp_handles[c->packet_stream_index], + c->buffer_ptr, len); c->buffer_ptr += len; /* here we continue as we can send several packets per 10 ms slot */ } @@ -3404,10 +3406,10 @@ static int rtp_new_av_stream(HTTPContext *c, "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port)); } - if (url_open(&h, ctx->filename, AVIO_FLAG_WRITE) < 0) + if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0) goto fail; c->rtp_handles[stream_index] = h; - max_packet_size = url_get_max_packet_size(h); + max_packet_size = h->max_packet_size; break; case RTSP_LOWER_TRANSPORT_TCP: /* RTP/TCP case */ @@ -3430,7 +3432,7 @@ static int rtp_new_av_stream(HTTPContext *c, if (avformat_write_header(ctx, NULL) < 0) { fail: if (h) - url_close(h); + ffurl_close(h); av_free(ctx); return -1; } @@ -3467,7 +3469,7 @@ static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int cop } fst->priv_data = av_mallocz(sizeof(FeedData)); fst->index = stream->nb_streams; - av_set_pts_info(fst, 33, 1, 90000); + avpriv_set_pts_info(fst, 33, 1, 90000); fst->sample_aspect_ratio = codec->sample_aspect_ratio; stream->streams[stream->nb_streams++] = fst; return fst; diff --git a/configure b/configure index 93b98a96dd..ad7d9d4c5b 100755 --- a/configure +++ b/configure @@ -1534,7 +1534,7 @@ avconv_deps="avcodec avformat swscale" avplay_deps="avcodec avformat swscale sdl" avplay_select="rdft" avprobe_deps="avcodec avformat" -avserver_deps="avformat ffm_muxer fork rtp_protocol rtsp_demuxer" +avserver_deps="avformat ffm_muxer fork rtp_protocol rtsp_demuxer !shared" avserver_extralibs='$ldl' ffmpeg_deps="avcodec avformat swscale" From d32b3f40c716785bb4073aca9bd0877ecac855b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Mon, 7 Feb 2011 16:07:41 +0100 Subject: [PATCH 05/25] mxfdec: parse MXF partitions --- libavformat/mxfdec.c | 108 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index cb2ae86e48..33c931e0e0 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -52,6 +52,35 @@ #include "internal.h" #include "mxf.h" +typedef enum { + Header, + BodyPartition, + Footer +} MXFPartitionType; + +typedef enum { + OP1a, + OP1b, + OP1c, + OP2a, + OP2b, + OP2c, + OP3a, + OP3b, + OP3c, + OPAtom, +} MXFOP; + +typedef struct { + int closed; + int complete; + MXFPartitionType type; + uint64_t previous_partition; + uint64_t footer_partition; + int index_sid; + int body_sid; +} MXFPartition; + typedef struct { UID uid; enum MXFMetadataSetType type; @@ -127,6 +156,9 @@ typedef struct { } MXFMetadataSet; typedef struct { + MXFPartition *partitions; + unsigned partitions_count; + MXFOP op; UID *packages_refs; int packages_count; MXFMetadataSet **metadata_sets; @@ -376,6 +408,69 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U return 0; } +static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid) +{ + MXFContext *mxf = arg; + MXFPartition *partition; + UID op; + + if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions)) + return AVERROR(ENOMEM); + + mxf->partitions = av_realloc(mxf->partitions, (mxf->partitions_count + 1) * sizeof(*mxf->partitions)); + if (!mxf->partitions) + return AVERROR(ENOMEM); + + partition = &mxf->partitions[mxf->partitions_count++]; + + switch(uid[13]) { + case 2: + partition->type = Header; + break; + case 3: + partition->type = BodyPartition; + break; + case 4: + partition->type = Footer; + break; + default: + av_log(mxf->fc, AV_LOG_ERROR, "unknown partition type %i\n", uid[13]); + return AVERROR_INVALIDDATA; + } + + /* consider both footers to be closed (there is only Footer and CompleteFooter) */ + partition->closed = partition->type == Footer || !(uid[14] & 1); + partition->complete = uid[14] > 2; + avio_skip(pb, 16); + partition->previous_partition = avio_rb64(pb); + partition->footer_partition = avio_rb64(pb); + avio_skip(pb, 16); + partition->index_sid = avio_rb32(pb); + avio_skip(pb, 8); + partition->body_sid = avio_rb32(pb); + avio_read(pb, op, sizeof(UID)); + + av_dlog(mxf->fc, "PartitionPack: PreviousPartition = 0x%lx, " + "FooterPartition = 0x%lx, IndexSID = %i, BodySID = %i\n", + partition->previous_partition, partition->footer_partition, + partition->index_sid, partition->body_sid); + + if (op[12] == 1 && op[13] == 1) mxf->op = OP1a; + else if (op[12] == 1 && op[13] == 2) mxf->op = OP1b; + else if (op[12] == 1 && op[13] == 3) mxf->op = OP1c; + else if (op[12] == 2 && op[13] == 1) mxf->op = OP2a; + else if (op[12] == 2 && op[13] == 2) mxf->op = OP2b; + else if (op[12] == 2 && op[13] == 3) mxf->op = OP2c; + else if (op[12] == 3 && op[13] == 1) mxf->op = OP3a; + else if (op[12] == 3 && op[13] == 2) mxf->op = OP3b; + else if (op[12] == 3 && op[13] == 3) mxf->op = OP3c; + else if (op[12] == 0x10) mxf->op = OPAtom; + else + av_log(mxf->fc, AV_LOG_ERROR, "unknown operational pattern: %02xh %02xh\n", op[12], op[13]); + + return 0; +} + static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set) { if (mxf->metadata_sets_count+1 >= UINT_MAX / sizeof(*mxf->metadata_sets)) @@ -856,6 +951,16 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }, mxf_read_primer_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x03,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x04,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x01,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x02,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x03,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x02,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage, 0, AnyType }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_source_package, sizeof(MXFPackage), SourcePackage }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_material_package, sizeof(MXFPackage), MaterialPackage }, @@ -949,7 +1054,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) if (klv.key[5] == 0x53) { res = mxf_read_local_tags(mxf, &klv, metadata->read, metadata->ctx_size, metadata->type); } else - res = metadata->read(mxf, s->pb, 0, 0, NULL); + res = metadata->read(mxf, s->pb, 0, 0, klv.key); if (res < 0) { av_log(s, AV_LOG_ERROR, "error reading header metadata\n"); return res; @@ -990,6 +1095,7 @@ static int mxf_read_close(AVFormatContext *s) } av_freep(&mxf->metadata_sets[i]); } + av_freep(&mxf->partitions); av_freep(&mxf->metadata_sets); av_freep(&mxf->aesc); av_freep(&mxf->local_tags); From 0662eea6b0b6a94753ed2fda813de1c3d8692fd8 Mon Sep 17 00:00:00 2001 From: Philip de Nier Date: Mon, 12 Sep 2011 11:13:44 +0100 Subject: [PATCH 06/25] mxfdec: skip to end of structural sets This fixes reading of partition packs. The code stops reading after the operational pattern and should skip the array of essence container labels that follow. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 33c931e0e0..38e98a35a0 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -1053,8 +1053,11 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) int res; if (klv.key[5] == 0x53) { res = mxf_read_local_tags(mxf, &klv, metadata->read, metadata->ctx_size, metadata->type); - } else + } else { + uint64_t next = avio_tell(s->pb) + klv.length; res = metadata->read(mxf, s->pb, 0, 0, klv.key); + avio_seek(s->pb, next, SEEK_SET); + } if (res < 0) { av_log(s, AV_LOG_ERROR, "error reading header metadata\n"); return res; From 3359246d9a47c3f4418d994853efe17324a0159b Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Wed, 14 Sep 2011 19:26:48 +0200 Subject: [PATCH 07/25] mxfdec: check return value of avio_seek Avoid modifying state if avio_seek fails. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 38e98a35a0..44dca69b1f 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -1127,13 +1127,16 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti { AVStream *st = s->streams[stream_index]; int64_t seconds; + int ret; if (!s->bit_rate) return AVERROR_INVALIDDATA; if (sample_time < 0) sample_time = 0; seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den); - avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET); + + if ((ret = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0) + return ret; ff_update_cur_dts(s, st, sample_time); return 0; } From 83ab10f6e9559be3420af1a26c36d6bec0496e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Fri, 7 Oct 2011 15:48:00 +0200 Subject: [PATCH 08/25] mxfdec: Move FooterPartition to MXFContext and make sure it is never zero. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 44dca69b1f..b63c0c9b1e 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -76,7 +76,6 @@ typedef struct { int complete; MXFPartitionType type; uint64_t previous_partition; - uint64_t footer_partition; int index_sid; int body_sid; } MXFPartition; @@ -167,6 +166,7 @@ typedef struct { struct AVAES *aesc; uint8_t *local_tags; int local_tags_count; + uint64_t footer_partition; } MXFContext; enum MXFWrappingScheme { @@ -413,6 +413,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size MXFContext *mxf = arg; MXFPartition *partition; UID op; + uint64_t footer_partition; if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions)) return AVERROR(ENOMEM); @@ -443,16 +444,26 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size partition->complete = uid[14] > 2; avio_skip(pb, 16); partition->previous_partition = avio_rb64(pb); - partition->footer_partition = avio_rb64(pb); + footer_partition = avio_rb64(pb); avio_skip(pb, 16); partition->index_sid = avio_rb32(pb); avio_skip(pb, 8); partition->body_sid = avio_rb32(pb); avio_read(pb, op, sizeof(UID)); + /* some files don'thave FooterPartition set in every partition */ + if (footer_partition) { + if (mxf->footer_partition && mxf->footer_partition != footer_partition) { + av_log(mxf->fc, AV_LOG_ERROR, "inconsistent FooterPartition value: %li != %li\n", + mxf->footer_partition, footer_partition); + } else { + mxf->footer_partition = footer_partition; + } + } + av_dlog(mxf->fc, "PartitionPack: PreviousPartition = 0x%lx, " "FooterPartition = 0x%lx, IndexSID = %i, BodySID = %i\n", - partition->previous_partition, partition->footer_partition, + partition->previous_partition, footer_partition, partition->index_sid, partition->body_sid); if (op[12] == 1 && op[13] == 1) mxf->op = OP1a; From 682b6db706a561de0241339f6674e59581ac1330 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 9 Nov 2011 11:47:57 +0100 Subject: [PATCH 09/25] mxfdec: Parse IndexTableSegments and convert them into AVIndexEntry arrays. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on patch from Tomas Härdin and work by Georg Lippitsch Changed av_calloc to av_mallocz and added overflow checks. --- libavformat/mxfdec.c | 326 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 318 insertions(+), 8 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index b63c0c9b1e..546706255f 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -137,6 +137,20 @@ typedef struct { typedef struct { UID uid; enum MXFMetadataSetType type; + int edit_unit_byte_count; + int index_sid; + int body_sid; + int slice_count; + AVRational index_edit_rate; + uint64_t index_start_position; + uint64_t index_duration; + int *slice; + int *element_delta; + int nb_delta_entries; + int *flag_entries; + uint64_t *stream_offset_entries; + uint32_t **slice_offset_entries; + int nb_index_entries; } MXFIndexTableSegment; typedef struct { @@ -167,6 +181,11 @@ typedef struct { uint8_t *local_tags; int local_tags_count; uint64_t footer_partition; + int64_t essence_offset; + int first_essence_kl_length; + int64_t first_essence_length; + KLVPacket current_klv_data; + int current_klv_index; } MXFContext; enum MXFWrappingScheme { @@ -633,15 +652,129 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size return 0; } +static int mxf_read_delta_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) +{ + int i, length; + + segment->nb_delta_entries = avio_rb32(pb); + if (segment->nb_delta_entries < 1 || + segment->nb_delta_entries > INT_MAX >> av_log2(sizeof(*segment->slice))) + return AVERROR(ENOMEM); + + length = avio_rb32(pb); + + segment->slice = av_mallocz(segment->nb_delta_entries * + sizeof(*segment->slice)); + if (!segment->slice) + return AVERROR(ENOMEM); + segment->element_delta = av_mallocz(segment->nb_delta_entries * + sizeof(*segment->element_delta)); + if (!segment->element_delta) { + av_freep(&segment->slice); + return AVERROR(ENOMEM); + } + + for (i = 0; i < segment->nb_delta_entries; i++) { + avio_r8(pb); /* PosTableIndex */ + segment->slice[i] = avio_r8(pb); + segment->element_delta[i] = avio_rb32(pb); + } + return 0; +} + +static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) +{ + int i, j, length; + + segment->nb_index_entries = avio_rb32(pb); + if (!segment->nb_index_entries) + return 0; + else if (segment->nb_index_entries < 0 || + segment->nb_index_entries > + (INT_MAX >> av_log2(sizeof(*segment->stream_offset_entries)))) + return AVERROR(ENOMEM); + + length = avio_rb32(pb); + + segment->flag_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->flag_entries)); + segment->stream_offset_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->stream_offset_entries)); + segment->slice_offset_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->slice_offset_entries)); + + if (!segment->flag_entries || !segment->stream_offset_entries || + !segment->slice_offset_entries) + goto errmem; + + for (i = 0; i < segment->nb_index_entries; i++) { + avio_rb16(pb); /* TemporalOffset and KeyFrameOffset */ + segment->flag_entries[i] = avio_r8(pb); + segment->stream_offset_entries[i] = avio_rb64(pb); + if (segment->slice_count) { + segment->slice_offset_entries[i] = av_mallocz(segment->slice_count * + sizeof(**segment->slice_offset_entries)); + if (!segment->slice_offset_entries[i]) + goto errmem; + + for (j = 0; j < segment->slice_count; j++) + segment->slice_offset_entries[i][j] = avio_rb32(pb); + } + + avio_skip(pb, length - 11 - 4 * segment->slice_count); + } + return 0; +errmem: + if (segment->slice_offset_entries && segment->slice_count) { + for (i = 0; i < segment->nb_index_entries; i++) + av_free(segment->slice_offset_entries[i]); + } + av_freep(&segment->flag_entries); + av_freep(&segment->stream_offset_entries); + av_freep(&segment->slice_offset_entries); + return AVERROR(ENOMEM); +} + static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int size, UID uid) { + MXFIndexTableSegment *segment = arg; switch(tag) { - case 0x3F05: av_dlog(NULL, "EditUnitByteCount %d\n", avio_rb32(pb)); break; - case 0x3F06: av_dlog(NULL, "IndexSID %d\n", avio_rb32(pb)); break; - case 0x3F07: av_dlog(NULL, "BodySID %d\n", avio_rb32(pb)); break; - case 0x3F0B: av_dlog(NULL, "IndexEditRate %d/%d\n", avio_rb32(pb), avio_rb32(pb)); break; - case 0x3F0C: av_dlog(NULL, "IndexStartPosition %"PRIu64"\n", avio_rb64(pb)); break; - case 0x3F0D: av_dlog(NULL, "IndexDuration %"PRIu64"\n", avio_rb64(pb)); break; + case 0x3F05: + segment->edit_unit_byte_count = avio_rb32(pb); + av_dlog(NULL, "EditUnitByteCount %d\n", segment->edit_unit_byte_count); + break; + case 0x3F06: + segment->index_sid = avio_rb32(pb); + av_dlog(NULL, "IndexSID %d\n", segment->index_sid); + break; + case 0x3F07: + segment->body_sid = avio_rb32(pb); + av_dlog(NULL, "BodySID %d\n", segment->body_sid); + break; + case 0x3F08: + segment->slice_count = avio_r8(pb); + av_dlog(NULL, "SliceCount %d\n", segment->slice_count); + break; + case 0x3F09: + av_dlog(NULL, "DeltaEntryArray found\n"); + return mxf_read_delta_entry_array(pb, segment); + case 0x3F0A: + av_dlog(NULL, "IndexEntryArray found\n"); + return mxf_read_index_entry_array(pb, segment); + case 0x3F0B: + segment->index_edit_rate.num = avio_rb32(pb); + segment->index_edit_rate.den = avio_rb32(pb); + av_dlog(NULL, "IndexEditRate %d/%d\n", segment->index_edit_rate.num, + segment->index_edit_rate.den); + break; + case 0x3F0C: + segment->index_start_position = avio_rb64(pb); + av_dlog(NULL, "IndexStartPosition %"PRId64"\n", segment->index_start_position); + break; + case 0x3F0D: + segment->index_duration = avio_rb64(pb); + av_dlog(NULL, "IndexDuration %"PRId64"\n", segment->index_duration); + break; } return 0; } @@ -778,11 +911,173 @@ static const MXFCodecUL mxf_essence_container_uls[] = { { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE }, }; +static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments) +{ + int i, j, nb_segments = 0; + MXFIndexTableSegment **unsorted_segments; + int last_body_sid = -1, last_index_sid = -1, last_index_start = -1; + + /* count number of segments, allocate arrays and copy unsorted segments */ + for (i = 0; i < mxf->metadata_sets_count; i++) + if (mxf->metadata_sets[i]->type == IndexTableSegment) + nb_segments++; + + *sorted_segments = av_mallocz(nb_segments * sizeof(**sorted_segments)); + unsorted_segments = av_mallocz(nb_segments * sizeof(*unsorted_segments)); + if (!sorted_segments || !unsorted_segments) { + av_freep(sorted_segments); + av_free(unsorted_segments); + return AVERROR(ENOMEM); + } + + for (i = j = 0; i < mxf->metadata_sets_count; i++) + if (mxf->metadata_sets[i]->type == IndexTableSegment) + unsorted_segments[j++] = (MXFIndexTableSegment*)mxf->metadata_sets[i]; + + *nb_sorted_segments = 0; + + /* sort segments by {BodySID, IndexSID, IndexStartPosition}, remove duplicates while we're at it */ + for (i = 0; i < nb_segments; i++) { + int best = -1, best_body_sid = -1, best_index_sid = -1, best_index_start = -1; + + for (j = 0; j < nb_segments; j++) { + MXFIndexTableSegment *s = unsorted_segments[j]; + + /* Require larger BosySID, IndexSID or IndexStartPosition then the previous entry. This removes duplicates. + * We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around. + */ + if ((i == 0 || s->body_sid > last_body_sid || s->index_sid > last_index_sid || s->index_start_position > last_index_start) && + (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start)) { + best = j; + best_body_sid = s->body_sid; + best_index_sid = s->index_sid; + best_index_start = s->index_start_position; + } + } + + /* no suitable entry found -> we're done */ + if (best == -1) + break; + + (*sorted_segments)[(*nb_sorted_segments)++] = unsorted_segments[best]; + last_body_sid = best_body_sid; + last_index_sid = best_index_sid; + last_index_start = best_index_start; + } + + av_free(unsorted_segments); + + return 0; +} + +static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) +{ + int64_t accumulated_offset = 0; + int j, k, ret, nb_sorted_segments; + MXFIndexTableSegment **sorted_segments; + + if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments))) + return ret; + + for (j = 0; j < nb_sorted_segments; j++) { + int n_delta = i; + int duration, sample_duration = 1, last_sample_size = 0; + int64_t segment_size; + MXFIndexTableSegment *tableseg = sorted_segments[j]; + + /* reset accumulated_offset on BodySID change */ + if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid) + accumulated_offset = 0; + + /* HACK: How to correctly link between streams and slices? */ + if (i < st->index) + n_delta++; + if (n_delta >= tableseg->nb_delta_entries && st->index != 0) + continue; + duration = tableseg->index_duration > 0 ? tableseg->index_duration : + st->duration - st->nb_index_entries; + segment_size = tableseg->edit_unit_byte_count * duration; + /* check small EditUnitByteCount for audio */ + if (tableseg->edit_unit_byte_count && tableseg->edit_unit_byte_count < 32 + && !tableseg->index_duration) { + /* duration might be prime relative to the new sample_duration, + * which means we need to handle the last frame differently */ + sample_duration = 8192; + last_sample_size = (duration % sample_duration) * tableseg->edit_unit_byte_count; + tableseg->edit_unit_byte_count *= sample_duration; + duration /= sample_duration; + if (last_sample_size) duration++; + } + + for (k = 0; k < duration; k++) { + int64_t pos; + int size, flags = 0; + + if (k < tableseg->nb_index_entries) { + pos = tableseg->stream_offset_entries[k]; + if (n_delta < tableseg->nb_delta_entries) { + if (n_delta < tableseg->nb_delta_entries - 1) { + size = + tableseg->slice_offset_entries[k][tableseg->slice[n_delta+1]-1] + + tableseg->element_delta[n_delta+1] - + tableseg->element_delta[n_delta]; + if (tableseg->slice[n_delta] > 0) + size -= tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1]; + } else if (k < duration - 1) { + size = tableseg->stream_offset_entries[k+1] - + tableseg->stream_offset_entries[k] - + tableseg->slice_offset_entries[k][tableseg->slice[tableseg->nb_delta_entries-1]-1] - + tableseg->element_delta[tableseg->nb_delta_entries-1]; + } else + size = 0; + if (tableseg->slice[n_delta] > 0) + pos += tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1]; + pos += tableseg->element_delta[n_delta]; + } else + size = 0; + flags = !(tableseg->flag_entries[k] & 0x30) ? AVINDEX_KEYFRAME : 0; + } else { + pos = (int64_t)k * tableseg->edit_unit_byte_count + accumulated_offset; + if (n_delta < tableseg->nb_delta_entries - 1) + size = tableseg->element_delta[n_delta+1] - tableseg->element_delta[n_delta]; + else { + /* use smaller size for last sample if we should */ + if (last_sample_size && k == duration - 1) + size = last_sample_size; + else + size = tableseg->edit_unit_byte_count; + if (tableseg->nb_delta_entries) + size -= tableseg->element_delta[tableseg->nb_delta_entries-1]; + } + if (n_delta < tableseg->nb_delta_entries) + pos += tableseg->element_delta[n_delta]; + flags = AVINDEX_KEYFRAME; + } + + if (k > 0 && pos < mxf->first_essence_length && accumulated_offset == 0) + pos += mxf->first_essence_kl_length; + + pos += mxf->essence_offset; + + av_dlog(mxf->fc, "Stream %d IndexEntry %d n_Delta %d Offset %"PRIx64" Timestamp %"PRId64"\n", + st->index, st->nb_index_entries, n_delta, pos, sample_duration * st->nb_index_entries); + + if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0) + return ret; + } + accumulated_offset += segment_size; + } + + av_free(sorted_segments); + + return 0; +} + static int mxf_parse_structural_metadata(MXFContext *mxf) { MXFPackage *material_package = NULL; MXFPackage *temp_package = NULL; - int i, j, k; + int i, j, k, ret; av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count); /* TODO: handle multiple material packages (OP3x) */ @@ -956,6 +1251,9 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) av_log(mxf->fc, AV_LOG_WARNING, "only frame wrapped mappings are correctly supported\n"); st->need_parsing = AVSTREAM_PARSE_FULL; } + + if ((ret = mxf_parse_index(mxf, i, st))) + return ret; } return 0; } @@ -1085,7 +1383,8 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; - int i; + MXFIndexTableSegment *seg; + int i, j; av_freep(&mxf->packages_refs); @@ -1104,6 +1403,17 @@ static int mxf_read_close(AVFormatContext *s) case MaterialPackage: av_freep(&((MXFPackage *)mxf->metadata_sets[i])->tracks_refs); break; + case IndexTableSegment: + seg = (MXFIndexTableSegment *)mxf->metadata_sets[i]; + if (seg->slice_count) + for (j = 0; j < seg->nb_index_entries; j++) + av_freep(&seg->slice_offset_entries[j]); + av_freep(&seg->slice); + av_freep(&seg->element_delta); + av_freep(&seg->flag_entries); + av_freep(&seg->stream_offset_entries); + av_freep(&seg->slice_offset_entries); + break; default: break; } From edb50856473d44a61e286af3a11087027722f5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Fri, 11 Nov 2011 15:01:34 +0100 Subject: [PATCH 10/25] mxfdec: Add hack that adjusts the n_delta calculation when system items are present. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 546706255f..2945ffa48b 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -181,6 +181,7 @@ typedef struct { uint8_t *local_tags; int local_tags_count; uint64_t footer_partition; + int system_item; int64_t essence_offset; int first_essence_kl_length; int64_t first_essence_length; @@ -205,6 +206,7 @@ typedef struct { /* partial keys to match */ static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 }; static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 }; +static const uint8_t mxf_system_item_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04 }; static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 }; /* complete keys to match */ static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 }; @@ -990,7 +992,7 @@ static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) accumulated_offset = 0; /* HACK: How to correctly link between streams and slices? */ - if (i < st->index) + if (i < mxf->system_item + st->index) n_delta++; if (n_delta >= tableseg->nb_delta_entries && st->index != 0) continue; @@ -1356,6 +1358,11 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) avio_seek(s->pb, klv.offset, SEEK_SET); break; } + if (IS_KLV_KEY(klv.key, mxf_system_item_key)) { + mxf->system_item = 1; + avio_skip(s->pb, klv.length); + continue; + } for (metadata = mxf_metadata_read_table; metadata->read; metadata++) { if (IS_KLV_KEY(klv.key, metadata->key)) { From 0ac5e6a8c4d8223323c1a8c2801d41d1011c1995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Mon, 28 Nov 2011 09:53:10 +0100 Subject: [PATCH 11/25] mxfdec: add EssenceContainer UL found in 0001GL00.MXF.A1.mxf_opatom.mxf Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 2945ffa48b..6f4824a44e 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -910,6 +910,7 @@ static const MXFCodecUL mxf_essence_container_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, CODEC_ID_MP2 }, /* MPEG-ES Frame wrapped, 0x40 ??? stream id */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, CODEC_ID_PCM_S16LE }, /* D-10 Mapping 50Mbps PAL Extended Template */ + { { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0xFF,0x4B,0x46,0x41,0x41,0x00,0x0D,0x4D,0x4F }, 14, CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE }, }; From 0999151962f60abee1989d863dba6c09573b7a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Mon, 28 Nov 2011 10:51:00 +0100 Subject: [PATCH 12/25] mxfdec: Make sure DataDefinition is consistent between material track and source track. This fixes 0001GL.MXF.V1.mxf_opatom.mxf and 0001GL00.MXF.A1.mxf_opatom.mxf getting two streams each due to both using the same SourcePackageID. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 6f4824a44e..7c45b01c49 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -1153,6 +1153,18 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (!source_track) continue; + if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n"); + return AVERROR_INVALIDDATA; + } + + /* 0001GL00.MXF.A1.mxf_opatom.mxf has the same SourcePackageID as 0001GL.MXF.V1.mxf_opatom.mxf + * This would result in both files appearing to have two streams. Work around this by sanity checking DataDefinition */ + if (memcmp(material_track->sequence->data_definition_ul, source_track->sequence->data_definition_ul, 16)) { + av_log(mxf->fc, AV_LOG_ERROR, "material track %d: DataDefinition mismatch\n", material_track->track_id); + continue; + } + st = avformat_new_stream(mxf->fc, NULL); if (!st) { av_log(mxf->fc, AV_LOG_ERROR, "could not allocate stream\n"); @@ -1166,11 +1178,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->start_time = component->start_position; avpriv_set_pts_info(st, 64, material_track->edit_rate.num, material_track->edit_rate.den); - if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) { - av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n"); - return AVERROR_INVALIDDATA; - } - PRINT_KEY(mxf->fc, "data definition ul", source_track->sequence->data_definition_ul); codec_ul = mxf_get_codec_ul(ff_mxf_data_definition_uls, &source_track->sequence->data_definition_ul); st->codec->codec_type = codec_ul->id; From 775d3b4ab174d3654864668beb8a68657bc6da9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Mon, 14 Mar 2011 17:00:21 +0100 Subject: [PATCH 13/25] mxfdec: Speed up metadata and index parsing. Specifically, this means parsing as before until we run into essence. At that point we seek to the footer and parse until EOF. After that we start seeking backward to the previous partition and parse that until we run into essence or the next partition. This procedure is repeated until we encounter the last partition we parsed in the forward direction. The end result of all this is that large essence containers are not needlessly parsed. This speeds up parsing large files a lot. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 129 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 10 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 7c45b01c49..2ab2efebf8 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -187,6 +187,10 @@ typedef struct { int64_t first_essence_length; KLVPacket current_klv_data; int current_klv_index; + int run_in; + MXFPartition *current_partition; + int parsing_backward; + int64_t last_forward_tell; } MXFContext; enum MXFWrappingScheme { @@ -443,7 +447,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size if (!mxf->partitions) return AVERROR(ENOMEM); - partition = &mxf->partitions[mxf->partitions_count++]; + partition = mxf->current_partition = &mxf->partitions[mxf->partitions_count++]; switch(uid[13]) { case 2: @@ -1341,35 +1345,134 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF return ctx_size ? mxf_add_metadata_set(mxf, ctx) : 0; } +/** + * Seeks to the previous partition, if possible + * @return <= 0 if we should stop parsing, > 0 if we should keep going + */ +static int mxf_seek_to_previous_partition(MXFContext *mxf) +{ + AVIOContext *pb = mxf->fc->pb; + + if (!mxf->current_partition || + mxf->run_in + mxf->current_partition->previous_partition <= mxf->last_forward_tell) + return 0; /* we've parsed all partitions */ + + /* seek to previous partition */ + avio_seek(pb, mxf->run_in + mxf->current_partition->previous_partition, SEEK_SET); + mxf->current_partition = NULL; + + av_dlog(mxf->fc, "seeking to previous partition\n"); + + return 1; +} + +/** + * Called when essence is encountered + * @return <= 0 if we should stop parsing, > 0 if we should keep going + */ +static int mxf_parse_handle_essence(MXFContext *mxf) +{ + AVIOContext *pb = mxf->fc->pb; + int64_t ret; + + if (!mxf->current_partition) { + av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to PartitionPack\n"); + return AVERROR_INVALIDDATA; + } + + if (mxf->parsing_backward) { + return mxf_seek_to_previous_partition(mxf); + } else { + if (!mxf->footer_partition) { + av_dlog(mxf->fc, "no footer\n"); + return 0; + } + + av_dlog(mxf->fc, "seeking to footer\n"); + + /* remember where we were so we don't end up seeking further back than this */ + mxf->last_forward_tell = avio_tell(pb); + + if (!pb->seekable) { + av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing footer\n"); + return -1; + } + + /* seek to footer partition and parse backward */ + if ((ret = avio_seek(pb, mxf->run_in + mxf->footer_partition, SEEK_SET)) < 0) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to seek to footer @ 0x%"PRIx64" (%"PRId64") - partial file?\n", + mxf->run_in + mxf->footer_partition, ret); + return ret; + } + + mxf->current_partition = NULL; + mxf->parsing_backward = 1; + } + + return 1; +} + +/** + * Called when the next partition or EOF is encountered + * @return <= 0 if we should stop parsing, > 0 if we should keep going + */ +static int mxf_parse_handle_partition_or_eof(MXFContext *mxf) +{ + return mxf->parsing_backward ? mxf_seek_to_previous_partition(mxf) : 1; +} + static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) { MXFContext *mxf = s->priv_data; KLVPacket klv; + mxf->last_forward_tell = INT64_MAX; + if (!mxf_read_sync(s->pb, mxf_header_partition_pack_key, 14)) { av_log(s, AV_LOG_ERROR, "could not find header partition pack key\n"); return AVERROR_INVALIDDATA; } avio_seek(s->pb, -14, SEEK_CUR); mxf->fc = s; + mxf->run_in = avio_tell(s->pb); + while (!s->pb->eof_reached) { - int ret; const MXFMetadataReadTableEntry *metadata; - if ((ret = klv_read_packet(&klv, s->pb)) < 0) - return ret; + if (klv_read_packet(&klv, s->pb) < 0) { + /* EOF - seek to previous partition or stop */ + if(mxf_parse_handle_partition_or_eof(mxf) <= 0) + break; + else + continue; + } + PRINT_KEY(s, "read header", klv.key); av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) || - IS_KLV_KEY(klv.key, mxf_essence_element_key)) { - /* FIXME avoid seek */ - avio_seek(s->pb, klv.offset, SEEK_SET); - break; - } + IS_KLV_KEY(klv.key, mxf_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_system_item_key)) { if (IS_KLV_KEY(klv.key, mxf_system_item_key)) { mxf->system_item = 1; - avio_skip(s->pb, klv.length); + } + + if (!mxf->essence_offset) + mxf->essence_offset = klv.offset; + + if (!mxf->first_essence_kl_length && IS_KLV_KEY(klv.key, mxf_essence_element_key)) { + mxf->first_essence_kl_length = avio_tell(s->pb) - klv.offset; + mxf->first_essence_length = klv.length; + } + + /* seek to footer, previous partition or stop */ + if (mxf_parse_handle_essence(mxf) <= 0) + break; continue; + } else if (!memcmp(klv.key, mxf_header_partition_pack_key, 13) && + klv.key[13] >= 2 && klv.key[13] <= 4 && mxf->current_partition) { + /* next partition pack - keep going, seek to previous partition or stop */ + if(mxf_parse_handle_partition_or_eof(mxf) <= 0) + break; } for (metadata = mxf_metadata_read_table; metadata->read; metadata++) { @@ -1392,6 +1495,12 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) if (!metadata->read) avio_skip(s->pb, klv.length); } + /* FIXME avoid seek */ + if (!mxf->essence_offset) { + av_log(s, AV_LOG_ERROR, "no essence\n"); + return AVERROR_INVALIDDATA; + } + avio_seek(s->pb, mxf->essence_offset, SEEK_SET); return mxf_parse_structural_metadata(mxf); } From be78e3f6ec42b150256830873a62c7f3e4f0a24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Wed, 7 Dec 2011 17:43:36 +0100 Subject: [PATCH 14/25] mxfdec: parse ThisPartition Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 2ab2efebf8..5ce9a24382 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -78,6 +78,7 @@ typedef struct { uint64_t previous_partition; int index_sid; int body_sid; + int64_t this_partition; } MXFPartition; typedef struct { @@ -467,7 +468,8 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size /* consider both footers to be closed (there is only Footer and CompleteFooter) */ partition->closed = partition->type == Footer || !(uid[14] & 1); partition->complete = uid[14] > 2; - avio_skip(pb, 16); + avio_skip(pb, 8); + partition->this_partition = avio_rb64(pb); partition->previous_partition = avio_rb64(pb); footer_partition = avio_rb64(pb); avio_skip(pb, 16); @@ -486,8 +488,9 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size } } - av_dlog(mxf->fc, "PartitionPack: PreviousPartition = 0x%lx, " + av_dlog(mxf->fc, "PartitionPack: ThisPartition = 0x%lx, PreviousPartition = 0x%lx, " "FooterPartition = 0x%lx, IndexSID = %i, BodySID = %i\n", + partition->this_partition, partition->previous_partition, footer_partition, partition->index_sid, partition->body_sid); From c77a5460b19e6f93470132af020e3fe813293bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Thu, 8 Dec 2011 11:06:23 +0100 Subject: [PATCH 15/25] mxfdec: Make mxf->partitions sorted by offset. This also zeroes new entries for good measure (used by future patches). Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 5ce9a24382..e451fe1520 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -192,6 +192,7 @@ typedef struct { MXFPartition *current_partition; int parsing_backward; int64_t last_forward_tell; + int last_forward_partition; } MXFContext; enum MXFWrappingScheme { @@ -448,7 +449,20 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size if (!mxf->partitions) return AVERROR(ENOMEM); - partition = mxf->current_partition = &mxf->partitions[mxf->partitions_count++]; + if (mxf->parsing_backward) { + /* insert the new partition pack in the middle + * this makes the entries in mxf->partitions sorted by offset */ + memmove(&mxf->partitions[mxf->last_forward_partition+1], + &mxf->partitions[mxf->last_forward_partition], + (mxf->partitions_count - mxf->last_forward_partition)*sizeof(*mxf->partitions)); + partition = mxf->current_partition = &mxf->partitions[mxf->last_forward_partition]; + } else { + mxf->last_forward_partition++; + partition = mxf->current_partition = &mxf->partitions[mxf->partitions_count]; + } + + memset(partition, 0, sizeof(*partition)); + mxf->partitions_count++; switch(uid[13]) { case 2: From 21cc4b3327e1b3df8cfa7320d2a0297d03686123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Thu, 8 Dec 2011 11:21:58 +0100 Subject: [PATCH 16/25] mxfdec: use av_dlog() for 'no corresponding source package found' This is not an error and expected behavior for OPAtom files. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 173 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 142 insertions(+), 31 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index e451fe1520..b6445e198c 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -69,6 +69,7 @@ typedef enum { OP3b, OP3c, OPAtom, + OPSonyOpt, /* FATE sample, violates the spec in places */ } MXFOP; typedef struct { @@ -79,6 +80,12 @@ typedef struct { int index_sid; int body_sid; int64_t this_partition; + int64_t essence_offset; ///< absolute offset of essence + int64_t essence_length; + int32_t kag_size; + int64_t header_byte_count; + int64_t index_byte_count; + int pack_length; } MXFPartition; typedef struct { @@ -148,6 +155,7 @@ typedef struct { int *slice; int *element_delta; int nb_delta_entries; + int8_t *temporal_offset_entries; int *flag_entries; uint64_t *stream_offset_entries; uint32_t **slice_offset_entries; @@ -200,7 +208,8 @@ enum MXFWrappingScheme { Clip, }; -typedef int MXFMetadataReadFunc(void *arg, AVIOContext *pb, int tag, int size, UID uid); +/* NOTE: klv_offset is not set (-1) for local keys */ +typedef int MXFMetadataReadFunc(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset); typedef struct { const UID key; @@ -414,7 +423,7 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; int item_num = avio_rb32(pb); @@ -435,7 +444,7 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U return 0; } -static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; MXFPartition *partition; @@ -463,6 +472,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size memset(partition, 0, sizeof(*partition)); mxf->partitions_count++; + partition->pack_length = avio_tell(pb) - klv_offset + size; switch(uid[13]) { case 2: @@ -482,11 +492,13 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size /* consider both footers to be closed (there is only Footer and CompleteFooter) */ partition->closed = partition->type == Footer || !(uid[14] & 1); partition->complete = uid[14] > 2; - avio_skip(pb, 8); + avio_skip(pb, 4); + partition->kag_size = avio_rb32(pb); partition->this_partition = avio_rb64(pb); partition->previous_partition = avio_rb64(pb); footer_partition = avio_rb64(pb); - avio_skip(pb, 16); + partition->header_byte_count = avio_rb64(pb); + partition->index_byte_count = avio_rb64(pb); partition->index_sid = avio_rb32(pb); avio_skip(pb, 8); partition->body_sid = avio_rb32(pb); @@ -518,8 +530,22 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size else if (op[12] == 3 && op[13] == 2) mxf->op = OP3b; else if (op[12] == 3 && op[13] == 3) mxf->op = OP3c; else if (op[12] == 0x10) mxf->op = OPAtom; - else - av_log(mxf->fc, AV_LOG_ERROR, "unknown operational pattern: %02xh %02xh\n", op[12], op[13]); + else if (op[12] == 64&& op[13] == 1) mxf->op = OPSonyOpt; + else { + av_log(mxf->fc, AV_LOG_ERROR, "unknown operational pattern: %02xh %02xh - guessing OP1a\n", op[12], op[13]); + mxf->op = OP1a; + } + + if (partition->kag_size <= 0 || partition->kag_size > (1 << 20)) { + av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %i - guessing ", partition->kag_size); + + if (mxf->op == OPSonyOpt) + partition->kag_size = 512; + else + partition->kag_size = 1; + + av_log(mxf->fc, AV_LOG_WARNING, "%i\n", partition->kag_size); + } return 0; } @@ -536,7 +562,7 @@ static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set) return 0; } -static int mxf_read_cryptographic_context(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_cryptographic_context(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFCryptoContext *cryptocontext = arg; if (size != 16) @@ -546,7 +572,7 @@ static int mxf_read_cryptographic_context(void *arg, AVIOContext *pb, int tag, i return 0; } -static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; switch (tag) { @@ -564,7 +590,7 @@ static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int siz return 0; } -static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFStructuralComponent *source_clip = arg; switch(tag) { @@ -586,7 +612,7 @@ static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, U return 0; } -static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFPackage *package = arg; switch(tag) { @@ -604,7 +630,7 @@ static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int si return 0; } -static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFTrack *track = arg; switch(tag) { @@ -625,7 +651,7 @@ static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid return 0; } -static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFSequence *sequence = arg; switch(tag) { @@ -649,7 +675,7 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID return 0; } -static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFPackage *package = arg; switch(tag) { @@ -719,19 +745,22 @@ static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *seg length = avio_rb32(pb); - segment->flag_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->flag_entries)); - segment->stream_offset_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->stream_offset_entries)); - segment->slice_offset_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->slice_offset_entries)); + segment->temporal_offset_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->temporal_offset_entries)); + segment->flag_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->flag_entries)); + segment->stream_offset_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->stream_offset_entries)); + segment->slice_offset_entries = av_mallocz(segment->nb_index_entries * + sizeof(*segment->slice_offset_entries)); - if (!segment->flag_entries || !segment->stream_offset_entries || - !segment->slice_offset_entries) + if (!segment->flag_entries || !segment->stream_offset_entries || + !segment->slice_offset_entries || !segment->temporal_offset_entries) goto errmem; for (i = 0; i < segment->nb_index_entries; i++) { - avio_rb16(pb); /* TemporalOffset and KeyFrameOffset */ + segment->temporal_offset_entries[i] = avio_r8(pb); + avio_r8(pb); /* KeyFrameOffset */ segment->flag_entries[i] = avio_r8(pb); segment->stream_offset_entries[i] = avio_rb64(pb); if (segment->slice_count) { @@ -755,10 +784,11 @@ errmem: av_freep(&segment->flag_entries); av_freep(&segment->stream_offset_entries); av_freep(&segment->slice_offset_entries); + av_freep(&segment->temporal_offset_entries); return AVERROR(ENOMEM); } -static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFIndexTableSegment *segment = arg; switch(tag) { @@ -821,7 +851,7 @@ static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor) ff_mxf_decode_pixel_layout(layout, &descriptor->pix_fmt); } -static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid) +static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFDescriptor *descriptor = arg; switch(tag) { @@ -1153,7 +1183,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } } if (!source_package) { - av_log(mxf->fc, AV_LOG_ERROR, "material track %d: no corresponding source package found\n", material_track->track_id); + av_dlog(mxf->fc, "material track %d: no corresponding source package found\n", material_track->track_id); break; } for (k = 0; k < source_package->tracks_count; k++) { @@ -1353,7 +1383,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF } if (ctx_size && tag == 0x3C0A) avio_read(pb, ctx->uid, 16); - else if ((ret = read_child(ctx, pb, tag, size, uid)) < 0) + else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0) return ret; avio_seek(pb, next, SEEK_SET); @@ -1438,6 +1468,79 @@ static int mxf_parse_handle_partition_or_eof(MXFContext *mxf) return mxf->parsing_backward ? mxf_seek_to_previous_partition(mxf) : 1; } +/** + * Figure out the proper offset and length of the essence container + * in each partition + */ +static void mxf_compute_essence_containers(MXFContext *mxf) +{ + int x; + + /* everything is already correct */ + if (mxf->op == OPAtom) + return; + + for (x = 0; x < mxf->partitions_count; x++) { + MXFPartition *p = &mxf->partitions[x]; + + if (!p->body_sid) + continue; /* BodySID == 0 -> no essence */ + + if (x >= mxf->partitions_count - 1) + break; /* last partition - can't compute length (and we don't need to) */ + + /* essence container spans to the next partition */ + p->essence_length = mxf->partitions[x+1].this_partition - p->essence_offset; + + if (p->essence_length < 0) { + /* next ThisPartition < essence_offset */ + p->essence_length = 0; + av_log(mxf->fc, AV_LOG_ERROR, "partition %i: bad ThisPartition = %lx\n", + x+1, mxf->partitions[x+1].this_partition); + } + } +} + +static int64_t round_to_kag(int64_t position, int kag_size) +{ + /* TODO: account for run-in? the spec isn't clear whether KAG should account for it */ + /* NOTE: kag_size may be any integer between 1 - 2^10 */ + int64_t ret = (position / kag_size) * kag_size; + return ret == position ? ret : ret + kag_size; +} + +static inline void compute_partition_essence_offset(AVFormatContext *s, + MXFContext *mxf, + KLVPacket *klv) +{ + MXFPartition *cur_part = mxf->current_partition; + /* for OP1a we compute essence_offset + * for OPAtom we point essence_offset after the KL + * (usually op1a_essence_offset + 20 or 25) + * TODO: for OP1a we could eliminate this entire if statement, always + * stopping parsing at op1a_essence_offset + * for OPAtom we still need the actual essence_offset though + * (the KL's length can vary) + */ + int64_t op1a_essence_offset = + round_to_kag(cur_part->this_partition + cur_part->pack_length, + cur_part->kag_size) + + round_to_kag(cur_part->header_byte_count, cur_part->kag_size) + + round_to_kag(cur_part->index_byte_count, cur_part->kag_size); + + if (mxf->op == OPAtom) { + /* point essence_offset to the actual data + * OPAtom has all the essence in one big KLV + */ + cur_part->essence_offset = avio_tell(s->pb); + cur_part->essence_length = klv->length; + } else { + /* NOTE: op1a_essence_offset may be less than to klv.offset + * (C0023S01.mxf) */ + cur_part->essence_offset = op1a_essence_offset; + } +} + static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) { MXFContext *mxf = s->priv_data; @@ -1469,9 +1572,13 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) || IS_KLV_KEY(klv.key, mxf_essence_element_key) || IS_KLV_KEY(klv.key, mxf_system_item_key)) { - if (IS_KLV_KEY(klv.key, mxf_system_item_key)) { - mxf->system_item = 1; - } + if (!mxf->current_partition->essence_offset) { + compute_partition_essence_offset(s, mxf, &klv); + } + + if (IS_KLV_KEY(klv.key, mxf_system_item_key)) { + mxf->system_item = 1; + } if (!mxf->essence_offset) mxf->essence_offset = klv.offset; @@ -1499,7 +1606,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) res = mxf_read_local_tags(mxf, &klv, metadata->read, metadata->ctx_size, metadata->type); } else { uint64_t next = avio_tell(s->pb) + klv.length; - res = metadata->read(mxf, s->pb, 0, 0, klv.key); + res = metadata->read(mxf, s->pb, 0, klv.length, klv.key, klv.offset); avio_seek(s->pb, next, SEEK_SET); } if (res < 0) { @@ -1518,6 +1625,9 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) return AVERROR_INVALIDDATA; } avio_seek(s->pb, mxf->essence_offset, SEEK_SET); + + mxf_compute_essence_containers(mxf); + return mxf_parse_structural_metadata(mxf); } @@ -1551,6 +1661,7 @@ static int mxf_read_close(AVFormatContext *s) av_freep(&seg->slice_offset_entries[j]); av_freep(&seg->slice); av_freep(&seg->element_delta); + av_freep(&seg->temporal_offset_entries); av_freep(&seg->flag_entries); av_freep(&seg->stream_offset_entries); av_freep(&seg->slice_offset_entries); From f4187945a0b5a75f0bae9039baf40c7f6cc5f394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Thu, 8 Dec 2011 11:53:57 +0100 Subject: [PATCH 17/25] mxfdec: Use MaterialPackage - Track - TrackID instead of the system_item hack. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index b6445e198c..f886f1f8ec 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -190,7 +190,6 @@ typedef struct { uint8_t *local_tags; int local_tags_count; uint64_t footer_partition; - int system_item; int64_t essence_offset; int first_essence_kl_length; int64_t first_essence_length; @@ -1024,17 +1023,22 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment return 0; } -static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) +static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st) { int64_t accumulated_offset = 0; int j, k, ret, nb_sorted_segments; MXFIndexTableSegment **sorted_segments; + int n_delta = track_id - 1; /* TrackID = 1-based stream index */ + + if (track_id < 1) { + av_log(mxf->fc, AV_LOG_ERROR, "TrackID not positive: %i\n", track_id); + return AVERROR_INVALIDDATA; + } if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments))) return ret; for (j = 0; j < nb_sorted_segments; j++) { - int n_delta = i; int duration, sample_duration = 1, last_sample_size = 0; int64_t segment_size; MXFIndexTableSegment *tableseg = sorted_segments[j]; @@ -1043,9 +1047,6 @@ static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid) accumulated_offset = 0; - /* HACK: How to correctly link between streams and slices? */ - if (i < mxf->system_item + st->index) - n_delta++; if (n_delta >= tableseg->nb_delta_entries && st->index != 0) continue; duration = tableseg->index_duration > 0 ? tableseg->index_duration : @@ -1113,8 +1114,8 @@ static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) pos += mxf->essence_offset; - av_dlog(mxf->fc, "Stream %d IndexEntry %d n_Delta %d Offset %"PRIx64" Timestamp %"PRId64"\n", - st->index, st->nb_index_entries, n_delta, pos, sample_duration * st->nb_index_entries); + av_dlog(mxf->fc, "Stream %d IndexEntry %d TrackID %d Offset %"PRIx64" Timestamp %"PRId64"\n", + st->index, st->nb_index_entries, track_id, pos, sample_duration * st->nb_index_entries); if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0) return ret; @@ -1313,7 +1314,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->need_parsing = AVSTREAM_PARSE_FULL; } - if ((ret = mxf_parse_index(mxf, i, st))) + if ((ret = mxf_parse_index(mxf, material_track->track_id, st))) return ret; } return 0; @@ -1576,10 +1577,6 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) compute_partition_essence_offset(s, mxf, &klv); } - if (IS_KLV_KEY(klv.key, mxf_system_item_key)) { - mxf->system_item = 1; - } - if (!mxf->essence_offset) mxf->essence_offset = klv.offset; From 3cb33fbe919d3664977e88a1ce4554a330ccc48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Thu, 8 Dec 2011 12:23:36 +0100 Subject: [PATCH 18/25] mxfdec: Compute packet offsets properly. This replaces the old essence_offset code. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 53 +++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index f886f1f8ec..c45e225d8f 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -190,9 +190,6 @@ typedef struct { uint8_t *local_tags; int local_tags_count; uint64_t footer_partition; - int64_t essence_offset; - int first_essence_kl_length; - int64_t first_essence_length; KLVPacket current_klv_data; int current_klv_index; int run_in; @@ -1023,6 +1020,34 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment return 0; } +/** + * Computes the absolute file offset of the given essence container offset + */ +static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t offset, int64_t *offset_out) +{ + int x; + int64_t offset_in = offset; /* for logging */ + + for (x = 0; x < mxf->partitions_count; x++) { + MXFPartition *p = &mxf->partitions[x]; + + if (p->body_sid != body_sid) + continue; + + if (offset < p->essence_length || !p->essence_length) { + *offset_out = p->essence_offset + offset; + return 0; + } + + offset -= p->essence_length; + } + + av_log(mxf->fc, AV_LOG_ERROR, "failed to find absolute offset of %lx in BodySID %i - partial file?\n", + offset_in, body_sid); + + return AVERROR_INVALIDDATA; +} + static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st) { int64_t accumulated_offset = 0; @@ -1109,10 +1134,10 @@ static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st) flags = AVINDEX_KEYFRAME; } - if (k > 0 && pos < mxf->first_essence_length && accumulated_offset == 0) - pos += mxf->first_essence_kl_length; - - pos += mxf->essence_offset; + if (mxf_absolute_bodysid_offset(mxf, tableseg->body_sid, pos, &pos) < 0) { + /* probably partial file - no point going further for this stream */ + break; + } av_dlog(mxf->fc, "Stream %d IndexEntry %d TrackID %d Offset %"PRIx64" Timestamp %"PRId64"\n", st->index, st->nb_index_entries, track_id, pos, sample_duration * st->nb_index_entries); @@ -1546,6 +1571,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) { MXFContext *mxf = s->priv_data; KLVPacket klv; + int64_t essence_offset = 0; mxf->last_forward_tell = INT64_MAX; @@ -1577,13 +1603,8 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) compute_partition_essence_offset(s, mxf, &klv); } - if (!mxf->essence_offset) - mxf->essence_offset = klv.offset; - - if (!mxf->first_essence_kl_length && IS_KLV_KEY(klv.key, mxf_essence_element_key)) { - mxf->first_essence_kl_length = avio_tell(s->pb) - klv.offset; - mxf->first_essence_length = klv.length; - } + if (!essence_offset) + essence_offset = klv.offset; /* seek to footer, previous partition or stop */ if (mxf_parse_handle_essence(mxf) <= 0) @@ -1617,11 +1638,11 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) avio_skip(s->pb, klv.length); } /* FIXME avoid seek */ - if (!mxf->essence_offset) { + if (!essence_offset) { av_log(s, AV_LOG_ERROR, "no essence\n"); return AVERROR_INVALIDDATA; } - avio_seek(s->pb, mxf->essence_offset, SEEK_SET); + avio_seek(s->pb, essence_offset, SEEK_SET); mxf_compute_essence_containers(mxf); From 44deb9f68de2ba3fef0d39eeab0531cc842e1624 Mon Sep 17 00:00:00 2001 From: Carl Eugen Hoyos Date: Sat, 10 Dec 2011 14:58:57 +0100 Subject: [PATCH 19/25] mfxdec: Separate mxf_essence_container_uls for audio and video. It is a really bad idea to assign a video codec id when we have set codec_type to audio and vice versa. Prevents detection of mp2 in mxf as mpeg2video. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index c45e225d8f..9de89abe2c 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -949,10 +949,13 @@ static void *mxf_resolve_strong_ref(MXFContext *mxf, UID *strong_ref, enum MXFMe return NULL; } -static const MXFCodecUL mxf_essence_container_uls[] = { +static const MXFCodecUL mxf_picture_essence_container_uls[] = { // video essence container uls { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, CODEC_ID_MPEG2VIDEO }, /* MPEG-ES Frame wrapped */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, CODEC_ID_DVVIDEO }, /* DV 625 25mbps */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE }, +}; +static const MXFCodecUL mxf_sound_essence_container_uls[] = { // sound essence container uls { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, CODEC_ID_MP2 }, /* MPEG-ES Frame wrapped, 0x40 ??? stream id */ @@ -1304,7 +1307,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codec->extradata_size = descriptor->extradata_size; } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - container_ul = mxf_get_codec_ul(mxf_essence_container_uls, essence_container_ul); + container_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul); if (st->codec->codec_id == CODEC_ID_NONE) st->codec->codec_id = container_ul->id; st->codec->width = descriptor->width; @@ -1313,7 +1316,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codec->pix_fmt = descriptor->pix_fmt; st->need_parsing = AVSTREAM_PARSE_HEADERS; } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - container_ul = mxf_get_codec_ul(mxf_essence_container_uls, essence_container_ul); + container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul); if (st->codec->codec_id == CODEC_ID_NONE) st->codec->codec_id = container_ul->id; st->codec->channels = descriptor->channels; From 1f5f8b26545c2c62c1600729963af282294c38ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Sat, 10 Dec 2011 12:11:11 +0100 Subject: [PATCH 20/25] mxfdec: Add Avid's essence element key. Signed-off-by: Janne Grunau --- libavformat/mxfdec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 9de89abe2c..37f22a6dd7 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -217,6 +217,7 @@ typedef struct { /* partial keys to match */ static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 }; static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 }; +static const uint8_t mxf_avid_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0e,0x04,0x03,0x01 }; static const uint8_t mxf_system_item_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04 }; static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 }; /* complete keys to match */ @@ -390,7 +391,8 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) } return 0; } - if (IS_KLV_KEY(klv.key, mxf_essence_element_key)) { + if (IS_KLV_KEY(klv.key, mxf_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) { int index = mxf_get_stream_index(s, &klv); if (index < 0) { av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12)); @@ -1601,6 +1603,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) || IS_KLV_KEY(klv.key, mxf_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_avid_essence_element_key) || IS_KLV_KEY(klv.key, mxf_system_item_key)) { if (!mxf->current_partition->essence_offset) { compute_partition_essence_offset(s, mxf, &klv); From f028d4d1c393a13c66e828d45ba8412c0b4df6da Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 8 Dec 2011 20:28:49 +0100 Subject: [PATCH 21/25] mxfdec: hybrid demuxing/seeking solution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses the old demuxing code for OP1a and separate demuxing code for OPAtom. Timestamp output is added to the old demuxing code. The seeking code is made to seek to the start of the desired EditUnit only, from which the normal demuxing code takes over (if OP1a). This means we do not use delta entries or slices, only StreamOffsets. OPAtom seeking basically works like before. This also makes D-10 seeking behave the same way as OP1a and OPAtom. In other words, we allow seeking before the start or past the end for D-10 too. Based on several patches by Tomas Härdin and Reimar Döffinger . Changed av_calloc to av_mallocz, added overflow checks. --- libavformat/mxfdec.c | 595 +++++++++++++++++++++++++++--------- tests/ref/seek/lavf_mxf | 59 ++-- tests/ref/seek/lavf_mxf_d10 | 36 +-- 3 files changed, 498 insertions(+), 192 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 37f22a6dd7..f192d80eae 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -177,6 +177,18 @@ typedef struct { enum MXFMetadataSetType type; } MXFMetadataSet; +/* decoded index table */ +typedef struct { + int index_sid; + int body_sid; + int nb_ptses; /* number of PTSes or total duration of index */ + int64_t first_dts; /* DTS = EditUnit + first_dts */ + int64_t *ptses; /* maps EditUnit -> PTS */ + int nb_segments; + MXFIndexTableSegment **segments; /* sorted by IndexStartPosition */ + AVIndexEntry *fake_index; /* used for calling ff_index_search_timestamp() */ +} MXFIndexTable; + typedef struct { MXFPartition *partitions; unsigned partitions_count; @@ -197,6 +209,9 @@ typedef struct { int parsing_backward; int64_t last_forward_tell; int last_forward_partition; + int current_edit_unit; + int nb_index_tables; + MXFIndexTable *index_tables; } MXFContext; enum MXFWrappingScheme { @@ -373,54 +388,6 @@ static int mxf_decrypt_triplet(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv return 0; } -static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - KLVPacket klv; - - while (!s->pb->eof_reached) { - int ret; - if (klv_read_packet(&klv, s->pb) < 0) - return -1; - PRINT_KEY(s, "read packet", klv.key); - av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); - if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) { - ret = mxf_decrypt_triplet(s, pkt, &klv); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n"); - return AVERROR_INVALIDDATA; - } - return 0; - } - if (IS_KLV_KEY(klv.key, mxf_essence_element_key) || - IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) { - int index = mxf_get_stream_index(s, &klv); - if (index < 0) { - av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12)); - goto skip; - } - if (s->streams[index]->discard == AVDISCARD_ALL) - goto skip; - /* check for 8 channels AES3 element */ - if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) { - if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) { - av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n"); - return AVERROR_INVALIDDATA; - } - } else { - ret = av_get_packet(s->pb, pkt, klv.length); - if (ret < 0) - return ret; - } - pkt->stream_index = index; - pkt->pos = klv.offset; - return 0; - } else - skip: - avio_skip(s->pb, klv.length); - } - return AVERROR_EOF; -} - static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; @@ -1053,109 +1020,266 @@ static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t of return AVERROR_INVALIDDATA; } -static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st) +/** + * Returns the end position of the essence container with given BodySID, or zero if unknown + */ +static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid) { - int64_t accumulated_offset = 0; - int j, k, ret, nb_sorted_segments; - MXFIndexTableSegment **sorted_segments; - int n_delta = track_id - 1; /* TrackID = 1-based stream index */ + int x; + int64_t ret = 0; - if (track_id < 1) { - av_log(mxf->fc, AV_LOG_ERROR, "TrackID not positive: %i\n", track_id); - return AVERROR_INVALIDDATA; + for (x = 0; x < mxf->partitions_count; x++) { + MXFPartition *p = &mxf->partitions[x]; + + if (p->body_sid != body_sid) + continue; + + if (!p->essence_length) + return 0; + + ret = p->essence_offset + p->essence_length; } - if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments))) - return ret; + return ret; +} - for (j = 0; j < nb_sorted_segments; j++) { - int duration, sample_duration = 1, last_sample_size = 0; - int64_t segment_size; - MXFIndexTableSegment *tableseg = sorted_segments[j]; +/* EditUnit -> absolute offset */ +static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, int64_t *edit_unit_out, int64_t *offset_out, int nag) +{ + int i; + int offset_temp = 0; - /* reset accumulated_offset on BodySID change */ - if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid) - accumulated_offset = 0; + for (i = 0; i < index_table->nb_segments; i++) { + MXFIndexTableSegment *s = index_table->segments[i]; - if (n_delta >= tableseg->nb_delta_entries && st->index != 0) - continue; - duration = tableseg->index_duration > 0 ? tableseg->index_duration : - st->duration - st->nb_index_entries; - segment_size = tableseg->edit_unit_byte_count * duration; - /* check small EditUnitByteCount for audio */ - if (tableseg->edit_unit_byte_count && tableseg->edit_unit_byte_count < 32 - && !tableseg->index_duration) { - /* duration might be prime relative to the new sample_duration, - * which means we need to handle the last frame differently */ - sample_duration = 8192; - last_sample_size = (duration % sample_duration) * tableseg->edit_unit_byte_count; - tableseg->edit_unit_byte_count *= sample_duration; - duration /= sample_duration; - if (last_sample_size) duration++; - } + edit_unit = FFMAX(edit_unit, s->index_start_position); /* clamp if trying to seek before start */ - for (k = 0; k < duration; k++) { - int64_t pos; - int size, flags = 0; + if (edit_unit < s->index_start_position + s->index_duration) { + int64_t index = edit_unit - s->index_start_position; - if (k < tableseg->nb_index_entries) { - pos = tableseg->stream_offset_entries[k]; - if (n_delta < tableseg->nb_delta_entries) { - if (n_delta < tableseg->nb_delta_entries - 1) { - size = - tableseg->slice_offset_entries[k][tableseg->slice[n_delta+1]-1] + - tableseg->element_delta[n_delta+1] - - tableseg->element_delta[n_delta]; - if (tableseg->slice[n_delta] > 0) - size -= tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1]; - } else if (k < duration - 1) { - size = tableseg->stream_offset_entries[k+1] - - tableseg->stream_offset_entries[k] - - tableseg->slice_offset_entries[k][tableseg->slice[tableseg->nb_delta_entries-1]-1] - - tableseg->element_delta[tableseg->nb_delta_entries-1]; - } else - size = 0; - if (tableseg->slice[n_delta] > 0) - pos += tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1]; - pos += tableseg->element_delta[n_delta]; - } else - size = 0; - flags = !(tableseg->flag_entries[k] & 0x30) ? AVINDEX_KEYFRAME : 0; - } else { - pos = (int64_t)k * tableseg->edit_unit_byte_count + accumulated_offset; - if (n_delta < tableseg->nb_delta_entries - 1) - size = tableseg->element_delta[n_delta+1] - tableseg->element_delta[n_delta]; - else { - /* use smaller size for last sample if we should */ - if (last_sample_size && k == duration - 1) - size = last_sample_size; - else - size = tableseg->edit_unit_byte_count; - if (tableseg->nb_delta_entries) - size -= tableseg->element_delta[tableseg->nb_delta_entries-1]; + if (s->edit_unit_byte_count) + offset_temp += s->edit_unit_byte_count * index; + else if (s->nb_index_entries) { + if (s->nb_index_entries == 2 * s->index_duration + 1) + index *= 2; /* Avid index */ + + if (index < 0 || index > s->nb_index_entries) { + av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" IndexEntryArray too small\n", + index_table->index_sid, s->index_start_position); + return AVERROR_INVALIDDATA; } - if (n_delta < tableseg->nb_delta_entries) - pos += tableseg->element_delta[n_delta]; - flags = AVINDEX_KEYFRAME; + + offset_temp = s->stream_offset_entries[index]; + } else { + av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n", + index_table->index_sid, s->index_start_position); + return AVERROR_INVALIDDATA; } - if (mxf_absolute_bodysid_offset(mxf, tableseg->body_sid, pos, &pos) < 0) { - /* probably partial file - no point going further for this stream */ + if (edit_unit_out) + *edit_unit_out = edit_unit; + + return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out); + } else { + /* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */ + offset_temp += s->edit_unit_byte_count * s->index_duration; + } + } + + if (nag) + av_log(mxf->fc, AV_LOG_ERROR, "failed to map EditUnit %"PRId64" in IndexSID %i to an offset\n", edit_unit, index_table->index_sid); + + return AVERROR_INVALIDDATA; +} + +static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_table) +{ + int i, j, x; + int8_t max_temporal_offset = -128; + + /* first compute how many entries we have */ + for (i = 0; i < index_table->nb_segments; i++) { + MXFIndexTableSegment *s = index_table->segments[i]; + + if (!s->nb_index_entries) + return 0; /* no TemporalOffsets */ + + index_table->nb_ptses += s->index_duration; + } + + /* paranoid check */ + if (index_table->nb_ptses <= 0) + return 0; + + if (index_table->nb_ptses > INT_MAX >> av_log2(sizeof(AVIndexEntry)) + 1) + return AVERROR(ENOMEM); + + index_table->ptses = av_mallocz(index_table->nb_ptses * + sizeof(int64_t)); + index_table->fake_index = av_mallocz(index_table->nb_ptses * + sizeof(AVIndexEntry)); + if (!index_table->ptses || !index_table->fake_index) { + av_freep(&index_table->ptses); + return AVERROR(ENOMEM); + } + + /* we may have a few bad TemporalOffsets + * make sure the corresponding PTSes don't have the bogus value 0 */ + for (x = 0; x < index_table->nb_ptses; x++) + index_table->ptses[x] = AV_NOPTS_VALUE; + + /** + * We have this: + * + * x TemporalOffset + * 0: 0 + * 1: 1 + * 2: 1 + * 3: -2 + * 4: 1 + * 5: 1 + * 6: -2 + * + * We want to transform it into this: + * + * x DTS PTS + * 0: -1 0 + * 1: 0 3 + * 2: 1 1 + * 3: 2 2 + * 4: 3 6 + * 5: 4 4 + * 6: 5 5 + * + * We do this by bucket sorting x by x+TemporalOffset[x] into mxf->ptses, + * then settings mxf->first_dts = -max(TemporalOffset[x]). + * The latter makes DTS <= PTS. + */ + for (i = x = 0; i < index_table->nb_segments; i++) { + MXFIndexTableSegment *s = index_table->segments[i]; + int index_delta = 1; + + if (s->nb_index_entries == 2 * s->index_duration + 1) + index_delta = 2; /* Avid index */ + + for (j = 0; j < s->nb_index_entries; j += index_delta, x++) { + int offset = s->temporal_offset_entries[j] / index_delta; + int index = x + offset; + + index_table->fake_index[x].timestamp = x; + index_table->fake_index[x].flags = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0; + + if (index < 0 || index >= index_table->nb_ptses) { + av_log(mxf->fc, AV_LOG_ERROR, + "index entry %i + TemporalOffset %i = %i, which is out of bounds\n", + x, offset, index); + continue; + } + + index_table->ptses[index] = x; + max_temporal_offset = FFMAX(max_temporal_offset, offset); + } + } + + index_table->first_dts = -max_temporal_offset; + + return 0; +} + +/** + * Sorts and collects index table segments into index tables. + * Also computes PTSes if possible. + */ +static int mxf_compute_index_tables(MXFContext *mxf) +{ + int i, j, k, ret, nb_sorted_segments; + MXFIndexTableSegment **sorted_segments = NULL; + + if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) || + nb_sorted_segments <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, "broken or empty index\n"); + return 0; + } + + /* sanity check and count unique BodySIDs/IndexSIDs */ + for (i = 0; i < nb_sorted_segments; i++) { + if (i == 0 || sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid) + mxf->nb_index_tables++; + else if (sorted_segments[i-1]->body_sid != sorted_segments[i]->body_sid) { + av_log(mxf->fc, AV_LOG_ERROR, "found inconsistent BodySID\n"); + ret = AVERROR_INVALIDDATA; + goto finish_decoding_index; + } + } + + if (mxf->nb_index_tables > INT_MAX >> av_log2(sizeof(MXFIndexTable)) + 1 || + !(mxf->index_tables = av_mallocz(mxf->nb_index_tables * + sizeof(MXFIndexTable)))) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n"); + ret = AVERROR(ENOMEM); + goto finish_decoding_index; + } + + /* distribute sorted segments to index tables */ + for (i = j = 0; i < nb_sorted_segments; i++) { + if (i != 0 && sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid) { + /* next IndexSID */ + j++; + } + + mxf->index_tables[j].nb_segments++; + } + + for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) { + MXFIndexTable *t = &mxf->index_tables[j]; + + if (t->nb_segments > + (INT_MAX >> av_log2(sizeof(MXFIndexTableSegment *))) || + !(t->segments = av_mallocz(t->nb_segments * + sizeof(MXFIndexTableSegment*)))) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment" + " pointer array\n"); + ret = AVERROR(ENOMEM); + goto finish_decoding_index; + } + + if (sorted_segments[i]->index_start_position) + av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i starts at EditUnit %"PRId64" - seeking may not work as expected\n", + sorted_segments[i]->index_sid, sorted_segments[i]->index_start_position); + + memcpy(t->segments, &sorted_segments[i], t->nb_segments * sizeof(MXFIndexTableSegment*)); + t->index_sid = sorted_segments[i]->index_sid; + t->body_sid = sorted_segments[i]->body_sid; + + if ((ret = mxf_compute_ptses_fake_index(mxf, t)) < 0) + goto finish_decoding_index; + + /* fix zero IndexDurations */ + for (k = 0; k < t->nb_segments; k++) { + if (t->segments[k]->index_duration) + continue; + + if (t->nb_segments > 1) + av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has zero IndexDuration and there's more than one segment\n", + t->index_sid, k); + + if (mxf->fc->nb_streams <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, "no streams?\n"); break; } - av_dlog(mxf->fc, "Stream %d IndexEntry %d TrackID %d Offset %"PRIx64" Timestamp %"PRId64"\n", - st->index, st->nb_index_entries, track_id, pos, sample_duration * st->nb_index_entries); - - if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0) - return ret; + /* assume the first stream's duration is reasonable + * leave index_duration = 0 on further segments in case we have any (unlikely) + */ + t->segments[k]->index_duration = mxf->fc->streams[0]->duration; + break; } - accumulated_offset += segment_size; } + ret = 0; +finish_decoding_index: av_free(sorted_segments); - - return 0; + return ret; } static int mxf_parse_structural_metadata(MXFContext *mxf) @@ -1220,7 +1344,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) for (k = 0; k < source_package->tracks_count; k++) { if (!(temp_track = mxf_resolve_strong_ref(mxf, &source_package->tracks_refs[k], Track))) { av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail_and_free; } if (temp_track->track_id == component->source_track_id) { source_track = temp_track; @@ -1237,7 +1362,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) { av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail_and_free; } /* 0001GL00.MXF.A1.mxf_opatom.mxf has the same SourcePackageID as 0001GL.MXF.V1.mxf_opatom.mxf @@ -1250,7 +1376,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st = avformat_new_stream(mxf->fc, NULL); if (!st) { av_log(mxf->fc, AV_LOG_ERROR, "could not allocate stream\n"); - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail_and_free; } st->id = source_track->track_id; st->priv_data = source_track; @@ -1301,6 +1428,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } } } + /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */ codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul); st->codec->codec_id = codec_ul->id; @@ -1340,14 +1468,14 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } } if (st->codec->codec_type != AVMEDIA_TYPE_DATA && (*essence_container_ul)[15] > 0x01) { - av_log(mxf->fc, AV_LOG_WARNING, "only frame wrapped mappings are correctly supported\n"); - st->need_parsing = AVSTREAM_PARSE_FULL; + /* TODO: decode timestamps */ + st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS; } - - if ((ret = mxf_parse_index(mxf, material_track->track_id, st))) - return ret; } - return 0; + + ret = 0; +fail_and_free: + return ret; } static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { @@ -1577,6 +1705,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) MXFContext *mxf = s->priv_data; KLVPacket klv; int64_t essence_offset = 0; + int ret; mxf->last_forward_tell = INT64_MAX; @@ -1652,9 +1781,156 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) mxf_compute_essence_containers(mxf); - return mxf_parse_structural_metadata(mxf); + /* we need to do this before computing the index tables + * to be able to fill in zero IndexDurations with st->duration */ + if ((ret = mxf_parse_structural_metadata(mxf)) < 0) + return ret; + + if ((ret = mxf_compute_index_tables(mxf)) < 0) + return ret; + + if (mxf->nb_index_tables > 1) { + /* TODO: look up which IndexSID to use via EssenceContainerData */ + av_log(mxf->fc, AV_LOG_INFO, "got %i index tables - only the first one (IndexSID %i) will be used\n", + mxf->nb_index_tables, mxf->index_tables[0].index_sid); + } else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom) { + av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n"); + return AVERROR_INVALIDDATA; + } + + return 0; } +/** + * Computes DTS and PTS for the given video packet based on its offset. + */ +static void mxf_packet_timestamps(MXFContext *mxf, AVPacket *pkt) +{ + int64_t next_ofs; + MXFIndexTable *t = &mxf->index_tables[0]; + + /* find mxf->current_edit_unit so that the next edit unit starts ahead of pkt->pos */ + for (;;) { + if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0) + break; + + if (next_ofs > pkt->pos) + break; + + mxf->current_edit_unit++; + } + + if (mxf->current_edit_unit >= t->nb_ptses) + return; + + pkt->dts = mxf->current_edit_unit + t->first_dts; + pkt->pts = t->ptses[mxf->current_edit_unit]; +} + +static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) +{ + KLVPacket klv; + + while (!s->pb->eof_reached) { + if (klv_read_packet(&klv, s->pb) < 0) + return -1; + PRINT_KEY(s, "read packet", klv.key); + av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); + if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) { + int res = mxf_decrypt_triplet(s, pkt, &klv); + if (res < 0) { + av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n"); + return -1; + } + return 0; + } + if (IS_KLV_KEY(klv.key, mxf_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) { + int index = mxf_get_stream_index(s, &klv); + if (index < 0) { + av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12)); + goto skip; + } + if (s->streams[index]->discard == AVDISCARD_ALL) + goto skip; + /* check for 8 channels AES3 element */ + if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) { + if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) { + av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n"); + return -1; + } + } else { + int ret = av_get_packet(s->pb, pkt, klv.length); + if (ret < 0) + return ret; + } + pkt->stream_index = index; + pkt->pos = klv.offset; + + if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO) + mxf_packet_timestamps(s->priv_data, pkt); /* offset -> EditUnit -> DTS/PTS */ + + return 0; + } else + skip: + avio_skip(s->pb, klv.length); + } + return AVERROR_EOF; +} + +static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + int ret, size; + int64_t ret64, pos, next_pos; + AVStream *st; + MXFIndexTable *t; + + if (mxf->op != OPAtom) + return mxf_read_packet_old(s, pkt); + + /* OPAtom - clip wrapped demuxing */ + st = s->streams[0]; + t = &mxf->index_tables[0]; + + if (mxf->current_edit_unit >= st->duration) + return AVERROR_EOF; + + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit, NULL, &pos, 1)) < 0) + return ret; + + /* compute size by finding the next edit unit or the end of the essence container + * not pretty, but it works */ + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_pos, 0)) < 0 && + (next_pos = mxf_essence_container_end(mxf, t->body_sid)) <= 0) { + av_log(s, AV_LOG_ERROR, "unable to compute the size of the last packet\n"); + return AVERROR_INVALIDDATA; + } + + if ((size = next_pos - pos) <= 0) { + av_log(s, AV_LOG_ERROR, "bad size: %i\n", size); + return AVERROR_INVALIDDATA; + } + + if ((ret64 = avio_seek(s->pb, pos, SEEK_SET)) < 0) + return ret64; + + if ((ret = av_get_packet(s->pb, pkt, size)) != size) + return ret < 0 ? ret : AVERROR_EOF; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses && + mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) { + pkt->dts = mxf->current_edit_unit + t->first_dts; + pkt->pts = t->ptses[mxf->current_edit_unit]; + } + + pkt->stream_index = 0; + mxf->current_edit_unit++; + + return 0; +} + + static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -1699,6 +1975,13 @@ static int mxf_read_close(AVFormatContext *s) av_freep(&mxf->metadata_sets); av_freep(&mxf->aesc); av_freep(&mxf->local_tags); + + for (i = 0; i < mxf->nb_index_tables; i++) { + av_freep(&mxf->index_tables[i].segments); + av_freep(&mxf->index_tables[i].fake_index); + } + av_freep(&mxf->index_tables); + return 0; } @@ -1724,8 +2007,12 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti { AVStream *st = s->streams[stream_index]; int64_t seconds; + MXFContext* mxf = s->priv_data; + int64_t seekpos; int ret; + MXFIndexTable *t; + if (mxf->index_tables <= 0) { if (!s->bit_rate) return AVERROR_INVALIDDATA; if (sample_time < 0) @@ -1735,6 +2022,30 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti if ((ret = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0) return ret; ff_update_cur_dts(s, st, sample_time); + } else { + t = &mxf->index_tables[0]; + + /* clamp above zero, else ff_index_search_timestamp() returns negative + * this also means we allow seeking before the start */ + sample_time = FFMAX(sample_time, 0); + + if (t->fake_index) { + /* behave as if we have a proper index */ + if ((sample_time = ff_index_search_timestamp(t->fake_index, t->nb_ptses, sample_time, flags)) < 0) + return sample_time; + } else { + /* no IndexEntryArray (one or more CBR segments) + * make sure we don't seek past the end */ + sample_time = FFMIN(sample_time, st->duration - 1); + } + + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0) + return ret; + + ff_update_cur_dts(s, st, sample_time); + mxf->current_edit_unit = sample_time; + avio_seek(s->pb, seekpos, SEEK_SET); + } return 0; } diff --git a/tests/ref/seek/lavf_mxf b/tests/ref/seek/lavf_mxf index 4c1aecc68e..cc634a8af2 100644 --- a/tests/ref/seek/lavf_mxf +++ b/tests/ref/seek/lavf_mxf @@ -1,53 +1,48 @@ -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 ret: 0 st:-1 flags:0 ts:-1.000000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 ret: 0 st:-1 flags:1 ts: 1.894167 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st: 0 flags:0 ts: 0.800000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st: 0 flags:1 ts:-0.320000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 -ret: 0 st: 1 flags:0 ts: 2.560000 -ret:-1 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 +ret:-1 st: 1 flags:0 ts: 2.560000 ret: 0 st: 1 flags:1 ts: 1.480000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st:-1 flags:0 ts: 0.365002 -ret: 0 st: 0 flags:1 dts: 0.360000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.480000 pos: 211968 size: 24787 ret: 0 st:-1 flags:1 ts:-0.740831 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 -ret: 0 st: 0 flags:0 ts: 2.160000 -ret:-1 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 +ret:-1 st: 0 flags:0 ts: 2.160000 ret: 0 st: 0 flags:1 ts: 1.040000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st: 1 flags:0 ts:-0.040000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 ret: 0 st: 1 flags:1 ts: 2.840000 -ret:-1 -ret: 0 st:-1 flags:0 ts: 1.730004 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 +ret:-1 st:-1 flags:0 ts: 1.730004 ret: 0 st:-1 flags:1 ts: 0.624171 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.480000 pos: 211968 size: 24787 ret: 0 st: 0 flags:0 ts:-0.480000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 ret: 0 st: 0 flags:1 ts: 2.400000 -ret:-1 -ret: 0 st: 1 flags:0 ts: 1.320000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 +ret:-1 st: 1 flags:0 ts: 1.320000 ret: 0 st: 1 flags:1 ts: 0.200000 -ret: 0 st: 0 flags:1 dts: 0.200000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 ret: 0 st:-1 flags:0 ts:-0.904994 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 ret: 0 st:-1 flags:1 ts: 1.989173 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st: 0 flags:0 ts: 0.880000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st: 0 flags:1 ts:-0.240000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 -ret: 0 st: 1 flags:0 ts: 2.680000 -ret:-1 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 +ret:-1 st: 1 flags:0 ts: 2.680000 ret: 0 st: 1 flags:1 ts: 1.560000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712 ret: 0 st:-1 flags:1 ts:-0.645825 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: NOPTS pos: 6144 size: 24801 +ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 6144 size: 24801 diff --git a/tests/ref/seek/lavf_mxf_d10 b/tests/ref/seek/lavf_mxf_d10 index c05870f402..4cfe595415 100644 --- a/tests/ref/seek/lavf_mxf_d10 +++ b/tests/ref/seek/lavf_mxf_d10 @@ -2,52 +2,52 @@ ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st:-1 flags:0 ts:-1.000000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st:-1 flags:1 ts: 1.894167 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 0 flags:0 ts: 0.800000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.800000 pts: 0.800000 pos:4265984 size:150000 ret: 0 st: 0 flags:1 ts:-0.320000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st: 1 flags:0 ts: 2.560000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 1 flags:1 ts: 1.480000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st:-1 flags:0 ts: 0.365002 -ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos: 6144 size:150000 +ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos:1923072 size:150000 ret: 0 st:-1 flags:1 ts:-0.740831 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st: 0 flags:0 ts: 2.160000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 0 flags:1 ts: 1.040000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 1 flags:0 ts:-0.040000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st: 1 flags:1 ts: 2.840000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st:-1 flags:0 ts: 1.730004 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st:-1 flags:1 ts: 0.624171 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.640000 pts: 0.640000 pos:3414016 size:150000 ret: 0 st: 0 flags:0 ts:-0.480000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st: 0 flags:1 ts: 2.400000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 1 flags:0 ts: 1.320000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 1 flags:1 ts: 0.200000 -ret: 0 st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos: 6144 size:150000 +ret: 0 st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos:1071104 size:150000 ret: 0 st:-1 flags:0 ts:-0.904994 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st:-1 flags:1 ts: 1.989173 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 0 flags:0 ts: 0.880000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.880000 pts: 0.880000 pos:4691968 size:150000 ret: 0 st: 0 flags:1 ts:-0.240000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 ret: 0 st: 1 flags:0 ts: 2.680000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st: 1 flags:1 ts: 1.560000 -ret:-1 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000 ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 6144 size:150000 +ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos:2562048 size:150000 ret: 0 st:-1 flags:1 ts:-0.645825 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 From fc85729458e994076cac661bd2dcecdd243b673f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Fri, 16 Dec 2011 11:02:04 +0100 Subject: [PATCH 22/25] mxfdec: Do not parse slices or DeltaEntryArrays. The most recent demuxing/seeking code does not need them. --- libavformat/mxfdec.c | 86 ++++++-------------------------------------- 1 file changed, 10 insertions(+), 76 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index f192d80eae..a81eec0519 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -148,17 +148,12 @@ typedef struct { int edit_unit_byte_count; int index_sid; int body_sid; - int slice_count; AVRational index_edit_rate; uint64_t index_start_position; uint64_t index_duration; - int *slice; - int *element_delta; - int nb_delta_entries; int8_t *temporal_offset_entries; int *flag_entries; uint64_t *stream_offset_entries; - uint32_t **slice_offset_entries; int nb_index_entries; } MXFIndexTableSegment; @@ -666,39 +661,9 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size return 0; } -static int mxf_read_delta_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) -{ - int i, length; - - segment->nb_delta_entries = avio_rb32(pb); - if (segment->nb_delta_entries < 1 || - segment->nb_delta_entries > INT_MAX >> av_log2(sizeof(*segment->slice))) - return AVERROR(ENOMEM); - - length = avio_rb32(pb); - - segment->slice = av_mallocz(segment->nb_delta_entries * - sizeof(*segment->slice)); - if (!segment->slice) - return AVERROR(ENOMEM); - segment->element_delta = av_mallocz(segment->nb_delta_entries * - sizeof(*segment->element_delta)); - if (!segment->element_delta) { - av_freep(&segment->slice); - return AVERROR(ENOMEM); - } - - for (i = 0; i < segment->nb_delta_entries; i++) { - avio_r8(pb); /* PosTableIndex */ - segment->slice[i] = avio_r8(pb); - segment->element_delta[i] = avio_rb32(pb); - } - return 0; -} - static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) { - int i, j, length; + int i, length; segment->nb_index_entries = avio_rb32(pb); if (!segment->nb_index_entries) @@ -716,41 +681,23 @@ static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *seg sizeof(*segment->flag_entries)); segment->stream_offset_entries = av_mallocz(segment->nb_index_entries * sizeof(*segment->stream_offset_entries)); - segment->slice_offset_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->slice_offset_entries)); - if (!segment->flag_entries || !segment->stream_offset_entries || - !segment->slice_offset_entries || !segment->temporal_offset_entries) - goto errmem; + if (!segment->flag_entries || !segment->stream_offset_entries || + !segment->temporal_offset_entries) { + av_freep(&segment->flag_entries); + av_freep(&segment->stream_offset_entries); + av_freep(&segment->temporal_offset_entries); + return AVERROR(ENOMEM); + } for (i = 0; i < segment->nb_index_entries; i++) { segment->temporal_offset_entries[i] = avio_r8(pb); avio_r8(pb); /* KeyFrameOffset */ segment->flag_entries[i] = avio_r8(pb); segment->stream_offset_entries[i] = avio_rb64(pb); - if (segment->slice_count) { - segment->slice_offset_entries[i] = av_mallocz(segment->slice_count * - sizeof(**segment->slice_offset_entries)); - if (!segment->slice_offset_entries[i]) - goto errmem; - - for (j = 0; j < segment->slice_count; j++) - segment->slice_offset_entries[i][j] = avio_rb32(pb); - } - - avio_skip(pb, length - 11 - 4 * segment->slice_count); + avio_skip(pb, length - 11); } return 0; -errmem: - if (segment->slice_offset_entries && segment->slice_count) { - for (i = 0; i < segment->nb_index_entries; i++) - av_free(segment->slice_offset_entries[i]); - } - av_freep(&segment->flag_entries); - av_freep(&segment->stream_offset_entries); - av_freep(&segment->slice_offset_entries); - av_freep(&segment->temporal_offset_entries); - return AVERROR(ENOMEM); } static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) @@ -769,13 +716,6 @@ static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int segment->body_sid = avio_rb32(pb); av_dlog(NULL, "BodySID %d\n", segment->body_sid); break; - case 0x3F08: - segment->slice_count = avio_r8(pb); - av_dlog(NULL, "SliceCount %d\n", segment->slice_count); - break; - case 0x3F09: - av_dlog(NULL, "DeltaEntryArray found\n"); - return mxf_read_delta_entry_array(pb, segment); case 0x3F0A: av_dlog(NULL, "IndexEntryArray found\n"); return mxf_read_index_entry_array(pb, segment); @@ -1935,7 +1875,7 @@ static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; MXFIndexTableSegment *seg; - int i, j; + int i; av_freep(&mxf->packages_refs); @@ -1956,15 +1896,9 @@ static int mxf_read_close(AVFormatContext *s) break; case IndexTableSegment: seg = (MXFIndexTableSegment *)mxf->metadata_sets[i]; - if (seg->slice_count) - for (j = 0; j < seg->nb_index_entries; j++) - av_freep(&seg->slice_offset_entries[j]); - av_freep(&seg->slice); - av_freep(&seg->element_delta); av_freep(&seg->temporal_offset_entries); av_freep(&seg->flag_entries); av_freep(&seg->stream_offset_entries); - av_freep(&seg->slice_offset_entries); break; default: break; From 20ea14d0f812d337950cbbe9600d47b07352ba5d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 21 Dec 2011 20:12:24 +0100 Subject: [PATCH 23/25] mxfdec: fix memleak on av_realloc failures --- libavformat/mxfdec.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index a81eec0519..4b63b277ff 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -407,16 +407,17 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; - MXFPartition *partition; + MXFPartition *partition, *tmp_part; UID op; uint64_t footer_partition; if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions)) return AVERROR(ENOMEM); - mxf->partitions = av_realloc(mxf->partitions, (mxf->partitions_count + 1) * sizeof(*mxf->partitions)); - if (!mxf->partitions) + tmp_part = av_realloc(mxf->partitions, (mxf->partitions_count + 1) * sizeof(*mxf->partitions)); + if (!tmp_part) return AVERROR(ENOMEM); + mxf->partitions = tmp_part; if (mxf->parsing_backward) { /* insert the new partition pack in the middle @@ -512,11 +513,13 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set) { + MXFMetadataSet **tmp; if (mxf->metadata_sets_count+1 >= UINT_MAX / sizeof(*mxf->metadata_sets)) return AVERROR(ENOMEM); - mxf->metadata_sets = av_realloc(mxf->metadata_sets, (mxf->metadata_sets_count + 1) * sizeof(*mxf->metadata_sets)); - if (!mxf->metadata_sets) + tmp = av_realloc(mxf->metadata_sets, (mxf->metadata_sets_count + 1) * sizeof(*mxf->metadata_sets)); + if (!tmp) return AVERROR(ENOMEM); + mxf->metadata_sets = tmp; mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set; mxf->metadata_sets_count++; return 0; From 66b9d7065a5c01bcf6b5d5325fd9b9d4132e7e6f Mon Sep 17 00:00:00 2001 From: Carl Eugen Hoyos Date: Wed, 28 Dec 2011 05:54:59 +0100 Subject: [PATCH 24/25] pam: Fix a typo that broke writing and reading PAM files. Reported and reviewed by Derek Buitenhuis. Signed-off-by: Janne Grunau --- libavcodec/pamenc.c | 2 +- libavcodec/pnm.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libavcodec/pamenc.c b/libavcodec/pamenc.c index 10960d5e9c..e070ed4b3b 100644 --- a/libavcodec/pamenc.c +++ b/libavcodec/pamenc.c @@ -78,7 +78,7 @@ static int pam_encode_frame(AVCodecContext *avctx, unsigned char *outbuf, return -1; } snprintf(s->bytestream, s->bytestream_end - s->bytestream, - "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLETYPE %s\nENDHDR\n", + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n", w, h, depth, maxval, tuple_type); s->bytestream += strlen(s->bytestream); diff --git a/libavcodec/pnm.c b/libavcodec/pnm.c index 54b55cfa53..f6e6d53ec9 100644 --- a/libavcodec/pnm.c +++ b/libavcodec/pnm.c @@ -93,7 +93,9 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) } else if (!strcmp(buf1, "MAXVAL")) { pnm_get(s, buf1, sizeof(buf1)); maxval = strtol(buf1, NULL, 10); - } else if (!strcmp(buf1, "TUPLETYPE")) { + } else if (!strcmp(buf1, "TUPLTYPE") || + /* libavcodec used to write invalid files */ + !strcmp(buf1, "TUPLETYPE")) { pnm_get(s, tuple_type, sizeof(tuple_type)); } else if (!strcmp(buf1, "ENDHDR")) { break; From 15cea3695daf3f6363794594982e3816ddc8d90b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 5 Jan 2012 20:15:53 +0100 Subject: [PATCH 25/25] riff: fix invalid av_freep() calls on EOF in ff_read_riff_info --- libavformat/riff.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libavformat/riff.c b/libavformat/riff.c index 4caac80643..99a8033732 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -669,8 +669,7 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size) AV_WL32(key, chunk_code); if (avio_read(pb, value, chunk_size) != chunk_size) { - av_freep(key); - av_freep(value); + av_free(value); av_log(s, AV_LOG_ERROR, "premature end of file while reading INFO tag\n"); return AVERROR_INVALIDDATA; }