summaryrefslogtreecommitdiff
path: root/libavformat/mxfdec.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/mxfdec.c')
-rw-r--r--libavformat/mxfdec.c713
1 files changed, 500 insertions, 213 deletions
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 3299e16528..d6eb7d1899 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -2,20 +2,20 @@
* MXF demuxer.
* Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -46,8 +46,11 @@
#include <stdint.h>
#include "libavutil/aes.h"
+#include "libavutil/avassert.h"
#include "libavutil/mathematics.h"
#include "libavcodec/bytestream.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/timecode.h"
#include "avformat.h"
#include "internal.h"
#include "mxf.h"
@@ -69,7 +72,7 @@ typedef enum {
OP3b,
OP3c,
OPAtom,
- OPSonyOpt, /* FATE sample, violates the spec in places */
+ OPSONYOpt, /* FATE sample, violates the spec in places */
} MXFOP;
typedef struct {
@@ -116,12 +119,23 @@ typedef struct {
typedef struct {
UID uid;
enum MXFMetadataSetType type;
+ int drop_frame;
+ int start_frame;
+ struct AVRational rate;
+ AVTimecode tc;
+} MXFTimecodeComponent;
+
+typedef struct {
+ UID uid;
+ enum MXFMetadataSetType type;
MXFSequence *sequence; /* mandatory, and only one */
UID sequence_ref;
int track_id;
uint8_t track_number[4];
AVRational edit_rate;
int intra_only;
+ uint64_t sample_count;
+ int64_t original_duration; ///< duration before multiplying st->duration by SampleRate/EditRate
} MXFTrack;
typedef struct {
@@ -136,6 +150,7 @@ typedef struct {
int frame_layout; /* See MXFFrameLayout enum */
int channels;
int bits_per_sample;
+ int field_dominance;
unsigned int component_depth;
unsigned int horiz_subsampling;
unsigned int vert_subsampling;
@@ -201,6 +216,7 @@ typedef struct {
struct AVAES *aesc;
uint8_t *local_tags;
int local_tags_count;
+ uint64_t last_partition;
uint64_t footer_partition;
KLVPacket current_klv_data;
int current_klv_index;
@@ -230,6 +246,8 @@ typedef struct {
enum MXFMetadataSetType type;
} MXFMetadataReadTableEntry;
+static int mxf_read_close(AVFormatContext *s);
+
/* 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 };
@@ -240,6 +258,7 @@ static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x
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 };
static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
static const uint8_t mxf_encrypted_essence_container[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 };
+static const uint8_t mxf_random_index_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x11,0x01,0x00 };
static const uint8_t mxf_sony_mpeg4_extradata[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0e,0x06,0x06,0x02,0x02,0x01,0x00,0x00 };
#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
@@ -262,7 +281,7 @@ static int64_t klv_decode_ber_length(AVIOContext *pb)
static int mxf_read_sync(AVIOContext *pb, const uint8_t *key, unsigned size)
{
int i, b;
- for (i = 0; i < size && !pb->eof_reached; i++) {
+ for (i = 0; i < size && !url_feof(pb); i++) {
b = avio_r8(pb);
if (b == key[0])
i = 0;
@@ -399,12 +418,14 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U
avpriv_request_sample(pb, "Primer pack item length %d", item_len);
return AVERROR_PATCHWELCOME;
}
- if (item_num > UINT_MAX / item_len)
+ if (item_num > 65536) {
+ av_log(mxf->fc, AV_LOG_ERROR, "item_num %d is too large\n", item_num);
return AVERROR_INVALIDDATA;
- mxf->local_tags_count = item_num;
- mxf->local_tags = av_malloc(item_num*item_len);
+ }
+ mxf->local_tags = av_calloc(item_num, item_len);
if (!mxf->local_tags)
return AVERROR(ENOMEM);
+ mxf->local_tags_count = item_num;
avio_read(pb, mxf->local_tags, item_num*item_len);
return 0;
}
@@ -412,20 +433,15 @@ 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;
uint32_t nb_essence_containers;
- int err;
- if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions))
+ tmp_part = av_realloc_array(mxf->partitions, mxf->partitions_count + 1, sizeof(*mxf->partitions));
+ if (!tmp_part)
return AVERROR(ENOMEM);
-
- if ((err = av_reallocp_array(&mxf->partitions, mxf->partitions_count + 1,
- sizeof(*mxf->partitions))) < 0) {
- mxf->partitions_count = 0;
- return err;
- }
+ mxf->partitions = tmp_part;
if (mxf->parsing_backward) {
/* insert the new partition pack in the middle
@@ -471,7 +487,10 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
partition->index_sid = avio_rb32(pb);
avio_skip(pb, 8);
partition->body_sid = avio_rb32(pb);
- avio_read(pb, op, sizeof(UID));
+ if (avio_read(pb, op, sizeof(UID)) != sizeof(UID)) {
+ av_log(mxf->fc, AV_LOG_ERROR, "Failed reading UID\n");
+ return AVERROR_INVALIDDATA;
+ }
nb_essence_containers = avio_rb32(pb);
/* some files don'thave FooterPartition set in every partition */
@@ -510,7 +529,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
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] == 64&& op[13] == 1) mxf->op = OPSonyOpt;
+ else if (op[12] == 64&& op[13] == 1) mxf->op = OPSONYOpt;
else if (op[12] == 0x10) {
/* SMPTE 390m: "There shall be exactly one essence container"
* The following block deals with files that violate this, namely:
@@ -521,10 +540,8 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
/* only nag once */
if (!mxf->op)
- av_log(mxf->fc, AV_LOG_WARNING,
- "\"OPAtom\" with %u ECs - assuming %s\n",
- nb_essence_containers,
- op == OP1a ? "OP1a" : "OPAtom");
+ av_log(mxf->fc, AV_LOG_WARNING, "\"OPAtom\" with %u ECs - assuming %s\n",
+ nb_essence_containers, op == OP1a ? "OP1a" : "OPAtom");
mxf->op = op;
} else
@@ -537,7 +554,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
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)
+ if (mxf->op == OPSONYOpt)
partition->kag_size = 512;
else
partition->kag_size = 1;
@@ -550,15 +567,12 @@ 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)
{
- int err;
+ MXFMetadataSet **tmp;
- if (mxf->metadata_sets_count+1 >= UINT_MAX / sizeof(*mxf->metadata_sets))
+ tmp = av_realloc_array(mxf->metadata_sets, mxf->metadata_sets_count + 1, sizeof(*mxf->metadata_sets));
+ if (!tmp)
return AVERROR(ENOMEM);
- if ((err = av_reallocp_array(&mxf->metadata_sets, mxf->metadata_sets_count + 1,
- sizeof(*mxf->metadata_sets))) < 0) {
- mxf->metadata_sets_count = 0;
- return err;
- }
+ mxf->metadata_sets = tmp;
mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set;
mxf->metadata_sets_count++;
return 0;
@@ -580,9 +594,7 @@ static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int siz
switch (tag) {
case 0x1901:
mxf->packages_count = avio_rb32(pb);
- if (mxf->packages_count >= UINT_MAX / sizeof(UID))
- return AVERROR_INVALIDDATA;
- mxf->packages_refs = av_malloc(mxf->packages_count * sizeof(UID));
+ mxf->packages_refs = av_calloc(mxf->packages_count, sizeof(UID));
if (!mxf->packages_refs)
return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
@@ -620,9 +632,7 @@ static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int si
switch(tag) {
case 0x4403:
package->tracks_count = avio_rb32(pb);
- if (package->tracks_count >= UINT_MAX / sizeof(UID))
- return AVERROR_INVALIDDATA;
- package->tracks_refs = av_malloc(package->tracks_count * sizeof(UID));
+ package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID));
if (!package->tracks_refs)
return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
@@ -632,6 +642,23 @@ static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int si
return 0;
}
+static int mxf_read_timecode_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+ MXFTimecodeComponent *mxf_timecode = arg;
+ switch(tag) {
+ case 0x1501:
+ mxf_timecode->start_frame = avio_rb64(pb);
+ break;
+ case 0x1502:
+ mxf_timecode->rate = (AVRational){avio_rb16(pb), 1};
+ break;
+ case 0x1503:
+ mxf_timecode->drop_frame = avio_r8(pb);
+ break;
+ }
+ return 0;
+}
+
static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFTrack *track = arg;
@@ -665,9 +692,7 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID
break;
case 0x1001:
sequence->structural_components_count = avio_rb32(pb);
- if (sequence->structural_components_count >= UINT_MAX / sizeof(UID))
- return AVERROR_INVALIDDATA;
- sequence->structural_components_refs = av_malloc(sequence->structural_components_count * sizeof(UID));
+ sequence->structural_components_refs = av_calloc(sequence->structural_components_count, sizeof(UID));
if (!sequence->structural_components_refs)
return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
@@ -683,9 +708,7 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size
switch(tag) {
case 0x4403:
package->tracks_count = avio_rb32(pb);
- if (package->tracks_count >= UINT_MAX / sizeof(UID))
- return AVERROR_INVALIDDATA;
- package->tracks_refs = av_malloc(package->tracks_count * sizeof(UID));
+ package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID));
if (!package->tracks_refs)
return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
@@ -708,29 +731,13 @@ static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *seg
int i, 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 / sizeof(*segment->stream_offset_entries)))
- return AVERROR(ENOMEM);
length = avio_rb32(pb);
- 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));
-
- 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);
+ if (!(segment->temporal_offset_entries=av_calloc(segment->nb_index_entries, sizeof(*segment->temporal_offset_entries))) ||
+ !(segment->flag_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->flag_entries))) ||
+ !(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries))))
return AVERROR(ENOMEM);
- }
for (i = 0; i < segment->nb_index_entries; i++) {
segment->temporal_offset_entries[i] = avio_r8(pb);
@@ -782,17 +789,18 @@ static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int
static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor)
{
int code, value, ofs = 0;
- char layout[16] = {0};
+ char layout[16] = {0}; /* not for printing, may end up not terminated on purpose */
do {
code = avio_r8(pb);
value = avio_r8(pb);
av_dlog(NULL, "pixel layout: code %#x\n", code);
- if (ofs < 16) {
+ if (ofs <= 14) {
layout[ofs++] = code;
layout[ofs++] = value;
- }
+ } else
+ break; /* don't read byte by byte on sneaky files filled with lots of non-zeroes */
} while (code != 0); /* SMPTE 377M E.2.46 */
ff_mxf_decode_pixel_layout(layout, &descriptor->pix_fmt);
@@ -805,9 +813,7 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
switch(tag) {
case 0x3F01:
descriptor->sub_descriptors_count = avio_rb32(pb);
- if (descriptor->sub_descriptors_count >= UINT_MAX / sizeof(UID))
- return AVERROR_INVALIDDATA;
- descriptor->sub_descriptors_refs = av_malloc(descriptor->sub_descriptors_count * sizeof(UID));
+ descriptor->sub_descriptors_refs = av_calloc(descriptor->sub_descriptors_count, sizeof(UID));
if (!descriptor->sub_descriptors_refs)
return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
@@ -835,6 +841,9 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
descriptor->aspect_ratio.num = avio_rb32(pb);
descriptor->aspect_ratio.den = avio_rb32(pb);
break;
+ case 0x3212:
+ descriptor->field_dominance = avio_r8(pb);
+ break;
case 0x3301:
descriptor->component_depth = avio_rb32(pb);
break;
@@ -863,7 +872,11 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
default:
/* Private uid used by SONY C0023S01.mxf */
if (IS_KLV_KEY(uid, mxf_sony_mpeg4_extradata)) {
- descriptor->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (descriptor->extradata)
+ av_log(NULL, AV_LOG_WARNING, "Duplicate sony_mpeg4_extradata\n");
+ av_free(descriptor->extradata);
+ descriptor->extradata_size = 0;
+ descriptor->extradata = av_malloc(size);
if (!descriptor->extradata)
return AVERROR(ENOMEM);
descriptor->extradata_size = size;
@@ -957,9 +970,8 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment
if (!nb_segments)
return AVERROR_INVALIDDATA;
- *sorted_segments = av_mallocz(nb_segments * sizeof(**sorted_segments));
- unsorted_segments = av_mallocz(nb_segments * sizeof(*unsorted_segments));
- if (!*sorted_segments || !unsorted_segments) {
+ if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) ||
+ !(*sorted_segments = av_calloc(nb_segments, sizeof(**sorted_segments)))) {
av_freep(sorted_segments);
av_free(unsorted_segments);
return AVERROR(ENOMEM);
@@ -974,19 +986,23 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment
/* 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;
+ uint64_t best_index_duration = 0;
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 we come across an entry with the same IndexStartPosition but larger IndexDuration, then we'll prefer it over the one we currently have.
*/
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 == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start ||
+ (s->index_start_position == best_index_start && s->index_duration > best_index_duration))) {
best = j;
best_body_sid = s->body_sid;
best_index_sid = s->index_sid;
best_index_start = s->index_start_position;
+ best_index_duration = s->index_duration;
}
}
@@ -1127,14 +1143,8 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta
if (index_table->nb_ptses <= 0)
return 0;
- if (index_table->nb_ptses > INT_MAX / sizeof(AVIndexEntry))
- 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) {
+ if (!(index_table->ptses = av_calloc(index_table->nb_ptses, sizeof(int64_t))) ||
+ !(index_table->fake_index = av_calloc(index_table->nb_ptses, sizeof(AVIndexEntry)))) {
av_freep(&index_table->ptses);
return AVERROR(ENOMEM);
}
@@ -1239,9 +1249,7 @@ static int mxf_compute_index_tables(MXFContext *mxf)
}
}
- if (mxf->nb_index_tables > INT_MAX / sizeof(MXFIndexTable) ||
- !(mxf->index_tables = av_mallocz(mxf->nb_index_tables *
- sizeof(MXFIndexTable)))) {
+ if (!(mxf->index_tables = av_calloc(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;
@@ -1260,12 +1268,8 @@ static int mxf_compute_index_tables(MXFContext *mxf)
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 / 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");
+ if (!(t->segments = av_calloc(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;
}
@@ -1309,12 +1313,20 @@ finish_decoding_index:
return ret;
}
-static int mxf_is_intra_only(MXFDescriptor *d)
+static int mxf_is_intra_only(MXFDescriptor *descriptor)
{
return mxf_get_codec_ul(mxf_intra_only_essence_container_uls,
- &d->essence_container_ul)->id != AV_CODEC_ID_NONE ||
+ &descriptor->essence_container_ul)->id != AV_CODEC_ID_NONE ||
mxf_get_codec_ul(mxf_intra_only_picture_essence_coding_uls,
- &d->essence_codec_ul)->id != AV_CODEC_ID_NONE;
+ &descriptor->essence_codec_ul)->id != AV_CODEC_ID_NONE;
+}
+
+static int mxf_add_timecode_metadata(AVDictionary **pm, const char *key, AVTimecode *tc)
+{
+ char buf[AV_TIMECODE_STR_SIZE];
+ av_dict_set(pm, key, av_timecode_make_string(tc, buf, 0), 0);
+
+ return 0;
}
static int mxf_parse_structural_metadata(MXFContext *mxf)
@@ -1341,25 +1353,48 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
MXFTrack *temp_track = NULL;
MXFDescriptor *descriptor = NULL;
MXFStructuralComponent *component = NULL;
+ MXFTimecodeComponent *mxf_tc = NULL;
UID *essence_container_ul = NULL;
const MXFCodecUL *codec_ul = NULL;
const MXFCodecUL *container_ul = NULL;
const MXFCodecUL *pix_fmt_ul = NULL;
AVStream *st;
+ AVTimecode tc;
+ int flags;
if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) {
av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track strong ref\n");
continue;
}
+ if ((component = mxf_resolve_strong_ref(mxf, &material_track->sequence_ref, TimecodeComponent))) {
+ mxf_tc = (MXFTimecodeComponent*)component;
+ flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0;
+ if (av_timecode_init(&tc, mxf_tc->rate, flags, mxf_tc->start_frame, mxf->fc) == 0) {
+ mxf_add_timecode_metadata(&mxf->fc->metadata, "timecode", &tc);
+ }
+ }
+
if (!(material_track->sequence = mxf_resolve_strong_ref(mxf, &material_track->sequence_ref, Sequence))) {
av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track sequence strong ref\n");
continue;
}
+ for (j = 0; j < material_track->sequence->structural_components_count; j++) {
+ component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], TimecodeComponent);
+ if (!component)
+ continue;
+
+ mxf_tc = (MXFTimecodeComponent*)component;
+ flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0;
+ if (av_timecode_init(&tc, mxf_tc->rate, flags, mxf_tc->start_frame, mxf->fc) == 0) {
+ mxf_add_timecode_metadata(&mxf->fc->metadata, "timecode", &tc);
+ break;
+ }
+ }
+
/* TODO: handle multiple source clips */
for (j = 0; j < material_track->sequence->structural_components_count; j++) {
- /* TODO: handle timecode component */
component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], SourceClip);
if (!component)
continue;
@@ -1417,12 +1452,21 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
}
st->id = source_track->track_id;
st->priv_data = source_track;
- st->duration = component->duration;
+ source_track->original_duration = st->duration = component->duration;
if (st->duration == -1)
st->duration = AV_NOPTS_VALUE;
st->start_time = component->start_position;
+ if (material_track->edit_rate.num <= 0 || material_track->edit_rate.den <= 0) {
+ av_log(mxf->fc, AV_LOG_WARNING,
+ "invalid edit rate (%d/%d) found on stream #%d, defaulting to 25/1\n",
+ material_track->edit_rate.num, material_track->edit_rate.den, st->index);
+ material_track->edit_rate = (AVRational){25, 1};
+ }
avpriv_set_pts_info(st, 64, material_track->edit_rate.den, material_track->edit_rate.num);
+ /* ensure SourceTrack EditRate == MaterialTrack EditRate since only the former is accessible via st->priv_data */
+ source_track->edit_rate = material_track->edit_rate;
+
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;
@@ -1467,7 +1511,16 @@ 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;
+ st->codec->codec_id = (enum AVCodecID)codec_ul->id;
+ av_log(mxf->fc, AV_LOG_VERBOSE, "%s: Universal Label: ",
+ avcodec_get_name(st->codec->codec_id));
+ for (k = 0; k < 16; k++) {
+ av_log(mxf->fc, AV_LOG_VERBOSE, "%.2x",
+ descriptor->essence_codec_ul[k]);
+ if (!(k+1 & 19) || k == 5)
+ av_log(mxf->fc, AV_LOG_VERBOSE, ".");
+ }
+ av_log(mxf->fc, AV_LOG_VERBOSE, "\n");
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
source_track->intra_only = mxf_is_intra_only(descriptor);
@@ -1475,41 +1528,35 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
if (st->codec->codec_id == AV_CODEC_ID_NONE)
st->codec->codec_id = container_ul->id;
st->codec->width = descriptor->width;
- /* Field height, not frame height */
- st->codec->height = descriptor->height;
+ st->codec->height = descriptor->height; /* Field height, not frame height */
switch (descriptor->frame_layout) {
case SegmentedFrame:
/* This one is a weird layout I don't fully understand. */
- av_log(mxf->fc, AV_LOG_INFO,
- "SegmentedFrame layout isn't currently supported\n");
+ av_log(mxf->fc, AV_LOG_INFO, "SegmentedFrame layout isn't currently supported\n");
break;
case FullFrame:
st->codec->field_order = AV_FIELD_PROGRESSIVE;
break;
case OneField:
/* Every other line is stored and needs to be duplicated. */
- av_log(mxf->fc, AV_LOG_INFO,
- "OneField frame layout isn't currently supported\n");
+ av_log(mxf->fc, AV_LOG_INFO, "OneField frame layout isn't currently supported\n");
+ break; /* The correct thing to do here is fall through, but by breaking we might be
+ able to decode some streams at half the vertical resolution, rather than not al all.
+ It's also for compatibility with the old behavior. */
+ case MixedFields:
break;
- /* The correct thing to do here is fall through, but by
- * breaking we might be able to decode some streams at half
- * the vertical resolution, rather than not al all.
- * It's also for compatibility with the old behavior. */
case SeparateFields:
- case MixedFields:
- /* Turn field height into frame height. */
- st->codec->height *= 2;
+ st->codec->height *= 2; /* Turn field height into frame height. */
+ break;
default:
- av_log(mxf->fc, AV_LOG_INFO,
- "Unknown frame layout type: %d\n",
- descriptor->frame_layout);
+ av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout);
}
if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) {
st->codec->pix_fmt = descriptor->pix_fmt;
if (st->codec->pix_fmt == AV_PIX_FMT_NONE) {
pix_fmt_ul = mxf_get_codec_ul(ff_mxf_pixel_format_uls,
&descriptor->essence_codec_ul);
- st->codec->pix_fmt = pix_fmt_ul->id;
+ st->codec->pix_fmt = (enum AVPixelFormat)pix_fmt_ul->id;
if (st->codec->pix_fmt == AV_PIX_FMT_NONE) {
/* support files created before RP224v10 by defaulting to UYVY422
if subsampling is 4:2:2 and component depth is 8-bit */
@@ -1524,8 +1571,9 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
st->need_parsing = AVSTREAM_PARSE_HEADERS;
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
- if (st->codec->codec_id == AV_CODEC_ID_NONE)
- st->codec->codec_id = container_ul->id;
+ /* Only overwrite existing codec ID if it is unset or A-law, which is the default according to SMPTE RP 224. */
+ if (st->codec->codec_id == AV_CODEC_ID_NONE || (st->codec->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE))
+ st->codec->codec_id = (enum AVCodecID)container_ul->id;
st->codec->channels = descriptor->channels;
st->codec->bits_per_coded_sample = descriptor->bits_per_sample;
@@ -1540,6 +1588,10 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
avpriv_set_pts_info(st, 64, 1, 48000);
}
+ /* if duration is set, rescale it from EditRate to SampleRate */
+ if (st->duration != AV_NOPTS_VALUE)
+ st->duration = av_rescale_q(st->duration, av_inv_q(material_track->edit_rate), st->time_base);
+
/* TODO: implement AV_CODEC_ID_RAWAUDIO */
if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) {
if (descriptor->bits_per_sample > 16 && descriptor->bits_per_sample <= 24)
@@ -1556,10 +1608,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
}
}
if (descriptor->extradata) {
- st->codec->extradata = av_mallocz(descriptor->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (st->codec->extradata) {
+ if (!ff_alloc_extradata(st->codec, descriptor->extradata_size)) {
memcpy(st->codec->extradata, descriptor->extradata, descriptor->extradata_size);
- st->codec->extradata_size = descriptor->extradata_size;
}
} else if (st->codec->codec_id == AV_CODEC_ID_H264) {
ret = ff_generate_avci_extradata(st);
@@ -1577,6 +1627,124 @@ fail_and_free:
return ret;
}
+static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str)
+{
+ int ret;
+ size_t buf_size;
+
+ if (size < 0)
+ return AVERROR(EINVAL);
+
+ buf_size = size + size/2 + 1;
+ *str = av_malloc(buf_size);
+ if (!*str)
+ return AVERROR(ENOMEM);
+
+ if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) {
+ av_freep(str);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mxf_uid_to_str(UID uid, char **str)
+{
+ int i;
+ char *p;
+ p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1);
+ if (!p)
+ return AVERROR(ENOMEM);
+ for (i = 0; i < sizeof(UID); i++) {
+ snprintf(p, 2 + 1, "%.2x", uid[i]);
+ p += 2;
+ if (i == 3 || i == 5 || i == 7 || i == 9) {
+ snprintf(p, 1 + 1, "-");
+ p++;
+ }
+ }
+ return 0;
+}
+
+static int mxf_timestamp_to_str(uint64_t timestamp, char **str)
+{
+ struct tm time = {0};
+ time.tm_year = (timestamp >> 48) - 1900;
+ time.tm_mon = (timestamp >> 40 & 0xFF) - 1;
+ time.tm_mday = (timestamp >> 32 & 0xFF);
+ time.tm_hour = (timestamp >> 24 & 0xFF);
+ time.tm_min = (timestamp >> 16 & 0xFF);
+ time.tm_sec = (timestamp >> 8 & 0xFF);
+
+ /* ensure month/day are valid */
+ time.tm_mon = FFMAX(time.tm_mon, 0);
+ time.tm_mday = FFMAX(time.tm_mday, 1);
+
+ *str = av_mallocz(32);
+ if (!*str)
+ return AVERROR(ENOMEM);
+ strftime(*str, 32, "%Y-%m-%d %H:%M:%S", &time);
+
+ return 0;
+}
+
+#define SET_STR_METADATA(pb, name, str) do { \
+ if ((ret = mxf_read_utf16_string(pb, size, &str)) < 0) \
+ return ret; \
+ av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \
+} while (0)
+
+#define SET_UID_METADATA(pb, name, var, str) do { \
+ avio_read(pb, var, 16); \
+ if ((ret = mxf_uid_to_str(var, &str)) < 0) \
+ return ret; \
+ av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \
+} while (0)
+
+#define SET_TS_METADATA(pb, name, var, str) do { \
+ var = avio_rb64(pb); \
+ if ((ret = mxf_timestamp_to_str(var, &str)) < 0) \
+ return ret; \
+ av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \
+} while (0)
+
+static int mxf_read_identification_metadata(void *arg, AVIOContext *pb, int tag, int size, UID _uid, int64_t klv_offset)
+{
+ MXFContext *mxf = arg;
+ AVFormatContext *s = mxf->fc;
+ int ret;
+ UID uid = { 0 };
+ char *str = NULL;
+ uint64_t ts;
+ switch (tag) {
+ case 0x3C01:
+ SET_STR_METADATA(pb, "company_name", str);
+ break;
+ case 0x3C02:
+ SET_STR_METADATA(pb, "product_name", str);
+ break;
+ case 0x3C04:
+ SET_STR_METADATA(pb, "product_version", str);
+ break;
+ case 0x3C05:
+ SET_UID_METADATA(pb, "product_uid", uid, str);
+ break;
+ case 0x3C06:
+ SET_TS_METADATA(pb, "modification_date", ts, str);
+ break;
+ case 0x3C08:
+ SET_STR_METADATA(pb, "application_platform", str);
+ break;
+ case 0x3C09:
+ SET_UID_METADATA(pb, "generation_uid", uid, str);
+ break;
+ case 0x3C0A:
+ SET_UID_METADATA(pb, "uid", uid, str);
+ break;
+ }
+ return 0;
+}
+
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 },
@@ -1589,6 +1757,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
{ { 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,0x30,0x00 }, mxf_read_identification_metadata },
{ { 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 },
@@ -1603,6 +1772,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
{ { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* AES3 */
{ { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */
{ { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */
+ { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent },
{ { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_cryptographic_context, sizeof(MXFCryptoContext), CryptoContext },
{ { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }, mxf_read_index_table_segment, sizeof(MXFIndexTableSegment), IndexTableSegment },
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType },
@@ -1616,7 +1786,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF
if (!ctx)
return AVERROR(ENOMEM);
- while (avio_tell(pb) + 4 < klv_end && !pb->eof_reached) {
+ while (avio_tell(pb) + 4 < klv_end && !url_feof(pb)) {
int ret;
int tag = avio_rb16(pb);
int size = avio_rb16(pb); /* KLV specified by 0x53 */
@@ -1647,6 +1817,9 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF
/* Accept the 64k local set limit being exceeded (Avid). Don't accept
* it extending past the end of the KLV though (zzuf5.mxf). */
if (avio_tell(pb) > klv_end) {
+ if (ctx_size)
+ av_free(ctx);
+
av_log(mxf->fc, AV_LOG_ERROR,
"local tag %#04x extends past end of local set @ %#"PRIx64"\n",
tag, klv->offset);
@@ -1690,31 +1863,33 @@ static int mxf_parse_handle_essence(MXFContext *mxf)
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;
- }
+ } else if (mxf->footer_partition || mxf->last_partition){
+ uint64_t offset;
- av_dlog(mxf->fc, "seeking to footer\n");
+ offset = mxf->footer_partition ? mxf->footer_partition : mxf->last_partition;
+
+ av_dlog(mxf->fc, "seeking to last partition\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");
+ av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing last partition\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);
+ /* seek to last partition and parse backward */
+ if ((ret = avio_seek(pb, mxf->run_in + offset, SEEK_SET)) < 0) {
+ av_log(mxf->fc, AV_LOG_ERROR, "failed to seek to last partition @ 0x%"PRIx64" (%"PRId64") - partial file?\n",
+ mxf->run_in + offset, ret);
return ret;
}
mxf->current_partition = NULL;
mxf->parsing_backward = 1;
+ } else {
+ av_dlog(mxf->fc, "can't find last partition\n");
+ return 0;
}
return 1;
@@ -1730,8 +1905,7 @@ static int mxf_parse_handle_partition_or_eof(MXFContext *mxf)
}
/**
- * Figure out the proper offset and length of the essence container
- * in each partition
+ * Figures out the proper offset and length of the essence container in each partition
*/
static void mxf_compute_essence_containers(MXFContext *mxf)
{
@@ -1771,38 +1945,6 @@ static int64_t round_to_kag(int64_t position, int 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 is_pcm(enum AVCodecID codec_id)
{
/* we only care about "normal" PCM codecs until we get samples */
@@ -1839,11 +1981,41 @@ static void mxf_handle_small_eubc(AVFormatContext *s)
mxf->edit_units_per_packet = 1920;
}
+static void mxf_read_random_index_pack(AVFormatContext *s)
+{
+ MXFContext *mxf = s->priv_data;
+ uint32_t length;
+ int64_t file_size;
+ KLVPacket klv;
+
+ if (!s->pb->seekable)
+ return;
+
+ file_size = avio_size(s->pb);
+ avio_seek(s->pb, file_size - 4, SEEK_SET);
+ length = avio_rb32(s->pb);
+ if (length <= 32 || length >= FFMIN(file_size, INT_MAX))
+ goto end;
+ avio_seek(s->pb, file_size - length, SEEK_SET);
+ if (klv_read_packet(&klv, s->pb) < 0 ||
+ !IS_KLV_KEY(klv.key, mxf_random_index_pack_key) ||
+ klv.length != length - 20)
+ goto end;
+
+ avio_skip(s->pb, klv.length - 12);
+ mxf->last_partition = avio_rb64(s->pb);
+
+end:
+ avio_seek(s->pb, mxf->run_in, SEEK_SET);
+}
+
static int mxf_read_header(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
KLVPacket klv;
int64_t essence_offset = 0;
+ int64_t last_pos = -1;
+ uint64_t last_pos_index = 1;
int ret;
mxf->last_forward_tell = INT64_MAX;
@@ -1857,9 +2029,16 @@ static int mxf_read_header(AVFormatContext *s)
mxf->fc = s;
mxf->run_in = avio_tell(s->pb);
- while (!s->pb->eof_reached) {
- const MXFMetadataReadTableEntry *metadata;
+ mxf_read_random_index_pack(s);
+ while (!url_feof(s->pb)) {
+ const MXFMetadataReadTableEntry *metadata;
+ if (avio_tell(s->pb) == last_pos) {
+ av_log(mxf->fc, AV_LOG_ERROR, "MXF structure loop detected\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((1ULL<<61) % last_pos_index++ == 0)
+ last_pos = avio_tell(s->pb);
if (klv_read_packet(&klv, s->pb) < 0) {
/* EOF - seek to previous partition or stop */
if(mxf_parse_handle_partition_or_eof(mxf) <= 0)
@@ -1876,13 +2055,32 @@ static int mxf_read_header(AVFormatContext *s)
IS_KLV_KEY(klv.key, mxf_system_item_key)) {
if (!mxf->current_partition) {
- av_log(mxf->fc, AV_LOG_ERROR,
- "found essence prior to first PartitionPack\n");
+ av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n");
return AVERROR_INVALIDDATA;
}
if (!mxf->current_partition->essence_offset) {
- compute_partition_essence_offset(s, mxf, &klv);
+ /* 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(mxf->current_partition->this_partition +
+ mxf->current_partition->pack_length, mxf->current_partition->kag_size) +
+ round_to_kag(mxf->current_partition->header_byte_count, mxf->current_partition->kag_size) +
+ round_to_kag(mxf->current_partition->index_byte_count, mxf->current_partition->kag_size);
+
+ if (mxf->op == OPAtom) {
+ /* point essence_offset to the actual data
+ * OPAtom has all the essence in one big KLV
+ */
+ mxf->current_partition->essence_offset = avio_tell(s->pb);
+ mxf->current_partition->essence_length = klv.length;
+ } else {
+ /* NOTE: op1a_essence_offset may be less than to klv.offset (C0023S01.mxf) */
+ mxf->current_partition->essence_offset = op1a_essence_offset;
+ }
}
if (!essence_offset)
@@ -1942,10 +2140,10 @@ static int mxf_read_header(AVFormatContext *s)
/* 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;
+ goto fail;
if ((ret = mxf_compute_index_tables(mxf)) < 0)
- return ret;
+ goto fail;
if (mxf->nb_index_tables > 1) {
/* TODO: look up which IndexSID to use via EssenceContainerData */
@@ -1953,12 +2151,17 @@ static int mxf_read_header(AVFormatContext *s)
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;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
mxf_handle_small_eubc(s);
return 0;
+fail:
+ mxf_read_close(s);
+
+ return ret;
}
/**
@@ -1975,11 +2178,9 @@ static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset
if (mxf->nb_index_tables <= 0)
return -1;
- /* find mxf->current_edit_unit so that the next edit unit starts ahead
- * of current_offset */
+ /* find mxf->current_edit_unit so that the next edit unit starts ahead of current_offset */
while (mxf->current_edit_unit >= 0) {
- if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1,
- NULL, &next_ofs, 0) < 0)
+ if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0)
return -1;
if (next_ofs <= last_ofs) {
@@ -1997,29 +2198,76 @@ static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset
mxf->current_edit_unit++;
}
- /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files
- * may lack IndexEntryArrays */
+ /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files may lack IndexEntryArrays */
if (mxf->current_edit_unit < 0)
return -1;
return next_ofs;
}
+static int mxf_compute_sample_count(MXFContext *mxf, int stream_index, uint64_t *sample_count)
+{
+ int i, total = 0, size = 0;
+ AVStream *st = mxf->fc->streams[stream_index];
+ MXFTrack *track = st->priv_data;
+ AVRational time_base = av_inv_q(track->edit_rate);
+ AVRational sample_rate = av_inv_q(st->time_base);
+ const MXFSamplesPerFrame *spf = NULL;
+
+ if ((sample_rate.num / sample_rate.den) == 48000)
+ spf = ff_mxf_get_samples_per_frame(mxf->fc, time_base);
+ if (!spf) {
+ int remainder = (sample_rate.num * time_base.num) % (time_base.den * sample_rate.den);
+ *sample_count = av_q2d(av_mul_q((AVRational){mxf->current_edit_unit, 1},
+ av_mul_q(sample_rate, time_base)));
+ if (remainder)
+ av_log(mxf->fc, AV_LOG_WARNING,
+ "seeking detected on stream #%d with time base (%d/%d) and sample rate (%d/%d), audio pts won't be accurate.\n",
+ stream_index, time_base.num, time_base.den, sample_rate.num, sample_rate.den);
+ return 0;
+ }
+
+ while (spf->samples_per_frame[size]) {
+ total += spf->samples_per_frame[size];
+ size++;
+ }
+
+ av_assert2(size);
+
+ *sample_count = (mxf->current_edit_unit / size) * (uint64_t)total;
+ for (i = 0; i < mxf->current_edit_unit % size; i++) {
+ *sample_count += spf->samples_per_frame[i];
+ }
+
+ return 0;
+}
+
+static int mxf_set_audio_pts(MXFContext *mxf, AVCodecContext *codec, AVPacket *pkt)
+{
+ MXFTrack *track = mxf->fc->streams[pkt->stream_index]->priv_data;
+ pkt->pts = track->sample_count;
+ if ( codec->channels <= 0
+ || av_get_bits_per_sample(codec->codec_id) <= 0
+ || codec->channels * (int64_t)av_get_bits_per_sample(codec->codec_id) < 8)
+ return AVERROR(EINVAL);
+ track->sample_count += pkt->size / (codec->channels * (int64_t)av_get_bits_per_sample(codec->codec_id) / 8);
+ return 0;
+}
+
static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
{
KLVPacket klv;
MXFContext *mxf = s->priv_data;
- while (!s->pb->eof_reached) {
- if (klv_read_packet(&klv, s->pb) < 0)
- return -1;
+ while (klv_read_packet(&klv, s->pb) == 0) {
+ int ret;
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) {
+ ret = mxf_decrypt_triplet(s, pkt, &klv);
+ if (ret < 0) {
av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
return 0;
}
@@ -2029,6 +2277,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
int64_t next_ofs, next_klv;
AVStream *st;
MXFTrack *track;
+ AVCodecContext *codec;
if (index < 0) {
av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12));
@@ -2045,9 +2294,8 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
next_ofs = mxf_set_current_edit_unit(mxf, klv.offset);
if (next_ofs >= 0 && next_klv > next_ofs) {
- /* if this check is hit then it's possible OPAtom was treated
- * as OP1a truncate the packet since it's probably very large
- * (>2 GiB is common) */
+ /* if this check is hit then it's possible OPAtom was treated as OP1a
+ * truncate the packet since it's probably very large (>2 GiB is common) */
avpriv_request_sample(s,
"OPAtom misinterpreted as OP1a?"
"KLV for edit unit %i extending into "
@@ -2060,31 +2308,33 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
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;
+ return AVERROR_INVALIDDATA;
}
} else {
- int ret = av_get_packet(s->pb, pkt, klv.length);
+ 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 && next_ofs >= 0) {
- /* mxf->current_edit_unit good - see if we have an
- * index table to derive timestamps from */
+ codec = s->streams[index]->codec;
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) {
+ /* mxf->current_edit_unit good - see if we have an index table to derive timestamps from */
MXFIndexTable *t = &mxf->index_tables[0];
- if (mxf->nb_index_tables >= 1 &&
- mxf->current_edit_unit < t->nb_ptses) {
+ if (mxf->nb_index_tables >= 1 && mxf->current_edit_unit < t->nb_ptses) {
pkt->dts = mxf->current_edit_unit + t->first_dts;
pkt->pts = t->ptses[mxf->current_edit_unit];
} else if (track->intra_only) {
/* intra-only -> PTS = EditUnit.
- * let utils.c figure out DTS since it can be
- * < PTS if low_delay = 0 (Sony IMX30) */
+ * let utils.c figure out DTS since it can be < PTS if low_delay = 0 (Sony IMX30) */
pkt->pts = mxf->current_edit_unit;
}
+ } else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ int ret = mxf_set_audio_pts(mxf, codec, pkt);
+ if (ret < 0)
+ return ret;
}
/* seek for truncated packets */
@@ -2095,7 +2345,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
skip:
avio_skip(s->pb, klv.length);
}
- return AVERROR_EOF;
+ return url_feof(s->pb) ? AVERROR_EOF : -1;
}
static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
@@ -2139,22 +2389,26 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
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 ((size = av_get_packet(s->pb, pkt, size)) < 0)
+ return size;
+
+ pkt->stream_index = 0;
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];
+ } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ int ret = mxf_set_audio_pts(mxf, st->codec, pkt);
+ if (ret < 0)
+ return ret;
}
- pkt->stream_index = 0;
mxf->current_edit_unit += edit_units;
return 0;
}
-
static int mxf_read_close(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
@@ -2168,6 +2422,9 @@ static int mxf_read_close(AVFormatContext *s)
for (i = 0; i < mxf->metadata_sets_count; i++) {
switch (mxf->metadata_sets[i]->type) {
+ case Descriptor:
+ av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->extradata);
+ break;
case MultipleDescriptor:
av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->sub_descriptors_refs);
break;
@@ -2194,10 +2451,12 @@ static int mxf_read_close(AVFormatContext *s)
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].ptses);
- av_freep(&mxf->index_tables[i].fake_index);
+ if (mxf->index_tables) {
+ for (i = 0; i < mxf->nb_index_tables; i++) {
+ av_freep(&mxf->index_tables[i].segments);
+ av_freep(&mxf->index_tables[i].ptses);
+ av_freep(&mxf->index_tables[i].fake_index);
+ }
}
av_freep(&mxf->index_tables);
@@ -2205,18 +2464,27 @@ static int mxf_read_close(AVFormatContext *s)
}
static int mxf_probe(AVProbeData *p) {
- uint8_t *bufp = p->buf;
- uint8_t *end = p->buf + p->buf_size;
+ const uint8_t *bufp = p->buf;
+ const uint8_t *end = p->buf + p->buf_size;
if (p->buf_size < sizeof(mxf_header_partition_pack_key))
return 0;
/* Must skip Run-In Sequence and search for MXF header partition pack key SMPTE 377M 5.5 */
end -= sizeof(mxf_header_partition_pack_key);
- for (; bufp < end; bufp++) {
- if (IS_KLV_KEY(bufp, mxf_header_partition_pack_key))
- return AVPROBE_SCORE_MAX;
+
+ for (; bufp < end;) {
+ if (!((bufp[13] - 1) & 0xF2)){
+ if (AV_RN32(bufp ) == AV_RN32(mxf_header_partition_pack_key ) &&
+ AV_RN32(bufp+ 4) == AV_RN32(mxf_header_partition_pack_key+ 4) &&
+ AV_RN32(bufp+ 8) == AV_RN32(mxf_header_partition_pack_key+ 8) &&
+ AV_RN16(bufp+12) == AV_RN16(mxf_header_partition_pack_key+12))
+ return AVPROBE_SCORE_MAX;
+ bufp ++;
+ } else
+ bufp += 10;
}
+
return 0;
}
@@ -2228,8 +2496,13 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
int64_t seconds;
MXFContext* mxf = s->priv_data;
int64_t seekpos;
- int ret;
+ int i, ret;
MXFIndexTable *t;
+ MXFTrack *source_track = st->priv_data;
+
+ /* if audio then truncate sample_time to EditRate */
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ sample_time = av_rescale_q(sample_time, st->time_base, av_inv_q(source_track->edit_rate));
if (mxf->nb_index_tables <= 0) {
if (!s->bit_rate)
@@ -2258,7 +2531,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
} 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);
+ sample_time = FFMIN(sample_time, source_track->original_duration - 1);
}
if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0)
@@ -2268,6 +2541,20 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
mxf->current_edit_unit = sample_time;
avio_seek(s->pb, seekpos, SEEK_SET);
}
+
+ // Update all tracks sample count
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *cur_st = s->streams[i];
+ MXFTrack *cur_track = cur_st->priv_data;
+ uint64_t current_sample_count = 0;
+ if (cur_st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ ret = mxf_compute_sample_count(mxf, i, &current_sample_count);
+ if (ret < 0)
+ return ret;
+
+ cur_track->sample_count = current_sample_count;
+ }
+ }
return 0;
}