X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmxfenc.c;h=b5f3aa52d7f5ba4e4bd24115bb9002792af2955c;hb=88297e80aa005afc4ebdfb4ab68e38a114439905;hp=1d36f447572fd24ee74e4e66229c79dc1a2e1650;hpb=b18783644c43feed12ce711e878348fe8446e8f5;p=ffmpeg diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 1d36f447572..b5f3aa52d7f 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -1,6 +1,7 @@ /* * MXF muxer * Copyright (c) 2008 GUCAS, Zhentan Feng + * Copyright (c) 2008 Baptiste Coudurier * * This file is part of FFmpeg. * @@ -31,64 +32,109 @@ //#define DEBUG +#include + +#include "libavutil/fifo.h" #include "mxf.h" +static const int NTSC_samples_per_frame[] = { 1602, 1601, 1602, 1601, 1602, 0 }; +static const int PAL_samples_per_frame[] = { 1920, 0 }; + +#define MXF_INDEX_CLUSTER_SIZE 4096 + +typedef struct { + AVFifoBuffer fifo; + unsigned fifo_size; ///< current fifo size allocated + uint64_t dts; ///< current dts + int sample_size; ///< size of one sample all channels included + const int *samples_per_frame; ///< must be 0 terminated + const int *samples; ///< current samples per frame, pointer to samples_per_frame +} AudioInterleaveContext; + typedef struct { int local_tag; UID uid; } MXFLocalTagPair; typedef struct { + uint8_t flags; + uint64_t offset; + unsigned slice_offset[17]; // one video, 16 audio +} MXFIndexEntry; + +typedef struct { + AudioInterleaveContext aic; UID track_essence_element_key; - int index; //<<< index in mxf_essence_container_uls table + int index; ///< index in mxf_essence_container_uls table const UID *codec_ul; int64_t duration; + int order; ///< interleaving order if dts are equal + int interlaced; ///< wether picture is interlaced + int temporal_reordering; } MXFStreamContext; typedef struct { UID container_ul; UID element_ul; UID codec_ul; - enum CodecID id; void (*write_desc)(); } MXFContainerEssenceEntry; +static const struct { + enum CodecID id; + int index; +} mxf_essence_mappings[] = { + { CODEC_ID_MPEG2VIDEO, 0 }, + { CODEC_ID_PCM_S24LE, 1 }, + { CODEC_ID_PCM_S16LE, 1 }, + { 0 } +}; + static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st); static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st); static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x00,0x00,0x00 }, - CODEC_ID_MPEG2VIDEO, mxf_write_mpegvideo_desc }, + mxf_write_mpegvideo_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x03,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x03,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_aes3_desc }, { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, - CODEC_ID_PCM_S16LE, mxf_write_wav_desc }, + mxf_write_wav_desc }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, - CODEC_ID_NONE, NULL }, + NULL }, }; typedef struct MXFContext { int64_t footer_partition_offset; int essence_container_count; - uint8_t essence_containers_indices[sizeof(mxf_essence_container_uls)/ - sizeof(*mxf_essence_container_uls)]; + uint8_t essence_containers_indices[FF_ARRAY_ELEMS(mxf_essence_container_uls)]; + AVRational time_base; + int header_written; + MXFIndexEntry *index_entries; + unsigned edit_units_count; + int edit_unit_start; ///< index of the stream starting edit unit } MXFContext; static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd }; -static const uint8_t umid_base[] = { 0x06,0x0A,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x0F,0x00,0x13,0x00,0x00,0x00 }; +static const uint8_t umid_base[] = { 0x06,0x0A,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x01,0x0D,0x00,0x13,0x00,0x00,0x00 }; /** * complete key for operation pattern, partitions, and primer pack */ -static const uint8_t op1a_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x01,0x00 }; -static const uint8_t footer_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }; // ClosedComplete -static const uint8_t primer_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }; - - +static const uint8_t op1a_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x01,0x00 }; +static const uint8_t footer_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }; // ClosedComplete +static const uint8_t primer_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }; +static const uint8_t index_table_segment_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }; +static const uint8_t random_index_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x11,0x01,0x00 }; static const uint8_t header_open_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }; // OpenIncomplete static const uint8_t header_closed_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x02,0x04,0x00 }; // ClosedComplete @@ -96,8 +142,7 @@ static const uint8_t header_closed_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02, * partial key for header metadata */ static const uint8_t header_metadata_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01 }; - -static const uint8_t multiple_desc_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x0D,0x01,0x03,0x01,0x02,0x7F,0x01,0x00 }; +static const uint8_t multiple_desc_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x0D,0x01,0x03,0x01,0x02,0x7F,0x01,0x00 }; /** * SMPTE RP210 http://www.smpte-ra.org/mdd/index.html @@ -121,6 +166,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x3C06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x03,0x00,0x00}}, /* Modification Date */ // Content Storage { 0x1901, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x05,0x01,0x00,0x00}}, /* Package strong reference batch */ + { 0x1902, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x05,0x02,0x00,0x00}}, /* Package strong reference batch */ // Essence Container Data { 0x2701, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x06,0x01,0x00,0x00,0x00}}, /* Linked Package UID */ { 0x3F07, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x01,0x03,0x04,0x04,0x00,0x00,0x00,0x00}}, /* BodySID */ @@ -141,7 +187,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x0202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x02,0x01,0x01,0x03,0x00,0x00}}, /* Duration */ { 0x1001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x09,0x00,0x00}}, /* Structural Components reference array */ // Source Clip - { 0x1201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x01,0x03,0x01,0x0A,0x00,0x00}}, /* Start position */ + { 0x1201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x01,0x03,0x01,0x04,0x00,0x00}}, /* Start position */ { 0x1101, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x01,0x00,0x00,0x00}}, /* SourcePackageID */ { 0x1102, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x02,0x00,0x00,0x00}}, /* SourceTrackID */ // File Descriptor @@ -150,15 +196,34 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x3001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x00,0x00,0x00,0x00}}, /* SampleRate */ { 0x3004, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x01,0x02,0x00,0x00}}, /* Essence Container */ // Generic Picture Essence Descriptor + { 0x320C, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Frame Layout */ + { 0x320D, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x03,0x02,0x05,0x00,0x00,0x00}}, /* Video Line Map */ { 0x3203, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x02,0x02,0x00,0x00,0x00}}, /* Stored Width */ { 0x3202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x02,0x01,0x00,0x00,0x00}}, /* Stored Height */ + { 0x3209, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0C,0x00,0x00,0x00}}, /* Display Width */ + { 0x3208, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0B,0x00,0x00,0x00}}, /* Display Height */ { 0x320E, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x01,0x01,0x01,0x00,0x00,0x00}}, /* Aspect Ratio */ { 0x3201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x06,0x01,0x00,0x00,0x00,0x00}}, /* Picture Essence Coding */ // Generic Sound Essence Descriptor + { 0x3D02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Locked/Unlocked */ { 0x3D03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x01,0x01,0x01,0x00,0x00}}, /* Audio sampling rate */ { 0x3D07, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x01,0x01,0x04,0x00,0x00,0x00}}, /* ChannelCount */ { 0x3D01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x02,0x03,0x03,0x04,0x00,0x00,0x00}}, /* Quantization bits */ - { 0x3D06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x06,0x01,0x00,0x00,0x00,0x00}}, /* Sound Essence Compression */ + { 0x3D06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x02,0x04,0x02,0x00,0x00,0x00,0x00}}, /* Sound Essence Compression */ + // Index Table Segment + { 0x3F0B, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x05,0x30,0x04,0x06,0x00,0x00,0x00,0x00}}, /* Index Edit Rate */ + { 0x3F0C, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x01,0x03,0x01,0x0A,0x00,0x00}}, /* Index Start Position */ + { 0x3F0D, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x02,0x01,0x01,0x02,0x00,0x00}}, /* Index Duration */ + { 0x3F05, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x06,0x02,0x01,0x00,0x00,0x00,0x00,0x00}}, /* Edit Unit Byte Count */ + { 0x3F06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x01,0x03,0x04,0x05,0x00,0x00,0x00,0x00}}, /* IndexSID */ + { 0x3F08, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x04,0x04,0x01,0x01,0x00,0x00,0x00}}, /* Slice Count */ + { 0x3F09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x04,0x04,0x01,0x06,0x00,0x00,0x00}}, /* Delta Entry Array */ + { 0x3F0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x04,0x04,0x02,0x05,0x00,0x00,0x00}}, /* Index Entry Array */ + // MPEG video Descriptor + { 0x8000, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x06,0x02,0x01,0x0B,0x00,0x00}}, /* BitRate */ + // Wave Audio Essence Descriptor + { 0x3D09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x03,0x05,0x00,0x00,0x00}}, /* Average Bytes Per Second */ + { 0x3D0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Block Align */ }; static void mxf_write_uuid(ByteIOContext *pb, enum MXFMetadataSetType type, int value) @@ -195,7 +260,7 @@ static int klv_encode_ber_length(ByteIOContext *pb, uint64_t len) // long form put_byte(pb, 0x80 + size); while(size) { - size --; + size--; put_byte(pb, len >> 8 * size & 0xff); } return 0; @@ -207,10 +272,9 @@ static int klv_encode_ber_length(ByteIOContext *pb, uint64_t len) static int mxf_get_essence_container_ul_index(enum CodecID id) { int i; - for (i = 0; i < sizeof(mxf_essence_container_uls)/ - sizeof(*mxf_essence_container_uls); i++) - if (mxf_essence_container_uls[i].id == id) - return i; + for (i = 0; mxf_essence_mappings[i].id; i++) + if (mxf_essence_mappings[i].id == id) + return mxf_essence_mappings[i].index; return -1; } @@ -219,7 +283,7 @@ static void mxf_write_primer_pack(AVFormatContext *s) ByteIOContext *pb = s->pb; int local_tag_number, i = 0; - local_tag_number = sizeof(mxf_local_tag_batch)/sizeof(*mxf_local_tag_batch); + local_tag_number = FF_ARRAY_ELEMS(mxf_local_tag_batch); put_buffer(pb, primer_pack_key, 16); klv_encode_ber_length(pb, local_tag_number * 18 + 8); @@ -378,7 +442,7 @@ static void mxf_write_content_storage(AVFormatContext *s) mxf_write_metadata_key(pb, 0x011800); PRINT_KEY(s, "content storage key", pb->buf_ptr - 16); - klv_encode_ber_length(pb, 64); + klv_encode_ber_length(pb, 92); // write uid mxf_write_local_tag(pb, 16, 0x3C0A); @@ -390,6 +454,11 @@ static void mxf_write_content_storage(AVFormatContext *s) mxf_write_refs_count(pb, 2); mxf_write_uuid(pb, MaterialPackage, 0); mxf_write_uuid(pb, SourcePackage, 0); + + // write essence container data + mxf_write_local_tag(pb, 8 + 16, 0x1902); + mxf_write_refs_count(pb, 1); + mxf_write_uuid(pb, EssenceContainerData, 0); } static void mxf_write_track(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) @@ -529,12 +598,12 @@ static void mxf_write_multi_descriptor(AVFormatContext *s) mxf_write_uuid(pb, SubDescriptor, i); } -static void mxf_write_generic_desc(ByteIOContext *pb, AVStream *st, const UID key) +static void mxf_write_generic_desc(ByteIOContext *pb, AVStream *st, const UID key, unsigned size) { MXFStreamContext *sc = st->priv_data; put_buffer(pb, key, 16); - klv_encode_ber_length(pb, 108); + klv_encode_ber_length(pb, size); mxf_write_local_tag(pb, 16, 0x3C0A); mxf_write_uuid(pb, SubDescriptor, st->index); @@ -548,36 +617,87 @@ static void mxf_write_generic_desc(ByteIOContext *pb, AVStream *st, const UID ke mxf_write_local_tag(pb, 16, 0x3004); put_buffer(pb, mxf_essence_container_uls[sc->index].container_ul, 16); - - mxf_write_local_tag(pb, 16, 0x3201); - put_buffer(pb, *sc->codec_ul, 16); } static const UID mxf_mpegvideo_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x51,0x00 }; static const UID mxf_wav_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }; +static const UID mxf_aes3_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }; static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st) { + MXFStreamContext *sc = st->priv_data; ByteIOContext *pb = s->pb; + int stored_height = (st->codec->height+15)/16*16; + AVRational dar; + int f1, f2; - mxf_write_generic_desc(pb, st, mxf_mpegvideo_descriptor_key); + mxf_write_generic_desc(pb, st, mxf_mpegvideo_descriptor_key, 153+sc->interlaced*4); mxf_write_local_tag(pb, 4, 0x3203); put_be32(pb, st->codec->width); mxf_write_local_tag(pb, 4, 0x3202); - put_be32(pb, st->codec->height); + put_be32(pb, stored_height>>sc->interlaced); + + mxf_write_local_tag(pb, 4, 0x3209); + put_be32(pb, st->codec->width); + + mxf_write_local_tag(pb, 4, 0x3208); + put_be32(pb, st->codec->height>>sc->interlaced); + + // bit rate + mxf_write_local_tag(pb, 4, 0x8000); + put_be32(pb, st->codec->bit_rate); + + // frame layout + mxf_write_local_tag(pb, 1, 0x320C); + put_byte(pb, sc->interlaced); + + // video line map + switch (st->codec->height) { + case 576: f1 = 23; f2 = 336; break; + case 608: f1 = 7; f2 = 320; break; + case 480: f1 = 20; f2 = 283; break; + case 512: f1 = 7; f2 = 270; break; + case 720: f1 = 26; f2 = 0; break; // progressive + case 1080: f1 = 21; f2 = 584; break; + default: f1 = 0; f2 = 0; break; + } + + if (!sc->interlaced) { + f2 = 0; + f1 *= 2; + } + + mxf_write_local_tag(pb, 12+sc->interlaced*4, 0x320D); + put_be32(pb, sc->interlaced ? 2 : 1); + put_be32(pb, 4); + put_be32(pb, f1); + if (sc->interlaced) + put_be32(pb, f2); + + av_reduce(&dar.num, &dar.den, + st->codec->width*st->codec->sample_aspect_ratio.num, + st->codec->height*st->codec->sample_aspect_ratio.den, + 1024*1024); mxf_write_local_tag(pb, 8, 0x320E); - put_be32(pb, st->codec->height * st->sample_aspect_ratio.den); - put_be32(pb, st->codec->width * st->sample_aspect_ratio.num); + put_be32(pb, dar.num); + put_be32(pb, dar.den); + + mxf_write_local_tag(pb, 16, 0x3201); + put_buffer(pb, *sc->codec_ul, 16); } -static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st) +static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st, const UID key, unsigned size) { ByteIOContext *pb = s->pb; - mxf_write_generic_desc(pb, st, mxf_wav_descriptor_key); + mxf_write_generic_desc(pb, st, key, size); + + // audio locked + mxf_write_local_tag(pb, 1, 0x3D02); + put_byte(pb, 1); // write audio sampling rate mxf_write_local_tag(pb, 8, 0x3D03); @@ -588,7 +708,31 @@ static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st) put_be32(pb, st->codec->channels); mxf_write_local_tag(pb, 4, 0x3D01); - put_be32(pb, st->codec->bits_per_sample); + put_be32(pb, av_get_bits_per_sample(st->codec->codec_id)); +} + +static void mxf_write_wav_common_desc(AVFormatContext *s, AVStream *st, const UID key, unsigned size) +{ + ByteIOContext *pb = s->pb; + + mxf_write_generic_sound_desc(s, st, key, size); + + mxf_write_local_tag(pb, 2, 0x3D0A); + put_be16(pb, st->codec->block_align); + + // avg bytes per sec + mxf_write_local_tag(pb, 4, 0x3D09); + put_be32(pb, st->codec->block_align*st->codec->sample_rate); +} + +static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st) +{ + mxf_write_wav_common_desc(s, st, mxf_wav_descriptor_key, 107); +} + +static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st) +{ + mxf_write_wav_common_desc(s, st, mxf_aes3_descriptor_key, 107); } static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type) @@ -654,6 +798,28 @@ static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type) } } +static int mxf_write_essence_container_data(AVFormatContext *s) +{ + ByteIOContext *pb = s->pb; + + mxf_write_metadata_key(pb, 0x012300); + klv_encode_ber_length(pb, 72); + + mxf_write_local_tag(pb, 16, 0x3C0A); // Instance UID + mxf_write_uuid(pb, EssenceContainerData, 0); + + mxf_write_local_tag(pb, 32, 0x2701); // Linked Package UID + mxf_write_umid(pb, SourcePackage, 0); + + mxf_write_local_tag(pb, 4, 0x3F07); // BodySID + put_be32(pb, 1); + + mxf_write_local_tag(pb, 4, 0x3F06); // IndexSID + put_be32(pb, 2); + + return 0; +} + static int mxf_write_header_metadata_sets(AVFormatContext *s) { mxf_write_preface(s); @@ -661,10 +827,110 @@ static int mxf_write_header_metadata_sets(AVFormatContext *s) mxf_write_content_storage(s); mxf_write_package(s, MaterialPackage); mxf_write_package(s, SourcePackage); + mxf_write_essence_container_data(s); return 0; } -static void mxf_write_partition(AVFormatContext *s, int bodysid, const uint8_t *key, int write_metadata) +static int mxf_write_index_table_segment(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + ByteIOContext *pb = s->pb; + int i, j, k, ret, key_offset = 0; + int temporal_reordering = 0; + + av_log(s, AV_LOG_DEBUG, "edit units count %d\n", mxf->edit_units_count); + + put_buffer(pb, index_table_segment_key, 16); + ret = klv_encode_ber_length(pb, 109 + s->nb_streams*6 + + mxf->edit_units_count*(11+(s->nb_streams-1)*4)); + + // instance id + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, IndexTableSegment, 0); + + // index edit rate + mxf_write_local_tag(pb, 8, 0x3F0B); + put_be32(pb, mxf->time_base.num); + put_be32(pb, mxf->time_base.den); + + // index start position + mxf_write_local_tag(pb, 8, 0x3F0C); + put_be64(pb, 0); + + // index duration + mxf_write_local_tag(pb, 8, 0x3F0D); + put_be64(pb, mxf->edit_units_count); + + // edit unit byte count + mxf_write_local_tag(pb, 4, 0x3F05); + put_be32(pb, 0); + + // index sid + mxf_write_local_tag(pb, 4, 0x3F06); + put_be32(pb, 2); + + // body sid + mxf_write_local_tag(pb, 4, 0x3F07); + put_be32(pb, 1); + + // slice count - 1 + mxf_write_local_tag(pb, 1, 0x3F08); + put_byte(pb, s->nb_streams-1); + + // delta entry array + mxf_write_local_tag(pb, 8 + s->nb_streams*6, 0x3F09); + put_be32(pb, s->nb_streams); // num of entries + put_be32(pb, 6); // size of one entry + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + put_byte(pb, sc->temporal_reordering); + if (sc->temporal_reordering) + temporal_reordering = 1; + put_byte(pb, i); + put_be32(pb, 0); // element delta + } + + mxf_write_local_tag(pb, 8 + mxf->edit_units_count*(11+(s->nb_streams-1)*4), 0x3F0A); + put_be32(pb, mxf->edit_units_count); // num of entries + put_be32(pb, 11+(s->nb_streams-1)*4); // size of one entry + for (i = 0; i < mxf->edit_units_count; i++) { + if (mxf->index_entries[i].flags & 0x40) + key_offset = 0; + if (temporal_reordering) { + int temporal_offset = 0; + for (j = i+1; j < mxf->edit_units_count; j++) { + temporal_offset++; + if (mxf->index_entries[j].flags & 0x10) { // backward prediction + // next is not b, so is reordered + if (!(mxf->index_entries[i+1].flags & 0x10)) { + if ((mxf->index_entries[i].flags & 0x11) == 0) // i frame + temporal_offset = 0; + else + temporal_offset = -temporal_offset; + } + break; + } + } + put_byte(pb, temporal_offset); + } else + put_byte(pb, 0); + put_byte(pb, key_offset); + put_byte(pb, mxf->index_entries[i].flags); + // stream offset + put_be64(pb, mxf->index_entries[i].offset - mxf->index_entries[0].offset); + for (k = 0; k < s->nb_streams; k++) { + if (mxf->index_entries[i].slice_offset[k]) + put_be32(pb, mxf->index_entries[i].slice_offset[k]); + } + key_offset--; + } + + return ret; +} + +static void mxf_write_partition(AVFormatContext *s, int bodysid, int indexsid, + const uint8_t *key, int write_metadata) { MXFContext *mxf = s->priv_data; ByteIOContext *pb = s->pb; @@ -689,9 +955,10 @@ static void mxf_write_partition(AVFormatContext *s, int bodysid, const uint8_t * header_byte_count_offset = url_ftell(pb); put_be64(pb, 0); // headerByteCount, update later - // no indexTable - put_be64(pb, 0); // indexByteCount - put_be32(pb, 0); // indexSID + // indexTable + put_be64(pb, indexsid ? 19 + 109 + s->nb_streams*6 + + mxf->edit_units_count*(11+(s->nb_streams-1)*4) : 0); // indexByteCount + put_be32(pb, indexsid); // indexSID put_be64(pb, 0); // bodyOffset put_be32(pb, bodysid); // bodySID @@ -750,12 +1017,99 @@ static const UID *mxf_get_mpeg2_codec_ul(AVCodecContext *avctx) return NULL; } +static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + uint32_t c = -1; + int i; + + mxf->index_entries[mxf->edit_units_count].flags = 0; + + for(i = 0; i < pkt->size - 4; i++) { + c = (c<<8) + pkt->data[i]; + if (c == 0x1B5) { + if (i + 2 < pkt->size && (pkt->data[i+1] & 0xf0) == 0x10) { // seq ext + st->codec->profile = pkt->data[i+1] & 0x07; + st->codec->level = pkt->data[i+2] >> 4; + } else if (i + 5 < pkt->size && (pkt->data[i+1] & 0xf0) == 0x80) { // pict coding ext + sc->interlaced = !(pkt->data[i+5] & 0x80); // progressive frame + break; + } + } else if (c == 0x1b8) { // gop + if (i + 4 < pkt->size && pkt->data[i+4]>>6 & 0x01) // closed + mxf->index_entries[mxf->edit_units_count].flags |= 0x80; // random access + } else if (c == 0x1b3) { // seq + mxf->index_entries[mxf->edit_units_count].flags |= 0x40; + } else if (c == 0x100) { // pic + int pict_type = (pkt->data[i+2]>>3) & 0x07; + if (pict_type == 2) { // P frame + mxf->index_entries[mxf->edit_units_count].flags |= 0x22; + st->codec->gop_size = 1; + } else if (pict_type == 3) { // B frame + mxf->index_entries[mxf->edit_units_count].flags |= 0x33; + sc->temporal_reordering = -1; + } else if (!pict_type) { + av_log(s, AV_LOG_ERROR, "error parsing mpeg2 frame\n"); + return 0; + } + } + } + sc->codec_ul = mxf_get_mpeg2_codec_ul(st->codec); + return !!sc->codec_ul; +} + +static int ff_audio_interleave_init(AVFormatContext *s, const int *samples_per_frame) +{ + int i; + + if (!samples_per_frame) + return -1; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AudioInterleaveContext *aic = st->priv_data; + + if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + aic->sample_size = (st->codec->channels * + av_get_bits_per_sample(st->codec->codec_id)) / 8; + if (!aic->sample_size) { + av_log(s, AV_LOG_ERROR, "could not compute sample size\n"); + return -1; + } + aic->samples_per_frame = samples_per_frame; + aic->samples = aic->samples_per_frame; + + av_fifo_init(&aic->fifo, 100 * *aic->samples); + } + } + + return 0; +} + +static void ff_audio_interleave_close(AVFormatContext *s) +{ + int i; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AudioInterleaveContext *aic = st->priv_data; + + if (st->codec->codec_type == CODEC_TYPE_AUDIO) + av_fifo_free(&aic->fifo); + } +} + static int mxf_write_header(AVFormatContext *s) { MXFContext *mxf = s->priv_data; int i; - uint8_t present[sizeof(mxf_essence_container_uls)/ - sizeof(*mxf_essence_container_uls)] = {0}; + uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; + const int *samples_per_frame = NULL; + + if (s->nb_streams > 17) { + av_log(s, AV_LOG_ERROR, "error, mxf muxer supports 17 tracks maximum\n"); + return -1; + } for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -763,11 +1117,25 @@ static int mxf_write_header(AVFormatContext *s) if (!sc) return AVERROR(ENOMEM); st->priv_data = sc; - // set pts information - if (st->codec->codec_type == CODEC_TYPE_VIDEO) - av_set_pts_info(st, 64, 1, st->codec->time_base.den); - else if (st->codec->codec_type == CODEC_TYPE_AUDIO) - av_set_pts_info(st, 64, 1, st->codec->sample_rate); + + if (st->codec->codec_type == CODEC_TYPE_VIDEO) { + if (fabs(av_q2d(st->codec->time_base) - 1/25.0) < 0.0001) { + samples_per_frame = PAL_samples_per_frame; + mxf->time_base = (AVRational){ 1, 25 }; + } else if (fabs(av_q2d(st->codec->time_base) - 1001/30000.0) < 0.0001) { + samples_per_frame = NTSC_samples_per_frame; + mxf->time_base = (AVRational){ 1001, 30000 }; + } else { + av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n"); + return -1; + } + mxf->edit_unit_start = st->index; + } else if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + if (st->codec->sample_rate != 48000) { + av_log(s, AV_LOG_ERROR, "only 48khz is implemented\n"); + return -1; + } + } sc->duration = -1; sc->index = mxf_get_essence_container_ul_index(st->codec->codec_id); @@ -777,20 +1145,7 @@ static int mxf_write_header(AVFormatContext *s) return -1; } - if (st->codec->codec_id == CODEC_ID_MPEG2VIDEO) { - if (st->codec->profile == FF_PROFILE_UNKNOWN || - st->codec->level == FF_LEVEL_UNKNOWN) { - av_log(s, AV_LOG_ERROR, "track %d: profile and level must be set for mpeg-2\n", i); - return -1; - } - sc->codec_ul = mxf_get_mpeg2_codec_ul(st->codec); - if (!sc->codec_ul) { - av_log(s, AV_LOG_ERROR, "track %d: could not find codec ul for mpeg-2, " - "unsupported profile/level\n", i); - return -1; - } - } else - sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; if (!present[sc->index]) { mxf->essence_containers_indices[mxf->essence_container_count++] = sc->index; @@ -802,17 +1157,61 @@ static int mxf_write_header(AVFormatContext *s) PRINT_KEY(s, "track essence element key", sc->track_essence_element_key); } - mxf_write_partition(s, 1, header_open_partition_key, 1); + for (i = 0; i < s->nb_streams; i++) { + MXFStreamContext *sc = s->streams[i]->priv_data; + av_set_pts_info(s->streams[i], 64, mxf->time_base.num, mxf->time_base.den); + // update element count + sc->track_essence_element_key[13] = present[sc->index]; + sc->order = AV_RB32(sc->track_essence_element_key+12); + } + + if (!samples_per_frame) + samples_per_frame = PAL_samples_per_frame; + + if (ff_audio_interleave_init(s, samples_per_frame) < 0) + return -1; return 0; } static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) { + MXFContext *mxf = s->priv_data; ByteIOContext *pb = s->pb; AVStream *st = s->streams[pkt->stream_index]; MXFStreamContext *sc = st->priv_data; + if (!(mxf->edit_units_count % MXF_INDEX_CLUSTER_SIZE)) { + mxf->index_entries = av_realloc(mxf->index_entries, + (mxf->edit_units_count + MXF_INDEX_CLUSTER_SIZE)* + sizeof(*mxf->index_entries)); + if (!mxf->index_entries) { + av_log(s, AV_LOG_ERROR, "could not allocate index entries\n"); + return -1; + } + } + + if (st->codec->codec_id == CODEC_ID_MPEG2VIDEO) { + if (!mxf_parse_mpeg2_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get mpeg2 profile and level\n"); + return -1; + } + } + + if (!mxf->header_written) { + mxf_write_partition(s, 1, 0, header_open_partition_key, 1); + mxf->header_written = 1; + } + + if (st->index == mxf->edit_unit_start) { + mxf->index_entries[mxf->edit_units_count].offset = url_ftell(pb); + mxf->index_entries[mxf->edit_units_count].slice_offset[st->index] = 0; + mxf->edit_units_count++; + } else { + mxf->index_entries[mxf->edit_units_count-1].slice_offset[st->index] = + url_ftell(pb) - mxf->index_entries[mxf->edit_units_count-1].offset; + } + put_buffer(pb, sc->track_essence_element_key, 16); // write key klv_encode_ber_length(pb, pkt->size); // write length put_buffer(pb, pkt->data, pkt->size); // write value @@ -823,21 +1222,172 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } +static void mxf_write_random_index_pack(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + ByteIOContext *pb = s->pb; + uint64_t pos = url_ftell(pb); + + put_buffer(pb, random_index_pack_key, 16); + klv_encode_ber_length(pb, 28); + + put_be32(pb, 1); // BodySID of header partition + put_be64(pb, 0); // offset of header partition + + put_be32(pb, 0); // BodySID of footer partition + put_be64(pb, mxf->footer_partition_offset); + + put_be32(pb, url_ftell(pb) - pos + 4); +} + static int mxf_write_footer(AVFormatContext *s) { MXFContext *mxf = s->priv_data; ByteIOContext *pb = s->pb; mxf->footer_partition_offset = url_ftell(pb); - mxf_write_partition(s, 0, footer_partition_key, 0); + mxf_write_partition(s, 0, 2, footer_partition_key, 0); + + mxf_write_index_table_segment(s); + + mxf_write_random_index_pack(s); + if (!url_is_streamed(s->pb)) { url_fseek(pb, 0, SEEK_SET); - mxf_write_partition(s, 1, header_closed_partition_key, 1); + mxf_write_partition(s, 1, 0, header_closed_partition_key, 1); } + + ff_audio_interleave_close(s); + mxf_free(s); return 0; } +static int mxf_interleave_new_audio_packet(AVFormatContext *s, AVPacket *pkt, + int stream_index, int flush) +{ + AVStream *st = s->streams[stream_index]; + AudioInterleaveContext *aic = st->priv_data; + + int size = FFMIN(av_fifo_size(&aic->fifo), *aic->samples * aic->sample_size); + if (!size || (!flush && size == av_fifo_size(&aic->fifo))) + return 0; + + av_new_packet(pkt, size); + av_fifo_read(&aic->fifo, pkt->data, size); + + pkt->dts = pkt->pts = aic->dts; + pkt->duration = av_rescale_q(*aic->samples, + (AVRational){ 1, st->codec->sample_rate }, + st->time_base); + pkt->stream_index = stream_index; + aic->dts += pkt->duration; + + aic->samples++; + if (!*aic->samples) + aic->samples = aic->samples_per_frame; + + return size; +} + +static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) +{ + AVPacketList *pktl; + int stream_count = 0; + int streams[MAX_STREAMS]; + + memset(streams, 0, sizeof(streams)); + pktl = s->packet_buffer; + while (pktl) { + //av_log(s, AV_LOG_DEBUG, "show st:%d dts:%lld\n", pktl->pkt.stream_index, pktl->pkt.dts); + if (!streams[pktl->pkt.stream_index]) + stream_count++; + streams[pktl->pkt.stream_index]++; + pktl = pktl->next; + } + + if (stream_count && (s->nb_streams == stream_count || flush)) { + pktl = s->packet_buffer; + if (s->nb_streams != stream_count) { + MXFContext *mxf = s->priv_data; + AVPacketList *first = NULL; + // find first packet in edit unit + while (pktl) { + AVStream *st = s->streams[pktl->pkt.stream_index]; + if (st->index == mxf->edit_unit_start) + break; + else if (!first) + first = pktl; + pktl = pktl->next; + } + // purge packet queue + while (pktl) { + AVPacketList *next = pktl->next; + av_free_packet(&pktl->pkt); + av_freep(&pktl); + pktl = next; + } + if (!first) + goto out; + pktl = first; + } + + *out = pktl->pkt; + //av_log(s, AV_LOG_DEBUG, "out st:%d dts:%lld\n", (*out).stream_index, (*out).dts); + s->packet_buffer = pktl->next; + av_freep(&pktl); + return 1; + } else { + out: + av_init_packet(out); + return 0; + } +} + +static int mxf_compare_timestamps(AVFormatContext *s, AVPacket *next, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt ->stream_index]; + AVStream *st2 = s->streams[next->stream_index]; + MXFStreamContext *sc = st ->priv_data; + MXFStreamContext *sc2 = st2->priv_data; + + int64_t left = st2->time_base.num * (int64_t)st ->time_base.den; + int64_t right = st ->time_base.num * (int64_t)st2->time_base.den; + + return next->dts * left > pkt->dts * right || // FIXME this can overflow + (next->dts * left == pkt->dts * right && sc->order < sc2->order); +} + +static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) +{ + int i; + + if (pkt) { + AVStream *st = s->streams[pkt->stream_index]; + AudioInterleaveContext *aic = st->priv_data; + if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + av_fifo_generic_write(&aic->fifo, pkt->data, pkt->size, NULL); + } else { + // rewrite pts and dts to be decoded time line position + pkt->pts = pkt->dts = aic->dts; + aic->dts += pkt->duration; + ff_interleave_add_packet(s, pkt, mxf_compare_timestamps); + } + pkt = NULL; + } + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + AVPacket new_pkt; + while (mxf_interleave_new_audio_packet(s, &new_pkt, i, flush)) + ff_interleave_add_packet(s, &new_pkt, mxf_compare_timestamps); + } + } + + return mxf_interleave_get_packet(s, out, pkt, flush); +} + AVOutputFormat mxf_muxer = { "mxf", NULL_IF_CONFIG_SMALL("Material eXchange Format"), @@ -849,6 +1399,7 @@ AVOutputFormat mxf_muxer = { mxf_write_header, mxf_write_packet, mxf_write_footer, + AVFMT_NOTIMESTAMPS, + NULL, + mxf_interleave, }; - -