* SMPTE 381M Mapping MPEG Streams into the MXF Generic Container
* SMPTE 382M Mapping AES3 and Broadcast Wave Audio into the MXF Generic Container
* SMPTE 383M Mapping DV-DIF Data to the MXF Generic Container
+ * SMPTE 2067-21 Interoperable Master Format — Application #2E
*
* Principle
* Search for Track numbers which will identify essence element KLV packets.
#include "libavutil/aes.h"
#include "libavutil/avassert.h"
+#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mathematics.h"
#include "libavcodec/bytestream.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/parseutils.h"
#include "libavutil/timecode.h"
+#include "libavutil/opt.h"
#include "avformat.h"
#include "internal.h"
#include "mxf.h"
+#define MXF_MAX_CHUNK_SIZE (32 << 20)
+
typedef enum {
Header,
BodyPartition,
OPSONYOpt, /* FATE sample, violates the spec in places */
} MXFOP;
+typedef enum {
+ UnknownWrapped = 0,
+ FrameWrapped,
+ ClipWrapped,
+} MXFWrappingScheme;
+
typedef struct MXFPartition {
int closed;
int complete;
int pack_length;
int64_t pack_ofs; ///< absolute offset of pack in file, including run-in
int64_t body_offset;
+ KLVPacket first_essence_klv;
} MXFPartition;
typedef struct MXFCryptoContext {
uint8_t origin;
} MXFSequence;
-typedef struct MXFTrack {
+typedef struct MXFTimecodeComponent {
UID uid;
enum MXFMetadataSetType type;
int drop_frame;
int64_t original_duration; /* st->duration in SampleRate/EditRate units */
int index_sid;
int body_sid;
+ MXFWrappingScheme wrapping;
+ int edit_units_per_packet; /* how many edit units to read at a time (PCM, ClipWrapped) */
} MXFTrack;
typedef struct MXFDescriptor {
int bits_per_sample;
int64_t duration; /* ContainerDuration optional property */
unsigned int component_depth;
+ unsigned int black_ref_level;
+ unsigned int white_ref_level;
+ unsigned int color_range;
unsigned int horiz_subsampling;
unsigned int vert_subsampling;
UID *sub_descriptors_refs;
uint8_t *extradata;
int extradata_size;
enum AVPixelFormat pix_fmt;
+ UID color_primaries_ul;
+ UID color_trc_ul;
+ UID color_space_ul;
+ AVMasteringDisplayMetadata *mastering;
+ AVContentLightMetadata *coll;
+ size_t coll_size;
} MXFDescriptor;
typedef struct MXFIndexTableSegment {
} MXFIndexTable;
typedef struct MXFContext {
+ const AVClass *class; /**< Class for private options. */
MXFPartition *partitions;
unsigned partitions_count;
MXFOP op;
int local_tags_count;
uint64_t footer_partition;
KLVPacket current_klv_data;
- int current_klv_index;
int run_in;
MXFPartition *current_partition;
int parsing_backward;
int64_t last_forward_tell;
int last_forward_partition;
- int64_t current_edit_unit;
int nb_index_tables;
MXFIndexTable *index_tables;
- int edit_units_per_packet; ///< how many edit units to read at a time (PCM, OPAtom)
+ int eia608_extract;
} MXFContext;
-enum MXFWrappingScheme {
- Frame,
- Clip,
-};
-
/* 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);
static const uint8_t mxf_system_item_key_cp[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x03,0x01,0x04 };
static const uint8_t mxf_system_item_key_gc[] = { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 };
static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 };
+static const uint8_t mxf_apple_coll_prefix[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01 };
/* 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 };
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_jp2k_rsiz[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 };
static const uint8_t mxf_indirect_value_utf16le[] = { 0x4c,0x00,0x02,0x10,0x01,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
static const uint8_t mxf_indirect_value_utf16be[] = { 0x42,0x01,0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
+static const uint8_t mxf_apple_coll_max_cll[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x01 };
+static const uint8_t mxf_apple_coll_max_fall[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x02 };
#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
switch ((*ctx)->type) {
case Descriptor:
av_freep(&((MXFDescriptor *)*ctx)->extradata);
+ av_freep(&((MXFDescriptor *)*ctx)->mastering);
+ av_freep(&((MXFDescriptor *)*ctx)->coll);
break;
case MultipleDescriptor:
av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
default:
break;
}
- if (freectx)
- av_freep(ctx);
+ if (freectx) {
+ av_freep(ctx);
+ }
}
static int64_t klv_decode_ber_length(AVIOContext *pb)
while (bytes_num--)
size = size << 8 | avio_r8(pb);
}
+ if (size > INT64_MAX)
+ return AVERROR_INVALIDDATA;
return size;
}
static int klv_read_packet(KLVPacket *klv, AVIOContext *pb)
{
+ int64_t length, pos;
if (!mxf_read_sync(pb, mxf_klv_key, 4))
return AVERROR_INVALIDDATA;
klv->offset = avio_tell(pb) - 4;
memcpy(klv->key, mxf_klv_key, 4);
avio_read(pb, klv->key + 4, 12);
- klv->length = klv_decode_ber_length(pb);
- return klv->length == -1 ? -1 : 0;
+ length = klv_decode_ber_length(pb);
+ if (length < 0)
+ return length;
+ klv->length = length;
+ pos = avio_tell(pb);
+ if (pos > INT64_MAX - length)
+ return AVERROR_INVALIDDATA;
+ klv->next_klv = pos + length;
+ return 0;
}
static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv, int body_sid)
return i;
}
/* return 0 if only one stream, for OP Atom files with 0 as track number */
- return s->nb_streams == 1 ? 0 : -1;
+ return s->nb_streams == 1 && s->streams[0]->priv_data ? 0 : -1;
}
-static int find_body_sid_by_offset(MXFContext *mxf, int64_t offset)
+static int find_body_sid_by_absolute_offset(MXFContext *mxf, int64_t offset)
{
// we look for partition where the offset is placed
int a, b, m;
- int64_t this_partition;
+ int64_t pack_ofs;
a = -1;
b = mxf->partitions_count;
while (b - a > 1) {
- m = (a + b) >> 1;
- this_partition = mxf->partitions[m].this_partition;
- if (this_partition <= offset)
+ m = (a + b) >> 1;
+ pack_ofs = mxf->partitions[m].pack_ofs;
+ if (pack_ofs <= offset)
a = m;
else
b = m;
return mxf->partitions[a].body_sid;
}
+static int mxf_get_eia608_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt, int64_t length)
+{
+ int count = avio_rb16(s->pb);
+ int cdp_identifier, cdp_length, cdp_footer_id, ccdata_id, cc_count;
+ int line_num, sample_coding, sample_count;
+ int did, sdid, data_length;
+ int i, ret;
+
+ if (count != 1)
+ av_log(s, AV_LOG_WARNING, "unsupported multiple ANC packets (%d) per KLV packet\n", count);
+
+ for (i = 0; i < count; i++) {
+ if (length < 6) {
+ av_log(s, AV_LOG_ERROR, "error reading s436m packet %"PRId64"\n", length);
+ return AVERROR_INVALIDDATA;
+ }
+ line_num = avio_rb16(s->pb);
+ avio_r8(s->pb); // wrapping type
+ sample_coding = avio_r8(s->pb);
+ sample_count = avio_rb16(s->pb);
+ length -= 6 + 8 + sample_count;
+ if (line_num != 9 && line_num != 11)
+ continue;
+ if (sample_coding == 7 || sample_coding == 8 || sample_coding == 9) {
+ av_log(s, AV_LOG_WARNING, "unsupported s436m 10 bit sample coding\n");
+ continue;
+ }
+ if (length < 0)
+ return AVERROR_INVALIDDATA;
+
+ avio_rb32(s->pb); // array count
+ avio_rb32(s->pb); // array elem size
+ did = avio_r8(s->pb);
+ sdid = avio_r8(s->pb);
+ data_length = avio_r8(s->pb);
+ if (did != 0x61 || sdid != 1) {
+ av_log(s, AV_LOG_WARNING, "unsupported did or sdid: %x %x\n", did, sdid);
+ continue;
+ }
+ cdp_identifier = avio_rb16(s->pb); // cdp id
+ if (cdp_identifier != 0x9669) {
+ av_log(s, AV_LOG_ERROR, "wrong cdp identifier %x\n", cdp_identifier);
+ return AVERROR_INVALIDDATA;
+ }
+ cdp_length = avio_r8(s->pb);
+ avio_r8(s->pb); // cdp_frame_rate
+ avio_r8(s->pb); // cdp_flags
+ avio_rb16(s->pb); // cdp_hdr_sequence_cntr
+ ccdata_id = avio_r8(s->pb); // ccdata_id
+ if (ccdata_id != 0x72) {
+ av_log(s, AV_LOG_ERROR, "wrong cdp data section %x\n", ccdata_id);
+ return AVERROR_INVALIDDATA;
+ }
+ cc_count = avio_r8(s->pb) & 0x1f;
+ ret = av_get_packet(s->pb, pkt, cc_count * 3);
+ if (ret < 0)
+ return ret;
+ if (cdp_length - 9 - 4 < cc_count * 3) {
+ av_log(s, AV_LOG_ERROR, "wrong cdp size %d cc count %d\n", cdp_length, cc_count);
+ return AVERROR_INVALIDDATA;
+ }
+ avio_skip(s->pb, data_length - 9 - 4 - cc_count * 3);
+ cdp_footer_id = avio_r8(s->pb);
+ if (cdp_footer_id != 0x74) {
+ av_log(s, AV_LOG_ERROR, "wrong cdp footer section %x\n", cdp_footer_id);
+ return AVERROR_INVALIDDATA;
+ }
+ avio_rb16(s->pb); // cdp_ftr_sequence_cntr
+ avio_r8(s->pb); // packet_checksum
+ break;
+ }
+
+ return 0;
+}
+
/* XXX: use AVBitStreamFilter */
static int mxf_get_d10_aes3_packet(AVIOContext *pb, AVStream *st, AVPacket *pkt, int64_t length)
{
av_aes_init(mxf->aesc, s->key, 128, 1);
}
// crypto context
- avio_skip(pb, klv_decode_ber_length(pb));
+ size = klv_decode_ber_length(pb);
+ if (size < 0)
+ return size;
+ avio_skip(pb, size);
// plaintext offset
klv_decode_ber_length(pb);
plaintext_size = avio_rb64(pb);
if (!IS_KLV_KEY(klv, mxf_essence_element_key))
return AVERROR_INVALIDDATA;
- body_sid = find_body_sid_by_offset(mxf, klv->offset);
+ body_sid = find_body_sid_by_absolute_offset(mxf, klv->offset);
index = mxf_get_stream_index(s, klv, body_sid);
if (index < 0)
return AVERROR_INVALIDDATA;
static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFContext *mxf = arg;
+ AVFormatContext *s = mxf->fc;
MXFPartition *partition, *tmp_part;
UID op;
uint64_t footer_partition;
}
nb_essence_containers = avio_rb32(pb);
+ if (partition->type == Header) {
+ char str[36];
+ snprintf(str, sizeof(str), "%08x.%08x.%08x.%08x", AV_RB32(&op[0]), AV_RB32(&op[4]), AV_RB32(&op[8]), AV_RB32(&op[12]));
+ av_dict_set(&s->metadata, "operational_pattern_ul", str, 0);
+ }
+
if (partition->this_partition &&
partition->previous_partition == partition->this_partition) {
av_log(mxf->fc, AV_LOG_ERROR,
return 0;
}
-static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set)
+static int mxf_add_metadata_set(MXFContext *mxf, MXFMetadataSet **metadata_set)
{
MXFMetadataSet **tmp;
tmp = av_realloc_array(mxf->metadata_sets, mxf->metadata_sets_count + 1, sizeof(*mxf->metadata_sets));
- if (!tmp)
+ if (!tmp) {
+ mxf_free_metadataset(metadata_set, 1);
return AVERROR(ENOMEM);
+ }
mxf->metadata_sets = tmp;
- mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set;
+ mxf->metadata_sets[mxf->metadata_sets_count] = *metadata_set;
mxf->metadata_sets_count++;
return 0;
}
static int mxf_read_strong_ref_array(AVIOContext *pb, UID **refs, int *count)
{
*count = avio_rb32(pb);
+ av_free(*refs);
*refs = av_calloc(*count, sizeof(UID));
if (!*refs) {
*count = 0;
return AVERROR(EINVAL);
buf_size = size + size / 2 + 1;
+ av_free(*str);
*str = av_malloc(buf_size);
if (!*str)
return AVERROR(ENOMEM);
case 0x1901:
if (mxf->packages_refs)
av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple packages_refs\n");
- av_free(mxf->packages_refs);
return mxf_read_strong_ref_array(pb, &mxf->packages_refs, &mxf->packages_count);
case 0x1902:
- av_free(mxf->essence_container_data_refs);
return mxf_read_strong_ref_array(pb, &mxf->essence_container_data_refs, &mxf->essence_container_data_count);
}
return 0;
if(segment->nb_index_entries && length < 11)
return AVERROR_INVALIDDATA;
- 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)))) {
+ if (!FF_ALLOC_TYPED_ARRAY(segment->temporal_offset_entries, segment->nb_index_entries) ||
+ !FF_ALLOC_TYPED_ARRAY(segment->flag_entries , segment->nb_index_entries) ||
+ !FF_ALLOC_TYPED_ARRAY(segment->stream_offset_entries , segment->nb_index_entries)) {
av_freep(&segment->temporal_offset_entries);
av_freep(&segment->flag_entries);
return AVERROR(ENOMEM);
descriptor->aspect_ratio.num = avio_rb32(pb);
descriptor->aspect_ratio.den = avio_rb32(pb);
break;
+ case 0x3210:
+ avio_read(pb, descriptor->color_trc_ul, 16);
+ break;
case 0x3212:
descriptor->field_dominance = avio_r8(pb);
break;
+ case 0x3219:
+ avio_read(pb, descriptor->color_primaries_ul, 16);
+ break;
+ case 0x321A:
+ avio_read(pb, descriptor->color_space_ul, 16);
+ break;
case 0x3301:
descriptor->component_depth = avio_rb32(pb);
break;
case 0x3302:
descriptor->horiz_subsampling = avio_rb32(pb);
break;
+ case 0x3304:
+ descriptor->black_ref_level = avio_rb32(pb);
+ break;
+ case 0x3305:
+ descriptor->white_ref_level = avio_rb32(pb);
+ break;
+ case 0x3306:
+ descriptor->color_range = avio_rb32(pb);
+ break;
case 0x3308:
descriptor->vert_subsampling = avio_rb32(pb);
break;
rsiz == FF_PROFILE_JPEG2000_DCINEMA_4K)
descriptor->pix_fmt = AV_PIX_FMT_XYZ12;
}
+ if (IS_KLV_KEY(uid, ff_mxf_mastering_display_prefix)) {
+ if (!descriptor->mastering) {
+ descriptor->mastering = av_mastering_display_metadata_alloc();
+ if (!descriptor->mastering)
+ return AVERROR(ENOMEM);
+ }
+ if (IS_KLV_KEY(uid, ff_mxf_mastering_display_local_tags[0].uid)) {
+ for (int i = 0; i < 3; i++) {
+ /* Order: large x, large y, other (i.e. RGB) */
+ descriptor->mastering->display_primaries[i][0] = av_make_q(avio_rb16(pb), FF_MXF_MASTERING_CHROMA_DEN);
+ descriptor->mastering->display_primaries[i][1] = av_make_q(avio_rb16(pb), FF_MXF_MASTERING_CHROMA_DEN);
+ }
+ /* Check we have seen mxf_mastering_display_white_point_chromaticity */
+ if (descriptor->mastering->white_point[0].den != 0)
+ descriptor->mastering->has_primaries = 1;
+ }
+ if (IS_KLV_KEY(uid, ff_mxf_mastering_display_local_tags[1].uid)) {
+ descriptor->mastering->white_point[0] = av_make_q(avio_rb16(pb), FF_MXF_MASTERING_CHROMA_DEN);
+ descriptor->mastering->white_point[1] = av_make_q(avio_rb16(pb), FF_MXF_MASTERING_CHROMA_DEN);
+ /* Check we have seen mxf_mastering_display_primaries */
+ if (descriptor->mastering->display_primaries[0][0].den != 0)
+ descriptor->mastering->has_primaries = 1;
+ }
+ if (IS_KLV_KEY(uid, ff_mxf_mastering_display_local_tags[2].uid)) {
+ descriptor->mastering->max_luminance = av_make_q(avio_rb32(pb), FF_MXF_MASTERING_LUMA_DEN);
+ /* Check we have seen mxf_mastering_display_minimum_luminance */
+ if (descriptor->mastering->min_luminance.den != 0)
+ descriptor->mastering->has_luminance = 1;
+ }
+ if (IS_KLV_KEY(uid, ff_mxf_mastering_display_local_tags[3].uid)) {
+ descriptor->mastering->min_luminance = av_make_q(avio_rb32(pb), FF_MXF_MASTERING_LUMA_DEN);
+ /* Check we have seen mxf_mastering_display_maximum_luminance */
+ if (descriptor->mastering->max_luminance.den != 0)
+ descriptor->mastering->has_luminance = 1;
+ }
+ }
+ if (IS_KLV_KEY(uid, mxf_apple_coll_prefix)) {
+ if (!descriptor->coll) {
+ descriptor->coll = av_content_light_metadata_alloc(&descriptor->coll_size);
+ if (!descriptor->coll)
+ return AVERROR(ENOMEM);
+ }
+ if (IS_KLV_KEY(uid, mxf_apple_coll_max_cll)) {
+ descriptor->coll->MaxCLL = avio_rb16(pb);
+ }
+ if (IS_KLV_KEY(uid, mxf_apple_coll_max_fall)) {
+ descriptor->coll->MaxFALL = avio_rb16(pb);
+ }
+ }
break;
}
return 0;
static const MXFCodecUL mxf_picture_essence_container_uls[] = {
// video essence container uls
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0c,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000 },
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x10,0x60,0x01 }, 14, AV_CODEC_ID_H264 }, /* H.264 frame wrapped */
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x12,0x01,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC-1 frame wrapped */
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MPEG-ES frame wrapped */
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* Type D-10 mapping of 40Mbps 525/60-I */
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, AV_CODEC_ID_DVVIDEO }, /* DV 625 25mbps */
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x05,0x00,0x00 }, 14, AV_CODEC_ID_RAWVIDEO }, /* uncompressed picture */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0c,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000, NULL, 14 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x10,0x60,0x01 }, 14, AV_CODEC_ID_H264, NULL, 15 }, /* H.264 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, 14, AV_CODEC_ID_DNXHD, NULL, 14 }, /* VC-3 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x12,0x01,0x00 }, 14, AV_CODEC_ID_VC1, NULL, 14 }, /* VC-1 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x14,0x01,0x00 }, 14, AV_CODEC_ID_TIFF, NULL, 14 }, /* TIFF */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x15,0x01,0x00 }, 14, AV_CODEC_ID_DIRAC, NULL, 14 }, /* VC-2 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x1b,0x01,0x00 }, 14, AV_CODEC_ID_CFHD, NULL, 14 }, /* VC-5 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x1c,0x01,0x00 }, 14, AV_CODEC_ID_PRORES, NULL, 14 }, /* ProRes */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO, NULL, 15 }, /* MPEG-ES */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO, NULL, 15, D10D11Wrap }, /* SMPTE D-10 mapping */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, AV_CODEC_ID_DVVIDEO, NULL, 15 }, /* DV 625 25mbps */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x05,0x00,0x00 }, 14, AV_CODEC_ID_RAWVIDEO, NULL, 15, RawVWrap }, /* uncompressed picture */
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x0f,0x03,0x01,0x02,0x20,0x01,0x01 }, 15, AV_CODEC_ID_HQ_HQA },
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x0f,0x03,0x01,0x02,0x20,0x02,0x01 }, 15, AV_CODEC_ID_HQX },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x15,0x00,0x04,0x02,0x10,0x00,0x01 }, 16, AV_CODEC_ID_HEVC, NULL, 15 }, /* Canon XF-HEVC */
{ { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0xff,0x4b,0x46,0x41,0x41,0x00,0x0d,0x4d,0x4f }, 14, AV_CODEC_ID_RAWVIDEO }, /* Legacy ?? Uncompressed Picture */
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_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, AV_CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, AV_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, AV_CODEC_ID_PCM_S16LE }, /* D-10 Mapping 50Mbps PAL Extended Template */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, AV_CODEC_ID_PCM_S16LE, NULL, 14, RawAWrap }, /* BWF */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, AV_CODEC_ID_MP2, NULL, 15 }, /* MPEG-ES */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, AV_CODEC_ID_PCM_S16LE, NULL, 13 }, /* D-10 Mapping 50Mbps PAL Extended Template */
{ { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0xff,0x4b,0x46,0x41,0x41,0x00,0x0d,0x4d,0x4F }, 14, AV_CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x03,0x04,0x02,0x02,0x02,0x03,0x03,0x01,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-2 AAC ADTS (legacy) */
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE },
};
static const MXFCodecUL mxf_data_essence_container_uls[] = {
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, 0 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0d,0x00,0x00 }, 16, AV_CODEC_ID_NONE, "vbi_smpte_436M", 11 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, AV_CODEC_ID_NONE, "vbi_vanc_smpte_436M", 11 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x13,0x01,0x01 }, 16, AV_CODEC_ID_TTML },
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE },
};
-static const char * const mxf_data_essence_descriptor[] = {
- "vbi_vanc_smpte_436M",
-};
+static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul)
+{
+ int val;
+ const MXFCodecUL *codec_ul;
+
+ codec_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul);
+ if (!codec_ul->uid[0])
+ codec_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
+ if (!codec_ul->uid[0])
+ codec_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
+ if (!codec_ul->uid[0] || !codec_ul->wrapping_indicator_pos)
+ return UnknownWrapped;
+
+ val = (*essence_container_ul)[codec_ul->wrapping_indicator_pos];
+ switch (codec_ul->wrapping_indicator_type) {
+ case RawVWrap:
+ val = val % 4;
+ break;
+ case RawAWrap:
+ if (val == 0x03 || val == 0x04)
+ val -= 0x02;
+ break;
+ case D10D11Wrap:
+ if (val == 0x02)
+ val = 0x01;
+ break;
+ }
+ if (val == 0x01)
+ return FrameWrapped;
+ if (val == 0x02)
+ return ClipWrapped;
+ return UnknownWrapped;
+}
static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_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];
+ for (i = nb_segments = 0; i < mxf->metadata_sets_count; i++) {
+ if (mxf->metadata_sets[i]->type == IndexTableSegment) {
+ MXFIndexTableSegment *s = (MXFIndexTableSegment*)mxf->metadata_sets[i];
+ if (s->edit_unit_byte_count || s->nb_index_entries)
+ unsorted_segments[nb_segments++] = s;
+ else
+ av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n",
+ s->index_sid, s->index_start_position);
+ }
+ }
+
+ if (!nb_segments) {
+ av_freep(sorted_segments);
+ av_free(unsorted_segments);
+ return AVERROR_INVALIDDATA;
+ }
*nb_sorted_segments = 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)
+static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t offset, int64_t *offset_out, MXFPartition **partition_out)
{
MXFPartition *last_p = NULL;
int a, b, m, m0;
if (last_p && (!last_p->essence_length || last_p->essence_length > (offset - last_p->body_offset))) {
*offset_out = last_p->essence_offset + (offset - last_p->body_offset);
+ if (partition_out)
+ *partition_out = last_p;
return 0;
}
*/
static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid)
{
- int x;
- int64_t ret = 0;
-
- for (x = 0; x < mxf->partitions_count; x++) {
+ for (int x = mxf->partitions_count - 1; x >= 0; x--) {
MXFPartition *p = &mxf->partitions[x];
if (p->body_sid != body_sid)
if (!p->essence_length)
return 0;
- ret = p->essence_offset + p->essence_length;
+ return p->essence_offset + p->essence_length;
}
- return ret;
+ return 0;
}
/* 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)
+static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, AVRational edit_rate, int64_t *edit_unit_out, int64_t *offset_out, MXFPartition **partition_out, int nag)
{
int i;
int64_t offset_temp = 0;
+ edit_unit = av_rescale_q(edit_unit, index_table->segments[0]->index_edit_rate, edit_rate);
+
for (i = 0; i < index_table->nb_segments; i++) {
MXFIndexTableSegment *s = index_table->segments[i];
if (s->edit_unit_byte_count)
offset_temp += s->edit_unit_byte_count * index;
- else if (s->nb_index_entries) {
+ else {
if (s->nb_index_entries == 2 * s->index_duration + 1)
index *= 2; /* Avid index */
}
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 (edit_unit_out)
- *edit_unit_out = edit_unit;
+ *edit_unit_out = av_rescale_q(edit_unit, edit_rate, s->index_edit_rate);
- return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out);
+ return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out, partition_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;
return 0; /* no TemporalOffsets */
}
+ if (s->index_duration > INT_MAX - index_table->nb_ptses) {
+ index_table->nb_ptses = 0;
+ av_log(mxf->fc, AV_LOG_ERROR, "ignoring IndexSID %d, duration is too large\n", s->index_sid);
+ return 0;
+ }
+
index_table->nb_ptses += s->index_duration;
}
/* fix zero IndexDurations */
for (k = 0; k < t->nb_segments; k++) {
+ if (!t->segments[k]->index_edit_rate.num || !t->segments[k]->index_edit_rate.den) {
+ av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has invalid IndexEditRate\n",
+ t->index_sid, k);
+ if (mxf_track)
+ t->segments[k]->index_edit_rate = mxf_track->edit_rate;
+ }
+
if (t->segments[k]->index_duration)
continue;
static int mxf_parse_package_comments(MXFContext *mxf, AVDictionary **pm, MXFPackage *package)
{
MXFTaggedValue *tag;
- int size, i;
+ int i;
char *key = NULL;
for (i = 0; i < package->comment_count; i++) {
if (!tag || !tag->name || !tag->value)
continue;
- size = strlen(tag->name) + 8 + 1;
- key = av_mallocz(size);
+ key = av_asprintf("comment_%s", tag->name);
if (!key)
return AVERROR(ENOMEM);
- snprintf(key, size, "comment_%s", tag->name);
av_dict_set(pm, key, tag->value, AV_DICT_DONT_STRDUP_KEY);
}
return 0;
continue;
}
- if (physical_track->edit_rate.num <= 0 ||
- physical_track->edit_rate.den <= 0) {
- av_log(mxf->fc, AV_LOG_WARNING,
- "Invalid edit rate (%d/%d) found on structural"
- " component #%d, defaulting to 25/1\n",
- physical_track->edit_rate.num,
- physical_track->edit_rate.den, i);
- physical_track->edit_rate = (AVRational){25, 1};
- }
+ if (physical_track->edit_rate.num <= 0 ||
+ physical_track->edit_rate.den <= 0) {
+ av_log(mxf->fc, AV_LOG_WARNING,
+ "Invalid edit rate (%d/%d) found on structural"
+ " component #%d, defaulting to 25/1\n",
+ physical_track->edit_rate.num,
+ physical_track->edit_rate.den, i);
+ physical_track->edit_rate = (AVRational){25, 1};
+ }
for (k = 0; k < physical_track->sequence->structural_components_count; k++) {
if (!(mxf_tc = mxf_resolve_timecode_component(mxf, &physical_track->sequence->structural_components_refs[k])))
return 0;
}
+static enum AVColorRange mxf_get_color_range(MXFContext *mxf, MXFDescriptor *descriptor)
+{
+ if (descriptor->black_ref_level || descriptor->white_ref_level || descriptor->color_range) {
+ /* CDCI range metadata */
+ if (!descriptor->component_depth)
+ return AVCOL_RANGE_UNSPECIFIED;
+ if (descriptor->black_ref_level == 0 &&
+ descriptor->white_ref_level == ((1<<descriptor->component_depth) - 1) &&
+ (descriptor->color_range == (1<<descriptor->component_depth) ||
+ descriptor->color_range == ((1<<descriptor->component_depth) - 1)))
+ return AVCOL_RANGE_JPEG;
+ if (descriptor->component_depth >= 8 &&
+ descriptor->black_ref_level == (1 <<(descriptor->component_depth - 4)) &&
+ descriptor->white_ref_level == (235<<(descriptor->component_depth - 8)) &&
+ descriptor->color_range == ((14<<(descriptor->component_depth - 4)) + 1))
+ return AVCOL_RANGE_MPEG;
+ avpriv_request_sample(mxf->fc, "Unrecognized CDCI color range (color diff range %d, b %d, w %d, depth %d)",
+ descriptor->color_range, descriptor->black_ref_level,
+ descriptor->white_ref_level, descriptor->component_depth);
+ }
+
+ return AVCOL_RANGE_UNSPECIFIED;
+}
+
static int mxf_parse_structural_metadata(MXFContext *mxf)
{
MXFPackage *material_package = NULL;
MXFEssenceContainerData *essence_data;
if (!(essence_data = mxf_resolve_strong_ref(mxf, &mxf->essence_container_data_refs[k], EssenceContainerData))) {
- av_log(mxf, AV_LOG_TRACE, "could not resolve essence container data strong ref\n");
+ av_log(mxf->fc, AV_LOG_TRACE, "could not resolve essence container data strong ref\n");
continue;
}
if (!memcmp(component->source_package_ul, essence_data->package_ul, sizeof(UID)) && !memcmp(component->source_package_uid, essence_data->package_uid, sizeof(UID))) {
PRINT_KEY(mxf->fc, "essence codec ul", descriptor->essence_codec_ul);
PRINT_KEY(mxf->fc, "essence container ul", descriptor->essence_container_ul);
essence_container_ul = &descriptor->essence_container_ul;
+ source_track->wrapping = (mxf->op == OPAtom) ? ClipWrapped : mxf_get_wrapping_kind(essence_container_ul);
+ if (source_track->wrapping == UnknownWrapped)
+ av_log(mxf->fc, AV_LOG_INFO, "wrapping of stream %d is unknown\n", st->index);
/* HACK: replacing the original key with mxf_encrypted_essence_container
* is not allowed according to s429-6, try to find correct information anyway */
if (IS_KLV_KEY(essence_container_ul, mxf_encrypted_essence_container)) {
default:
av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout);
}
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_PRORES) {
+ switch (descriptor->essence_codec_ul[14]) {
+ case 1: st->codecpar->codec_tag = MKTAG('a','p','c','o'); break;
+ case 2: st->codecpar->codec_tag = MKTAG('a','p','c','s'); break;
+ case 3: st->codecpar->codec_tag = MKTAG('a','p','c','n'); break;
+ case 4: st->codecpar->codec_tag = MKTAG('a','p','c','h'); break;
+ case 5: st->codecpar->codec_tag = MKTAG('a','p','4','h'); break;
+ case 6: st->codecpar->codec_tag = MKTAG('a','p','4','x'); break;
+ }
+ }
+
if (st->codecpar->codec_id == AV_CODEC_ID_RAWVIDEO) {
st->codecpar->format = descriptor->pix_fmt;
if (st->codecpar->format == AV_PIX_FMT_NONE) {
av_dict_set_int(&st->metadata, "source_track_origin", source_track->sequence->origin, 0);
}
if (descriptor->aspect_ratio.num && descriptor->aspect_ratio.den)
- st->display_aspect_ratio = descriptor->aspect_ratio;
+ st->internal->display_aspect_ratio = descriptor->aspect_ratio;
+ st->codecpar->color_range = mxf_get_color_range(mxf, descriptor);
+ st->codecpar->color_primaries = mxf_get_codec_ul(ff_mxf_color_primaries_uls, &descriptor->color_primaries_ul)->id;
+ st->codecpar->color_trc = mxf_get_codec_ul(ff_mxf_color_trc_uls, &descriptor->color_trc_ul)->id;
+ st->codecpar->color_space = mxf_get_codec_ul(ff_mxf_color_space_uls, &descriptor->color_space_ul)->id;
+ if (descriptor->mastering) {
+ ret = av_stream_add_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ (uint8_t *)descriptor->mastering,
+ sizeof(*descriptor->mastering));
+ if (ret < 0)
+ goto fail_and_free;
+ descriptor->mastering = NULL;
+ }
+ if (descriptor->coll) {
+ ret = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+ (uint8_t *)descriptor->coll,
+ descriptor->coll_size);
+ if (ret < 0)
+ goto fail_and_free;
+ descriptor->coll = NULL;
+ }
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
/* Only overwrite existing codec ID if it is unset or A-law, which is the default according to SMPTE RP 224. */
if (st->codecpar->codec_id == AV_CODEC_ID_NONE || (st->codecpar->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE))
st->codecpar->codec_id = (enum AVCodecID)container_ul->id;
st->codecpar->channels = descriptor->channels;
- st->codecpar->bits_per_coded_sample = descriptor->bits_per_sample;
if (descriptor->sample_rate.den > 0) {
st->codecpar->sample_rate = descriptor->sample_rate.num / descriptor->sample_rate.den;
} else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) {
st->need_parsing = AVSTREAM_PARSE_FULL;
}
+ st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
- int codec_id = mxf_get_codec_ul(mxf_data_essence_container_uls,
- essence_container_ul)->id;
- if (codec_id >= 0 &&
- codec_id < FF_ARRAY_ELEMS(mxf_data_essence_descriptor)) {
- av_dict_set(&st->metadata, "data_type",
- mxf_data_essence_descriptor[codec_id], 0);
+ enum AVMediaType type;
+ container_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
+ st->codecpar->codec_id = container_ul->id;
+ type = avcodec_get_type(st->codecpar->codec_id);
+ if (type == AVMEDIA_TYPE_SUBTITLE)
+ st->codecpar->codec_type = type;
+ if (container_ul->desc)
+ av_dict_set(&st->metadata, "data_type", container_ul->desc, 0);
+ if (mxf->eia608_extract &&
+ !strcmp(container_ul->desc, "vbi_vanc_smpte_436M")) {
+ st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->codec_id = AV_CODEC_ID_EIA_608;
}
}
if (descriptor->extradata) {
if (ret < 0)
return ret;
}
- if (st->codecpar->codec_type != AVMEDIA_TYPE_DATA && (*essence_container_ul)[15] > 0x01) {
+ if (st->codecpar->codec_type != AVMEDIA_TYPE_DATA && source_track->wrapping != FrameWrapped) {
/* TODO: decode timestamps */
st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;
}
}
+ for (int i = 0; i < mxf->fc->nb_streams; i++) {
+ MXFTrack *track1 = mxf->fc->streams[i]->priv_data;
+ if (track1 && track1->body_sid) {
+ for (int j = i + 1; j < mxf->fc->nb_streams; j++) {
+ MXFTrack *track2 = mxf->fc->streams[j]->priv_data;
+ if (track2 && track1->body_sid == track2->body_sid && track1->wrapping != track2->wrapping) {
+ if (track1->wrapping == UnknownWrapped)
+ track1->wrapping = track2->wrapping;
+ else if (track2->wrapping == UnknownWrapped)
+ track2->wrapping = track1->wrapping;
+ else
+ av_log(mxf->fc, AV_LOG_ERROR, "stream %d and stream %d have the same BodySID (%d) "
+ "with different wrapping\n", i, j, track1->body_sid);
+ }
+ }
+ }
+ }
+
ret = 0;
fail_and_free:
return ret;
static int64_t mxf_timestamp_to_int64(uint64_t timestamp)
{
struct tm time = { 0 };
+ int msecs;
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);
+ msecs = (timestamp & 0xFF) * 4;
- /* msvcrt versions of strftime calls the invalid parameter handler
- * (aborting the process if one isn't set) if the parameters are out
- * of range. */
+ /* Clip values for legacy reasons. Maybe we should return error instead? */
time.tm_mon = av_clip(time.tm_mon, 0, 11);
time.tm_mday = av_clip(time.tm_mday, 1, 31);
time.tm_hour = av_clip(time.tm_hour, 0, 23);
time.tm_min = av_clip(time.tm_min, 0, 59);
time.tm_sec = av_clip(time.tm_sec, 0, 59);
+ msecs = av_clip(msecs, 0, 999);
- return (int64_t)av_timegm(&time) * 1000000;
+ return (int64_t)av_timegm(&time) * 1000000 + msecs * 1000;
}
#define SET_STR_METADATA(pb, name, str) do { \
#define SET_TS_METADATA(pb, name, var, str) do { \
var = avio_rb64(pb); \
- if ((ret = avpriv_dict_set_timestamp(&s->metadata, name, mxf_timestamp_to_int64(var)) < 0)) \
+ if (var && (ret = avpriv_dict_set_timestamp(&s->metadata, name, mxf_timestamp_to_int64(var))) < 0) \
return ret; \
} while (0)
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Wave */
{ { 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,0x51,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2VideoDescriptor */
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5b,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* VBI - SMPTE 436M */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5c,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* VANC/VBI - SMPTE 436M */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x64,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* DC Timed Text Descriptor */
{ { 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 },
static int mxf_metadataset_init(MXFMetadataSet *ctx, enum MXFMetadataSetType type)
{
+ ctx->type = type;
switch (type){
case MultipleDescriptor:
case Descriptor:
if (!ctx)
return AVERROR(ENOMEM);
- mxf_metadataset_init(ctx, type);
+ if (ctx_size)
+ mxf_metadataset_init(ctx, type);
while (avio_tell(pb) + 4 < klv_end && !avio_feof(pb)) {
int ret;
int tag = avio_rb16(pb);
if (ctx_size && tag == 0x3C0A) {
avio_read(pb, ctx->uid, 16);
} else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0) {
- mxf_free_metadataset(&ctx, !!ctx_size);
+ if (ctx_size)
+ mxf_free_metadataset(&ctx, 1);
return ret;
}
* it extending past the end of the KLV though (zzuf5.mxf). */
if (avio_tell(pb) > klv_end) {
if (ctx_size) {
- ctx->type = type;
- mxf_free_metadataset(&ctx, !!ctx_size);
+ mxf_free_metadataset(&ctx, 1);
}
av_log(mxf->fc, AV_LOG_ERROR,
} else if (avio_tell(pb) <= next) /* only seek forward, else this can loop for a long time */
avio_seek(pb, next, SEEK_SET);
}
- if (ctx_size) ctx->type = type;
- return ctx_size ? mxf_add_metadata_set(mxf, ctx) : 0;
+ return ctx_size ? mxf_add_metadata_set(mxf, &ctx) : 0;
}
/**
return mxf->parsing_backward ? mxf_seek_to_previous_partition(mxf) : 1;
}
+static MXFWrappingScheme mxf_get_wrapping_by_body_sid(AVFormatContext *s, int body_sid)
+{
+ for (int i = 0; i < s->nb_streams; i++) {
+ MXFTrack *track = s->streams[i]->priv_data;
+ if (track && track->body_sid == body_sid && track->wrapping != UnknownWrapped)
+ return track->wrapping;
+ }
+ return UnknownWrapped;
+}
+
/**
* Figures out the proper offset and length of the essence container in each partition
*/
-static void mxf_compute_essence_containers(MXFContext *mxf)
+static void mxf_compute_essence_containers(AVFormatContext *s)
{
+ MXFContext *mxf = s->priv_data;
int x;
- /* everything is already correct */
- if (mxf->op == OPAtom)
- return;
-
for (x = 0; x < mxf->partitions_count; x++) {
MXFPartition *p = &mxf->partitions[x];
+ MXFWrappingScheme wrapping;
if (!p->body_sid)
continue; /* BodySID == 0 -> no essence */
- if (x >= mxf->partitions_count - 1)
- break; /* FooterPartition - can't compute length (and we don't need to) */
+ /* for clip wrapped essences we point essence_offset after the KL (usually klv.offset + 20 or 25)
+ * otherwise we point essence_offset at the key of the first essence KLV.
+ */
- /* essence container spans to the next partition */
- p->essence_length = mxf->partitions[x+1].this_partition - p->essence_offset;
+ wrapping = (mxf->op == OPAtom) ? ClipWrapped : mxf_get_wrapping_by_body_sid(s, p->body_sid);
- if (p->essence_length < 0) {
- /* next ThisPartition < essence_offset */
- p->essence_length = 0;
- av_log(mxf->fc, AV_LOG_ERROR,
- "partition %i: bad ThisPartition = %"PRIX64"\n",
- x+1, mxf->partitions[x+1].this_partition);
+ if (wrapping == ClipWrapped) {
+ p->essence_offset = p->first_essence_klv.next_klv - p->first_essence_klv.length;
+ p->essence_length = p->first_essence_klv.length;
+ } else {
+ p->essence_offset = p->first_essence_klv.offset;
+
+ /* essence container spans to the next partition */
+ if (x < mxf->partitions_count - 1)
+ 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 = %"PRIX64"\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 int is_pcm(enum AVCodecID codec_id)
{
/* we only care about "normal" PCM codecs until we get samples */
return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
}
-static AVStream* mxf_get_opatom_stream(MXFContext *mxf)
+static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
{
int i;
-
- if (mxf->op != OPAtom)
- return NULL;
-
- for (i = 0; i < mxf->fc->nb_streams; i++) {
- if (mxf->fc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_DATA)
- continue;
- return mxf->fc->streams[i];
- }
+ for (i = 0; i < mxf->nb_index_tables; i++)
+ if (mxf->index_tables[i].index_sid == index_sid)
+ return &mxf->index_tables[i];
return NULL;
}
* very small (2, 4..). In those cases we should read more than one
* sample per call to mxf_read_packet().
*/
-static void mxf_handle_small_eubc(AVFormatContext *s)
+static void mxf_compute_edit_units_per_packet(MXFContext *mxf, AVStream *st)
{
- MXFContext *mxf = s->priv_data;
- MXFTrack *track;
+ MXFTrack *track = st->priv_data;
+ MXFIndexTable *t;
- /* assuming non-OPAtom == frame wrapped
- * no sane writer would wrap 2 byte PCM packets with 20 byte headers.. */
- AVStream *st = mxf_get_opatom_stream(mxf);
- if (!st)
+ if (!track)
return;
+ track->edit_units_per_packet = 1;
+ if (track->wrapping != ClipWrapped)
+ return;
+
+ t = mxf_find_index_table(mxf, track->index_sid);
/* expect PCM with exactly one index table segment and a small (< 32) EUBC */
if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO ||
!is_pcm(st->codecpar->codec_id) ||
- mxf->nb_index_tables != 1 ||
- mxf->index_tables[0].nb_segments != 1 ||
- mxf->index_tables[0].segments[0]->edit_unit_byte_count >= 32)
+ !t ||
+ t->nb_segments != 1 ||
+ t->segments[0]->edit_unit_byte_count >= 32)
return;
/* arbitrarily default to 48 kHz PAL audio frame size */
/* TODO: We could compute this from the ratio between the audio
* and video edit rates for 48 kHz NTSC we could use the
* 1802-1802-1802-1802-1801 pattern. */
- track = st->priv_data;
- mxf->edit_units_per_packet = FFMAX(1, track->edit_rate.num / track->edit_rate.den / 25);
+ track->edit_units_per_packet = FFMAX(1, track->edit_rate.num / track->edit_rate.den / 25);
}
/**
- * Deal with the case where OPAtom files does not have any IndexTableSegments.
+ * Deal with the case where ClipWrapped essences does not have any IndexTableSegments.
*/
-static int mxf_handle_missing_index_segment(MXFContext *mxf)
+static int mxf_handle_missing_index_segment(MXFContext *mxf, AVStream *st)
{
- AVFormatContext *s = mxf->fc;
- AVStream *st = NULL;
+ MXFTrack *track = st->priv_data;
MXFIndexTableSegment *segment = NULL;
MXFPartition *p = NULL;
int essence_partition_count = 0;
+ int edit_unit_byte_count = 0;
int i, ret;
- st = mxf_get_opatom_stream(mxf);
- if (!st)
- return 0;
-
- /* TODO: support raw video without an index if they exist */
- if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO || !is_pcm(st->codecpar->codec_id))
+ if (!track || track->wrapping != ClipWrapped)
return 0;
- /* check if file already has a IndexTableSegment */
+ /* check if track already has an IndexTableSegment */
for (i = 0; i < mxf->metadata_sets_count; i++) {
- if (mxf->metadata_sets[i]->type == IndexTableSegment)
- return 0;
+ if (mxf->metadata_sets[i]->type == IndexTableSegment) {
+ MXFIndexTableSegment *s = (MXFIndexTableSegment*)mxf->metadata_sets[i];
+ if (s->body_sid == track->body_sid)
+ return 0;
+ }
}
/* find the essence partition */
for (i = 0; i < mxf->partitions_count; i++) {
/* BodySID == 0 -> no essence */
- if (!mxf->partitions[i].body_sid)
+ if (mxf->partitions[i].body_sid != track->body_sid)
continue;
p = &mxf->partitions[i];
if (essence_partition_count != 1)
return 0;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && is_pcm(st->codecpar->codec_id)) {
+ edit_unit_byte_count = (av_get_bits_per_sample(st->codecpar->codec_id) * st->codecpar->channels) >> 3;
+ } else if (st->duration > 0 && p->first_essence_klv.length > 0 && p->first_essence_klv.length % st->duration == 0) {
+ edit_unit_byte_count = p->first_essence_klv.length / st->duration;
+ }
+
+ if (edit_unit_byte_count <= 0)
+ return 0;
+
+ av_log(mxf->fc, AV_LOG_WARNING, "guessing index for stream %d using edit unit byte count %d\n", st->index, edit_unit_byte_count);
+
if (!(segment = av_mallocz(sizeof(*segment))))
return AVERROR(ENOMEM);
- if ((ret = mxf_add_metadata_set(mxf, segment))) {
- mxf_free_metadataset((MXFMetadataSet**)&segment, 1);
+ if ((ret = mxf_add_metadata_set(mxf, (MXFMetadataSet**)&segment)))
return ret;
- }
+
+ /* Make sure we have nonzero unique index_sid, body_sid will be ok, because
+ * using the same SID for index is forbidden in MXF. */
+ if (!track->index_sid)
+ track->index_sid = track->body_sid;
segment->type = IndexTableSegment;
/* stream will be treated as small EditUnitByteCount */
- segment->edit_unit_byte_count = (av_get_bits_per_sample(st->codecpar->codec_id) * st->codecpar->channels) >> 3;
+ segment->edit_unit_byte_count = edit_unit_byte_count;
segment->index_start_position = 0;
- segment->index_duration = s->streams[0]->duration;
- segment->index_sid = p->index_sid;
+ segment->index_duration = st->duration;
+ segment->index_edit_rate = av_inv_q(st->time_base);
+ segment->index_sid = track->index_sid;
segment->body_sid = p->body_sid;
return 0;
}
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)
+ !IS_KLV_KEY(klv.key, mxf_random_index_pack_key))
+ goto end;
+ if (klv.next_klv != file_size || klv.length <= 4 || (klv.length - 4) % 12) {
+ av_log(s, AV_LOG_WARNING, "Invalid RIP KLV length\n");
goto end;
+ }
avio_skip(s->pb, klv.length - 12);
mxf->footer_partition = avio_rb64(s->pb);
int ret;
mxf->last_forward_tell = INT64_MAX;
- mxf->edit_units_per_packet = 1;
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");
+ //goto fail should not be needed as no metadata sets will have been parsed yet
return AVERROR_INVALIDDATA;
}
avio_seek(s->pb, -14, SEEK_CUR);
av_log(s, AV_LOG_TRACE, "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_canopus_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_avid_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_system_item_key_cp) ||
IS_KLV_KEY(klv.key, mxf_system_item_key_gc)) {
if (!mxf->current_partition) {
av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n");
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
- if (!mxf->current_partition->essence_offset) {
- /* 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 =
- mxf->current_partition->this_partition +
- round_to_kag(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) */
- if (IS_KLV_KEY(klv.key, mxf_system_item_key_cp) || IS_KLV_KEY(klv.key, mxf_system_item_key_gc))
- mxf->current_partition->essence_offset = klv.offset;
- else
- mxf->current_partition->essence_offset = op1a_essence_offset;
- }
- }
+ if (!mxf->current_partition->first_essence_klv.offset)
+ mxf->current_partition->first_essence_klv = klv;
if (!essence_offset)
essence_offset = klv.offset;
}
avio_seek(s->pb, essence_offset, SEEK_SET);
- mxf_compute_essence_containers(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)
goto fail;
- mxf_handle_missing_index_segment(mxf);
+ for (int i = 0; i < s->nb_streams; i++)
+ mxf_handle_missing_index_segment(mxf, s->streams[i]);
+
if ((ret = mxf_compute_index_tables(mxf)) < 0)
goto fail;
/* 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) {
+ } else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom && (s->error_recognition & AV_EF_EXPLODE)) {
av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
- mxf_handle_small_eubc(s);
+ mxf_compute_essence_containers(s);
+
+ for (int i = 0; i < s->nb_streams; i++)
+ mxf_compute_edit_units_per_packet(mxf, s->streams[i]);
return 0;
fail:
return ret;
}
-static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
-{
- int i;
- for (i = 0; i < mxf->nb_index_tables; i++)
- if (mxf->index_tables[i].index_sid == index_sid)
- return &mxf->index_tables[i];
- return NULL;
-}
-
/* Get the edit unit of the next packet from current_offset in a track. The returned edit unit can be original_duration as well! */
static int mxf_get_next_track_edit_unit(MXFContext *mxf, MXFTrack *track, int64_t current_offset, int64_t *edit_unit_out)
{
while (b - a > 1) {
m = (a + b) >> 1;
- if (mxf_edit_unit_absolute_offset(mxf, t, m, NULL, &offset, 0) < 0)
+ if (mxf_edit_unit_absolute_offset(mxf, t, m, track->edit_rate, NULL, &offset, NULL, 0) < 0)
return -1;
if (offset < current_offset)
a = m;
return 0;
}
-/**
- * Sets mxf->current_edit_unit based on what offset we're currently at.
- * @return next_ofs if OK, <0 on error
- */
-static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset)
-{
- int64_t last_ofs = -1, next_ofs = -1;
- MXFIndexTable *t = &mxf->index_tables[0];
-
- /* this is called from the OP1a demuxing logic, which means there
- * may be no index tables */
- if (mxf->nb_index_tables <= 0)
- return -1;
-
- /* 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)
- return -2;
-
- if (next_ofs <= last_ofs) {
- /* large next_ofs didn't change or current_edit_unit wrapped
- * around this fixes the infinite loop on zzuf3.mxf */
- av_log(mxf->fc, AV_LOG_ERROR,
- "next_ofs didn't change. not deriving packet timestamps\n");
- return -1;
- }
-
- if (next_ofs > current_offset)
- break;
-
- last_ofs = next_ofs;
- mxf->current_edit_unit++;
- }
-
- /* 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)
+static int64_t mxf_compute_sample_count(MXFContext *mxf, AVStream *st,
+ int64_t edit_unit)
{
- 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) {
+ // For non-audio sample_count equals current edit unit
+ if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
+ return edit_unit;
+
+ if ((sample_rate.num / sample_rate.den) == 48000) {
+ return av_rescale_q(edit_unit, sample_rate, track->edit_rate);
+ } else {
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,
+ st->index, time_base.num, time_base.den,
sample_rate.num, sample_rate.den);
- return 0;
+ return av_rescale_q(edit_unit, sample_rate, track->edit_rate);
}
+}
+
+/**
+ * Make sure track->sample_count is correct based on what offset we're currently at.
+ * Also determine the next edit unit (or packet) offset.
+ * @return next_ofs if OK, <0 on error
+ */
+static int64_t mxf_set_current_edit_unit(MXFContext *mxf, AVStream *st, int64_t current_offset, int resync)
+{
+ int64_t next_ofs = -1;
+ MXFTrack *track = st->priv_data;
+ int64_t edit_unit = av_rescale_q(track->sample_count, st->time_base, av_inv_q(track->edit_rate));
+ int64_t new_edit_unit;
+ MXFIndexTable *t = mxf_find_index_table(mxf, track->index_sid);
+
+ if (!t || track->wrapping == UnknownWrapped)
+ return -1;
- while (spf->samples_per_frame[size]) {
- total += spf->samples_per_frame[size];
- size++;
+ if (mxf_edit_unit_absolute_offset(mxf, t, edit_unit + track->edit_units_per_packet, track->edit_rate, NULL, &next_ofs, NULL, 0) < 0 &&
+ (next_ofs = mxf_essence_container_end(mxf, t->body_sid)) <= 0) {
+ av_log(mxf->fc, AV_LOG_ERROR, "unable to compute the size of the last packet\n");
+ return -1;
}
- av_assert2(size);
+ /* check if the next edit unit offset (next_ofs) starts ahead of current_offset */
+ if (next_ofs > current_offset)
+ return next_ofs;
- *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];
+ if (!resync) {
+ av_log(mxf->fc, AV_LOG_ERROR, "cannot find current edit unit for stream %d, invalid index?\n", st->index);
+ return -1;
}
- return 0;
+ if (mxf_get_next_track_edit_unit(mxf, track, current_offset + 1, &new_edit_unit) < 0 || new_edit_unit <= 0) {
+ av_log(mxf->fc, AV_LOG_ERROR, "failed to find next track edit unit in stream %d\n", st->index);
+ return -1;
+ }
+
+ new_edit_unit--;
+ track->sample_count = mxf_compute_sample_count(mxf, st, new_edit_unit);
+ av_log(mxf->fc, AV_LOG_WARNING, "edit unit sync lost on stream %d, jumping from %"PRId64" to %"PRId64"\n", st->index, edit_unit, new_edit_unit);
+
+ return mxf_set_current_edit_unit(mxf, st, current_offset, 0);
}
static int mxf_set_audio_pts(MXFContext *mxf, AVCodecParameters *par,
AVPacket *pkt)
{
- MXFTrack *track = mxf->fc->streams[pkt->stream_index]->priv_data;
+ AVStream *st = mxf->fc->streams[pkt->stream_index];
+ MXFTrack *track = st->priv_data;
int64_t bits_per_sample = par->bits_per_coded_sample;
if (!bits_per_sample)
if ( par->channels <= 0
|| bits_per_sample <= 0
|| par->channels * (int64_t)bits_per_sample < 8)
- return AVERROR(EINVAL);
- track->sample_count += pkt->size / (par->channels * (int64_t)bits_per_sample / 8);
+ track->sample_count = mxf_compute_sample_count(mxf, st, av_rescale_q(track->sample_count, st->time_base, av_inv_q(track->edit_rate)) + 1);
+ else
+ track->sample_count += pkt->size / (par->channels * (int64_t)bits_per_sample / 8);
+
return 0;
}
-static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt, int64_t next_ofs)
+static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt)
{
AVCodecParameters *par = st->codecpar;
MXFTrack *track = st->priv_data;
- if (par->codec_type == AVMEDIA_TYPE_VIDEO && (next_ofs >= 0 || next_ofs == -2 && st->duration == mxf->current_edit_unit + 1)) {
- /* mxf->current_edit_unit good - see if we have an
- * index table to derive timestamps from */
- MXFIndexTable *t = &mxf->index_tables[0];
+ if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
+ /* see if we have an index table to derive timestamps from */
+ MXFIndexTable *t = mxf_find_index_table(mxf, track->index_sid);
- 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 && track->intra_only) {
+ if (t && track->sample_count < t->nb_ptses) {
+ pkt->dts = track->sample_count + t->first_dts;
+ pkt->pts = t->ptses[track->sample_count];
+ } 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) */
- pkt->pts = mxf->current_edit_unit;
+ pkt->pts = track->sample_count;
}
+ track->sample_count++;
} else if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
int ret = mxf_set_audio_pts(mxf, par, pkt);
if (ret < 0)
return ret;
+ } else if (track) {
+ pkt->dts = pkt->pts = track->sample_count;
+ pkt->duration = 1;
+ track->sample_count++;
}
return 0;
}
-static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
+static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
KLVPacket klv;
MXFContext *mxf = s->priv_data;
int ret;
- while ((ret = klv_read_packet(&klv, s->pb)) == 0) {
- PRINT_KEY(s, "read packet", klv.key);
- av_log(s, AV_LOG_TRACE, "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 ret;
+ while (1) {
+ int64_t max_data_size;
+ int64_t pos = avio_tell(s->pb);
+
+ if (pos < mxf->current_klv_data.next_klv - mxf->current_klv_data.length || pos >= mxf->current_klv_data.next_klv) {
+ mxf->current_klv_data = (KLVPacket){{0}};
+ ret = klv_read_packet(&klv, s->pb);
+ if (ret < 0)
+ break;
+ max_data_size = klv.length;
+ pos = klv.next_klv - klv.length;
+ PRINT_KEY(s, "read packet", klv.key);
+ av_log(s, AV_LOG_TRACE, "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 ret;
+ }
+ return 0;
}
- return 0;
+ } else {
+ klv = mxf->current_klv_data;
+ max_data_size = klv.next_klv - pos;
}
if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_canopus_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
- int body_sid = find_body_sid_by_offset(mxf, klv.offset);
+ int body_sid = find_body_sid_by_absolute_offset(mxf, klv.offset);
int index = mxf_get_stream_index(s, &klv, body_sid);
- int64_t next_ofs, next_klv;
+ int64_t next_ofs;
AVStream *st;
+ MXFTrack *track;
if (index < 0) {
av_log(s, AV_LOG_ERROR,
}
st = s->streams[index];
+ track = st->priv_data;
if (s->streams[index]->discard == AVDISCARD_ALL)
goto skip;
- next_klv = avio_tell(s->pb) + klv.length;
- 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) */
- avpriv_request_sample(s,
- "OPAtom misinterpreted as OP1a? "
- "KLV for edit unit %"PRId64" extending into "
- "next edit unit",
- mxf->current_edit_unit);
- klv.length = next_ofs - avio_tell(s->pb);
+ next_ofs = mxf_set_current_edit_unit(mxf, st, pos, 1);
+
+ if (track->wrapping != FrameWrapped) {
+ int64_t size;
+
+ if (next_ofs <= 0) {
+ // If we have no way to packetize the data, then return it in chunks...
+ if (klv.next_klv - klv.length == pos && max_data_size > MXF_MAX_CHUNK_SIZE) {
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ avpriv_request_sample(s, "Huge KLV without proper index in non-frame wrapped essence");
+ }
+ size = FFMIN(max_data_size, MXF_MAX_CHUNK_SIZE);
+ } else {
+ if ((size = next_ofs - pos) <= 0) {
+ av_log(s, AV_LOG_ERROR, "bad size: %"PRId64"\n", size);
+ mxf->current_klv_data = (KLVPacket){{0}};
+ return AVERROR_INVALIDDATA;
+ }
+ // We must not overread, because the next edit unit might be in another KLV
+ if (size > max_data_size)
+ size = max_data_size;
+ }
+
+ mxf->current_klv_data = klv;
+ klv.offset = pos;
+ klv.length = size;
+ klv.next_klv = klv.offset + klv.length;
}
/* check for 8 channels AES3 element */
pkt, klv.length);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
+ mxf->current_klv_data = (KLVPacket){{0}};
+ return ret;
+ }
+ } else if (mxf->eia608_extract &&
+ s->streams[index]->codecpar->codec_id == AV_CODEC_ID_EIA_608) {
+ ret = mxf_get_eia608_packet(s, s->streams[index], pkt, klv.length);
+ if (ret < 0) {
+ mxf->current_klv_data = (KLVPacket){{0}};
return ret;
}
} else {
ret = av_get_packet(s->pb, pkt, klv.length);
- if (ret < 0)
+ if (ret < 0) {
+ mxf->current_klv_data = (KLVPacket){{0}};
return ret;
+ }
}
pkt->stream_index = index;
pkt->pos = klv.offset;
- ret = mxf_set_pts(mxf, st, pkt, next_ofs);
- if (ret < 0)
+ ret = mxf_set_pts(mxf, st, pkt);
+ if (ret < 0) {
+ mxf->current_klv_data = (KLVPacket){{0}};
return ret;
+ }
/* seek for truncated packets */
- avio_seek(s->pb, next_klv, SEEK_SET);
+ avio_seek(s->pb, klv.next_klv, SEEK_SET);
return 0;
- } else
+ } else {
skip:
- avio_skip(s->pb, klv.length);
+ avio_skip(s->pb, max_data_size);
+ mxf->current_klv_data = (KLVPacket){{0}};
+ }
}
return avio_feof(s->pb) ? AVERROR_EOF : ret;
}
-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;
- MXFTrack *track;
- int edit_units;
-
- if (mxf->op != OPAtom)
- return mxf_read_packet_old(s, pkt);
-
- // If we have no streams then we basically are at EOF
- st = mxf_get_opatom_stream(mxf);
- if (!st)
- return AVERROR_EOF;
-
- track = st->priv_data;
-
- /* OPAtom - clip wrapped demuxing */
- /* NOTE: mxf_read_header() makes sure nb_index_tables > 0 for OPAtom */
- t = &mxf->index_tables[0];
-
- if (mxf->current_edit_unit >= track->original_duration)
- return AVERROR_EOF;
-
- edit_units = FFMIN(mxf->edit_units_per_packet, track->original_duration - mxf->current_edit_unit);
-
- 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 + edit_units, 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 ((size = av_get_packet(s->pb, pkt, size)) < 0)
- return size;
-
- pkt->stream_index = st->index;
-
- ret = mxf_set_pts(mxf, st, pkt, next_pos);
- if (ret < 0)
- return ret;
-
- mxf->current_edit_unit += edit_units;
-
- return 0;
-}
-
static int mxf_read_close(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
for (i = 0; i < mxf->metadata_sets_count; i++) {
mxf_free_metadataset(mxf->metadata_sets + i, 1);
}
+ mxf->metadata_sets_count = 0;
av_freep(&mxf->partitions);
av_freep(&mxf->metadata_sets);
av_freep(&mxf->aesc);
return 0;
}
-static int mxf_probe(AVProbeData *p) {
+static int mxf_probe(const AVProbeData *p) {
const uint8_t *bufp = p->buf;
const uint8_t *end = p->buf + p->buf_size;
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;
+ return bufp == p->buf ? AVPROBE_SCORE_MAX : AVPROBE_SCORE_MAX - 1;
bufp ++;
} else
bufp += 10;
MXFIndexTable *t;
MXFTrack *source_track = st->priv_data;
- if(st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ if (!source_track)
return 0;
/* if audio then truncate sample_time to EditRate */
return seekpos;
ff_update_cur_dts(s, st, sample_time);
- mxf->current_edit_unit = sample_time;
+ mxf->current_klv_data = (KLVPacket){{0}};
} else {
+ MXFPartition *partition;
+
t = &mxf->index_tables[0];
if (t->index_sid != source_track->index_sid) {
/* If the first index table does not belong to the stream, then find a stream which does belong to the index table */
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)
+ if (source_track->wrapping == UnknownWrapped)
+ av_log(mxf->fc, AV_LOG_WARNING, "attempted seek in an UnknownWrapped essence\n");
+
+ if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, source_track->edit_rate, &sample_time, &seekpos, &partition, 1)) < 0)
return ret;
ff_update_cur_dts(s, st, sample_time);
- mxf->current_edit_unit = sample_time;
+ if (source_track->wrapping == ClipWrapped) {
+ KLVPacket klv = partition->first_essence_klv;
+ if (seekpos < klv.next_klv - klv.length || seekpos >= klv.next_klv) {
+ av_log(mxf->fc, AV_LOG_ERROR, "attempted seek out of clip wrapped KLV\n");
+ return AVERROR_INVALIDDATA;
+ }
+ mxf->current_klv_data = klv;
+ } else {
+ mxf->current_klv_data = (KLVPacket){{0}};
+ }
avio_seek(s->pb, seekpos, SEEK_SET);
}
for (i = 0; i < s->nb_streams; i++) {
AVStream *cur_st = s->streams[i];
MXFTrack *cur_track = cur_st->priv_data;
- if (cur_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- int64_t track_edit_unit;
- if (st != cur_st && mxf_get_next_track_edit_unit(mxf, cur_track, seekpos, &track_edit_unit) >= 0) {
- cur_track->sample_count = av_rescale_q(track_edit_unit,
- av_inv_q(cur_track->edit_rate),
- cur_st->time_base);
- } else {
- uint64_t current_sample_count = 0;
- ret = mxf_compute_sample_count(mxf, i, ¤t_sample_count);
- if (ret < 0)
- return ret;
- cur_track->sample_count = current_sample_count;
- }
+ if (cur_track) {
+ int64_t track_edit_unit = sample_time;
+ if (st != cur_st)
+ mxf_get_next_track_edit_unit(mxf, cur_track, seekpos, &track_edit_unit);
+ cur_track->sample_count = mxf_compute_sample_count(mxf, cur_st, track_edit_unit);
}
}
return 0;
}
+static const AVOption options[] = {
+ { "eia608_extract", "extract eia 608 captions from s436m track",
+ offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
+ AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass demuxer_class = {
+ .class_name = "mxf",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEMUXER,
+};
+
AVInputFormat ff_mxf_demuxer = {
.name = "mxf",
.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"),
.read_packet = mxf_read_packet,
.read_close = mxf_read_close,
.read_seek = mxf_read_seek,
+ .priv_class = &demuxer_class,
};