avformat/mxfenc: Correct klv_fill_key
[ffmpeg.git] / libavformat / mxfenc.c
index e9c4a9d..e0ae14e 100644 (file)
@@ -39,6 +39,7 @@
 #include "libavutil/random_seed.h"
 #include "libavutil/timecode.h"
 #include "libavutil/avassert.h"
+#include "libavutil/pixdesc.h"
 #include "libavutil/time_internal.h"
 #include "libavcodec/bytestream.h"
 #include "libavcodec/dnxhddata.h"
@@ -78,6 +79,9 @@ typedef struct MXFStreamContext {
     int interlaced;          ///< whether picture is interlaced
     int field_dominance;     ///< tff=1, bff=2
     int component_depth;
+    int color_siting;
+    int signal_standard;
+    int h_chroma_sub_sample;
     int temporal_reordering;
     AVRational aspect_ratio; ///< display aspect ratio
     int closed_gop;          ///< gop is closed, used in mpeg-2 frame parsing
@@ -313,6 +317,7 @@ typedef struct MXFContext {
     uint8_t umid[16];        ///< unique material identifier
     int channel_count;
     uint32_t tagged_value_count;
+    AVRational audio_edit_rate;
 } MXFContext;
 
 static const uint8_t uuid_base[]            = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd };
@@ -329,7 +334,7 @@ static const uint8_t index_table_segment_key[]     = { 0x06,0x0E,0x2B,0x34,0x02,
 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
-static const uint8_t klv_fill_key[]                = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 };
+static const uint8_t klv_fill_key[]                = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 };
 static const uint8_t body_partition_key[]          = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }; // ClosedComplete
 
 /**
@@ -368,6 +373,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0x4401, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x15,0x10,0x00,0x00,0x00,0x00}}, /* Package UID */
     { 0x4405, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x01,0x03,0x00,0x00}}, /* Package Creation Date */
     { 0x4404, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x05,0x00,0x00}}, /* Package Modified Date */
+    { 0x4402, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x03,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Package Name */
     { 0x4403, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x05,0x00,0x00}}, /* Tracks Strong reference array */
     { 0x4406, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0C,0x00,0x00,0x00}}, /* User Comments */
     { 0x4701, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x03,0x00,0x00}}, /* Descriptor */
@@ -396,6 +402,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0x3F01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x04,0x06,0x0B,0x00,0x00}}, /* Sub Descriptors reference array */
     { 0x3006, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x06,0x01,0x01,0x03,0x05,0x00,0x00,0x00}}, /* Linked Track ID */
     { 0x3001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x00,0x00,0x00,0x00}}, /* SampleRate */
+    { 0x3002, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x06,0x01,0x02,0x00,0x00,0x00,0x00}}, /* ContainerDuration */
     { 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 */
@@ -404,12 +411,15 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 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 */
+    { 0x320B, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0E,0x00,0x00,0x00}}, /* Presentation Y offset */
     { 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 */
     { 0x3212, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x03,0x01,0x06,0x00,0x00,0x00}}, /* Field Dominance (Opt) */
+    { 0x3215, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x05,0x01,0x13,0x00,0x00,0x00,0x00}}, /* Signal Standard */
     // CDCI Picture Essence Descriptor
     { 0x3301, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x05,0x03,0x0A,0x00,0x00,0x00}}, /* Component Depth */
     { 0x3302, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x05,0x00,0x00,0x00}}, /* Horizontal Subsampling */
+    { 0x3303, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x06,0x00,0x00,0x00}}, /* Color Siting */
     // Generic Sound Essence Descriptor
     { 0x3D02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,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 */
@@ -778,8 +788,14 @@ static void mxf_write_track(AVFormatContext *s, AVStream *st, enum MXFMetadataSe
         avio_write(pb, sc->track_essence_element_key + 12, 4);
 
     mxf_write_local_tag(pb, 8, 0x4B01);
-    avio_wb32(pb, mxf->time_base.den);
-    avio_wb32(pb, mxf->time_base.num);
+
+    if (st == mxf->timecode_track && s->oformat == &ff_mxf_opatom_muxer){
+        avio_wb32(pb, mxf->tc.rate.num);
+        avio_wb32(pb, mxf->tc.rate.den);
+    } else {
+        avio_wb32(pb, mxf->time_base.den);
+        avio_wb32(pb, mxf->time_base.num);
+    }
 
     // write origin
     mxf_write_local_tag(pb, 8, 0x4B02);
@@ -808,7 +824,12 @@ static void mxf_write_common_fields(AVFormatContext *s, AVStream *st)
 
     // write duration
     mxf_write_local_tag(pb, 8, 0x0202);
-    avio_wb64(pb, mxf->duration);
+
+    if (st != mxf->timecode_track && s->oformat == &ff_mxf_opatom_muxer && st->codec->codec_type == AVMEDIA_TYPE_AUDIO){
+        avio_wb64(pb, mxf->body_offset / mxf->edit_unit_byte_count);
+    } else {
+        avio_wb64(pb, mxf->duration);
+    }
 }
 
 static void mxf_write_sequence(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type)
@@ -976,9 +997,11 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
     int stored_height = (st->codec->height+15)/16*16;
     int display_height;
     int f1, f2;
-    unsigned desc_size = size+8+8+8+8+8+8+5+16+sc->interlaced*4+12+20;
+    unsigned desc_size = size+8+8+8+8+8+8+8+5+16+sc->interlaced*4+12+20+5;
     if (sc->interlaced && sc->field_dominance)
         desc_size += 5;
+    if (sc->signal_standard)
+        desc_size += 5;
 
     mxf_write_generic_desc(s, st, key, desc_size);
 
@@ -1001,13 +1024,26 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
     mxf_write_local_tag(pb, 4, 0x3208);
     avio_wb32(pb, display_height>>sc->interlaced);
 
+    // presentation Y offset
+    mxf_write_local_tag(pb, 4, 0x320B);
+    avio_wb32(pb, (st->codec->height - display_height)>>sc->interlaced);
+
     // component depth
     mxf_write_local_tag(pb, 4, 0x3301);
     avio_wb32(pb, sc->component_depth);
 
     // horizontal subsampling
     mxf_write_local_tag(pb, 4, 0x3302);
-    avio_wb32(pb, 2);
+    avio_wb32(pb, sc->h_chroma_sub_sample);
+
+    // color siting
+    mxf_write_local_tag(pb, 1, 0x3303);
+    avio_w8(pb, sc->color_siting);
+
+    if (sc->signal_standard) {
+        mxf_write_local_tag(pb, 1, 0x3215);
+        avio_w8(pb, sc->signal_standard);
+    }
 
     // frame layout
     mxf_write_local_tag(pb, 1, 0x320C);
@@ -1083,8 +1119,17 @@ static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, con
     AVIOContext *pb = s->pb;
     MXFContext *mxf = s->priv_data;
     int show_warnings = !mxf->footer_partition_offset;
+    int duration_size = 0;
 
-    mxf_write_generic_desc(s, st, key, size+5+12+8+8);
+    if (s->oformat == &ff_mxf_opatom_muxer)
+        duration_size = 12;
+
+    mxf_write_generic_desc(s, st, key, size+duration_size+5+12+8+8);
+
+    if (duration_size > 0){
+        mxf_write_local_tag(pb, 8, 0x3002);
+        avio_wb64(pb, mxf->body_offset / mxf->edit_unit_byte_count);
+    }
 
     // audio locked
     mxf_write_local_tag(pb, 1, 0x3D02);
@@ -1107,7 +1152,7 @@ static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, con
             av_log(s, AV_LOG_WARNING, "d10_channelcount shall be set to 4 or 8 : the output will not comply to MXF D-10 specs\n");
         avio_wb32(pb, mxf->channel_count);
     } else {
-        if (show_warnings && mxf->channel_count != -1)
+        if (show_warnings && mxf->channel_count != -1 && s->oformat != &ff_mxf_opatom_muxer)
             av_log(s, AV_LOG_ERROR, "-d10_channelcount requires MXF D-10 and will be ignored\n");
         avio_wb32(pb, st->codec->channels);
     }
@@ -1612,6 +1657,8 @@ AVPacket *pkt)
 
     if ((frame_size = avpriv_dnxhd_get_frame_size(cid)) < 0)
         return -1;
+    if ((sc->interlaced = avpriv_dnxhd_get_interlaced(cid)) < 0)
+        return AVERROR_INVALIDDATA;
 
     switch (cid) {
     case 1235:
@@ -1956,6 +2003,19 @@ static void mxf_gen_umid(AVFormatContext *s)
     mxf->instance_number = seed & 0xFFFFFF;
 }
 
+static int mxf_init_timecode(AVFormatContext *s, AVStream *st, AVRational rate)
+{
+    MXFContext *mxf = s->priv_data;
+    AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+    if (!tcr)
+        tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
+
+    if (tcr)
+        return av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s);
+    else
+        return av_timecode_init(&mxf->tc, rate, 0, 0, s);
+}
+
 static int mxf_write_header(AVFormatContext *s)
 {
     MXFContext *mxf = s->priv_data;
@@ -1964,7 +2024,6 @@ static int mxf_write_header(AVFormatContext *s)
     const MXFSamplesPerFrame *spf = NULL;
     AVDictionaryEntry *t;
     int64_t timestamp = 0;
-    AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
 
     if (!s->nb_streams)
         return -1;
@@ -1981,16 +2040,31 @@ static int mxf_write_header(AVFormatContext *s)
             return AVERROR(ENOMEM);
         st->priv_data = sc;
 
-        if ((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) {
+        if (((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) && s->oformat != &ff_mxf_opatom_muxer) {
             av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n");
             return -1;
         }
 
         if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+            const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codec->pix_fmt);
             // TODO: should be avg_frame_rate
             AVRational rate, tbc = st->time_base;
             // Default component depth to 8
             sc->component_depth = 8;
+            sc->h_chroma_sub_sample = 2;
+            sc->color_siting = 0xFF;
+
+            if (pix_desc) {
+                sc->component_depth     = pix_desc->comp[0].depth_minus1 + 1;
+                sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
+            }
+            switch (ff_choose_chroma_location(s, st)) {
+            case AVCHROMA_LOC_TOPLEFT: sc->color_siting = 0; break;
+            case AVCHROMA_LOC_LEFT:    sc->color_siting = 6; break;
+            case AVCHROMA_LOC_TOP:     sc->color_siting = 1; break;
+            case AVCHROMA_LOC_CENTER:  sc->color_siting = 3; break;
+            }
+
             mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num;
             spf = ff_mxf_get_samples_per_frame(s, tbc);
             if (!spf) {
@@ -2001,14 +2075,9 @@ static int mxf_write_header(AVFormatContext *s)
             mxf->time_base = spf->time_base;
             rate = av_inv_q(mxf->time_base);
             avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den);
-            if (!tcr)
-                tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
-            if (tcr)
-                ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s);
-            else
-                ret = av_timecode_init(&mxf->tc, rate, 0, 0, s);
-            if (ret < 0)
+            if((ret = mxf_init_timecode(s, st, rate)) < 0)
                 return ret;
+
             sc->video_bit_rate = st->codec->bit_rate ? st->codec->bit_rate : st->codec->rc_max_rate;
             if (s->oformat == &ff_mxf_d10_muxer) {
                 if (sc->video_bit_rate == 50000000) {
@@ -2031,6 +2100,8 @@ static int mxf_write_header(AVFormatContext *s)
                 mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count);
                 mxf->edit_unit_byte_count += 16 + 4 + 4 + spf->samples_per_frame[0]*8*4;
                 mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count);
+
+                sc->signal_standard = 1;
             }
         } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
             if (st->codec->sample_rate != 48000) {
@@ -2048,8 +2119,35 @@ static int mxf_write_header(AVFormatContext *s)
                     av_log(s, AV_LOG_ERROR, "MXF D-10 only support 16 or 24 bits le audio\n");
                 }
                 sc->index = ((MXFStreamContext*)s->streams[0]->priv_data)->index + 1;
-            } else
-            mxf->slice_count = 1;
+            } else if (s->oformat == &ff_mxf_opatom_muxer) {
+                AVRational tbc = av_inv_q(mxf->audio_edit_rate);
+
+                if (st->codec->codec_id != AV_CODEC_ID_PCM_S16LE &&
+                    st->codec->codec_id != AV_CODEC_ID_PCM_S24LE) {
+                    av_log(s, AV_LOG_ERROR, "Only pcm_s16le and pcm_s24le audio codecs are implemented\n");
+                    return AVERROR_PATCHWELCOME;
+                }
+                if (st->codec->channels != 1) {
+                    av_log(s, AV_LOG_ERROR, "MXF OPAtom only supports single channel audio\n");
+                    return AVERROR(EINVAL);
+                }
+
+                spf = ff_mxf_get_samples_per_frame(s, tbc);
+                if (!spf){
+                    av_log(s, AV_LOG_ERROR, "Unsupported timecode frame rate %d/%d\n", tbc.den, tbc.num);
+                    return AVERROR(EINVAL);
+                }
+
+                mxf->time_base = st->time_base;
+                if((ret = mxf_init_timecode(s, st, av_inv_q(spf->time_base))) < 0)
+                    return ret;
+
+                mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num;
+                mxf->edit_unit_byte_count = (av_get_bits_per_sample(st->codec->codec_id) * st->codec->channels) >> 3;
+                sc->index = 2;
+            } else {
+                mxf->slice_count = 1;
+            }
         }
 
         if (!sc->index) {
@@ -2263,7 +2361,6 @@ static int mxf_write_opatom_packet(AVFormatContext *s, AVPacket *pkt, MXFIndexEn
     mxf->edit_units_count++;
     avio_write(pb, pkt->data, pkt->size);
     mxf->body_offset += pkt->size;
-
     avio_flush(pb);
 
     return 0;
@@ -2402,6 +2499,13 @@ static int mxf_write_footer(AVFormatContext *s)
     AVIOContext *pb = s->pb;
     int err = 0;
 
+    if (!mxf->header_written ||
+        (s->oformat == &ff_mxf_opatom_muxer && !mxf->body_partition_offset)) {
+        /* reason could be invalid options/not supported codec/out of memory */
+        err = AVERROR_UNKNOWN;
+        goto end;
+    }
+
     mxf->duration = mxf->last_indexed_edit_unit + mxf->edit_units_count;
 
     mxf_write_klv_fill(s);
@@ -2492,7 +2596,7 @@ static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket
         }
 
         *out = pktl->pkt;
-        av_dlog(s, "out st:%d dts:%"PRId64"\n", (*out).stream_index, (*out).dts);
+        av_log(s, AV_LOG_TRACE, "out st:%d dts:%"PRId64"\n", (*out).stream_index, (*out).dts);
         s->internal->packet_buffer = pktl->next;
         if(s->streams[pktl->pkt.stream_index]->last_in_packet_buffer == pktl)
             s->streams[pktl->pkt.stream_index]->last_in_packet_buffer= NULL;
@@ -2535,6 +2639,19 @@ static const AVClass mxf_d10_muxer_class = {
     .version        = LIBAVUTIL_VERSION_INT,
 };
 
+static const AVOption opatom_options[] = {
+    { "mxf_audio_edit_rate", "Audio edit rate for timecode",
+        offsetof(MXFContext, audio_edit_rate), AV_OPT_TYPE_RATIONAL, {.dbl=25}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+    { NULL },
+};
+
+static const AVClass mxf_opatom_muxer_class = {
+    .class_name     = "MXF-OPAtom muxer",
+    .item_name      = av_default_item_name,
+    .option         = opatom_options,
+    .version        = LIBAVUTIL_VERSION_INT,
+};
+
 AVOutputFormat ff_mxf_muxer = {
     .name              = "mxf",
     .long_name         = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"),
@@ -2578,4 +2695,5 @@ AVOutputFormat ff_mxf_opatom_muxer = {
     .write_trailer     = mxf_write_footer,
     .flags             = AVFMT_NOTIMESTAMPS,
     .interleave_packet = mxf_interleave,
+    .priv_class        = &mxf_opatom_muxer_class,
 };