diff options
Diffstat (limited to 'libavformat/mxfenc.c')
-rw-r--r-- | libavformat/mxfenc.c | 111 |
1 files changed, 57 insertions, 54 deletions
diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 597a4f7..5597dd2 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -3,20 +3,20 @@ * Copyright (c) 2008 GUCAS, Zhentan Feng <spyfeng at gmail dot com> * Copyright (c) 2008 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -35,15 +35,19 @@ #include <math.h> #include <time.h> +#include "libavutil/opt.h" #include "libavutil/random_seed.h" +#include "libavutil/timecode.h" #include "libavcodec/bytestream.h" #include "audiointerleave.h" #include "avformat.h" #include "internal.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 }; +static const int NTSC_samples_per_frame[] = { 1602, 1601, 1602, 1601, 1602, 0 }; +static const int NTSC_60_samples_per_frame[] = { 801, 801, 801, 801, 800, 0 }; +static const int PAL_samples_per_frame[] = { 1920, 0 }; +static const int PAL_50_samples_per_frame[] = { 960, 0 }; extern AVOutputFormat ff_mxf_d10_muxer; @@ -69,6 +73,7 @@ typedef struct { const UID *codec_ul; int order; ///< interleaving order if dts are equal int interlaced; ///< whether picture is interlaced + int field_dominance; ///< tff=1, bff=2 int temporal_reordering; AVRational aspect_ratio; ///< display aspect ratio int closed_gop; ///< gop is closed, used in mpeg-2 frame parsing @@ -171,6 +176,7 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { }; typedef struct MXFContext { + AVClass *av_class; int64_t footer_partition_offset; int essence_container_count; AVRational time_base; @@ -184,10 +190,9 @@ typedef struct MXFContext { unsigned body_partitions_count; int last_key_index; ///< index of last key frame uint64_t duration; + AVTimecode tc; ///< timecode context AVStream *timecode_track; int timecode_base; ///< rounded time code base (25 or 30) - int timecode_start; ///< frame number computed from mpeg-2 gop header timecode - int timecode_drop_frame; ///< time code use drop frame method frop mpeg-2 essence gop header int edit_unit_byte_count; ///< fixed edit unit byte count uint64_t body_offset; uint32_t instance_number; @@ -280,6 +285,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 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 */ + { 0x3212, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x03,0x01,0x06,0x00,0x00,0x00}}, /* Field Dominance (Opt) */ // 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 */ @@ -500,7 +506,7 @@ static void mxf_write_identification(AVFormatContext *s) { MXFContext *mxf = s->priv_data; AVIOContext *pb = s->pb; - const char *company = "Libav"; + const char *company = "FFmpeg"; const char *product = "OP1a Muxer"; const char *version; int length; @@ -665,7 +671,7 @@ static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum // Start Time Code mxf_write_local_tag(pb, 8, 0x1501); - avio_wb64(pb, mxf->timecode_start); + avio_wb64(pb, mxf->tc.start); // Rounded Time Code Base mxf_write_local_tag(pb, 2, 0x1502); @@ -673,7 +679,7 @@ static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum // Drop Frame mxf_write_local_tag(pb, 1, 0x1503); - avio_w8(pb, mxf->timecode_drop_frame); + avio_w8(pb, !!(mxf->tc.flags & AV_TIMECODE_FLAG_DROPFRAME)); } static void mxf_write_structural_component(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) @@ -785,8 +791,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; + if (sc->interlaced && sc->field_dominance) + desc_size += 5; - mxf_write_generic_desc(s, st, key, size+8+8+8+8+8+8+5+16+sc->interlaced*4+12+20); + mxf_write_generic_desc(s, st, key, desc_size); mxf_write_local_tag(pb, 4, 0x3203); avio_wb32(pb, st->codec->width); @@ -848,6 +857,12 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke mxf_write_local_tag(pb, 16, 0x3201); avio_write(pb, *sc->codec_ul, 16); + + if (sc->interlaced && sc->field_dominance) { + mxf_write_local_tag(pb, 1, 0x3212); + avio_w8(pb, sc->field_dominance); + } + } static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) @@ -1313,7 +1328,6 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt, MXFIndexEntry *e) { MXFStreamContext *sc = st->priv_data; - MXFContext *mxf = s->priv_data; uint32_t c = -1; int i; @@ -1325,6 +1339,8 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, 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 + if (sc->interlaced) + sc->field_dominance = 1 + !(pkt->data[i+4] & 0x80); // top field first break; } } else if (c == 0x1b8) { // gop @@ -1333,21 +1349,6 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, if (e->flags & 0x40) // sequence header present e->flags |= 0x80; // random access } - if (!mxf->header_written) { - unsigned hours = (pkt->data[i+1]>>2) & 0x1f; - unsigned minutes = ((pkt->data[i+1] & 0x03) << 4) | (pkt->data[i+2]>>4); - unsigned seconds = ((pkt->data[i+2] & 0x07) << 3) | (pkt->data[i+3]>>5); - unsigned frames = ((pkt->data[i+3] & 0x1f) << 1) | (pkt->data[i+4]>>7); - mxf->timecode_drop_frame = !!(pkt->data[i+1] & 0x80); - mxf->timecode_start = (hours*3600 + minutes*60 + seconds) * - mxf->timecode_base + frames; - if (mxf->timecode_drop_frame) { - unsigned tminutes = 60 * hours + minutes; - mxf->timecode_start -= 2 * (tminutes - tminutes / 10); - } - av_log(s, AV_LOG_DEBUG, "frame %d %d:%d:%d%c%d\n", mxf->timecode_start, - hours, minutes, seconds, mxf->timecode_drop_frame ? ';':':', frames); - } } else if (c == 0x1b3) { // seq e->flags |= 0x40; switch ((pkt->data[i+4]>>4) & 0xf) { @@ -1409,11 +1410,12 @@ static void mxf_gen_umid(AVFormatContext *s) static int mxf_write_header(AVFormatContext *s) { MXFContext *mxf = s->priv_data; - int i; + int i, ret; uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; const int *samples_per_frame = NULL; AVDictionaryEntry *t; int64_t timestamp = 0; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); if (!s->nb_streams) return -1; @@ -1425,24 +1427,43 @@ static int mxf_write_header(AVFormatContext *s) return AVERROR(ENOMEM); st->priv_data = sc; + if ((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) { + 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) { - if (i != 0) { - av_log(s, AV_LOG_ERROR, "video stream must be first track\n"); - return -1; - } + AVRational rate; 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 }; mxf->timecode_base = 25; + } else if (fabs(av_q2d(st->codec->time_base) - 1/50.0) < 0.0001) { + samples_per_frame = PAL_50_samples_per_frame; + mxf->time_base = (AVRational){ 1, 50 }; + mxf->timecode_base = 50; } 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 }; mxf->timecode_base = 30; + } else if (fabs(av_q2d(st->codec->time_base) - 1001/60000.0) < 0.0001) { + samples_per_frame = NTSC_60_samples_per_frame; + mxf->time_base = (AVRational){ 1001, 60000 }; + mxf->timecode_base = 60; } else { av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n"); return -1; } + rate = (AVRational){mxf->time_base.den, mxf->time_base.num}; 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) + return ret; if (s->oformat == &ff_mxf_d10_muxer) { if (st->codec->bit_rate == 50000000) if (mxf->time_base.den == 25) sc->index = 3; @@ -1545,24 +1566,6 @@ static int mxf_write_header(AVFormatContext *s) static const uint8_t system_metadata_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x01,0x00 }; static const uint8_t system_metadata_package_set_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x43,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x02,0x01 }; -static uint32_t ff_framenum_to_12m_time_code(unsigned frame, int drop, int fps) -{ - return (0 << 31) | // color frame flag - (drop << 30) | // drop frame flag - ( ((frame % fps) / 10) << 28) | // tens of frames - ( ((frame % fps) % 10) << 24) | // units of frames - (0 << 23) | // field phase (NTSC), b0 (PAL) - ((((frame / fps) % 60) / 10) << 20) | // tens of seconds - ((((frame / fps) % 60) % 10) << 16) | // units of seconds - (0 << 15) | // b0 (NTSC), b2 (PAL) - ((((frame / (fps * 60)) % 60) / 10) << 12) | // tens of minutes - ((((frame / (fps * 60)) % 60) % 10) << 8) | // units of minutes - (0 << 7) | // b1 - (0 << 6) | // b2 (NTSC), field phase (PAL) - ((((frame / (fps * 3600) % 24)) / 10) << 4) | // tens of hours - ( (frame / (fps * 3600) % 24)) % 10; // units of hours -} - static void mxf_write_system_item(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -1570,7 +1573,7 @@ static void mxf_write_system_item(AVFormatContext *s) unsigned frame; uint32_t time_code; - frame = mxf->timecode_start + mxf->last_indexed_edit_unit + mxf->edit_units_count; + frame = mxf->last_indexed_edit_unit + mxf->edit_units_count; // write system metadata pack avio_write(pb, system_metadata_pack_key, 16); @@ -1579,7 +1582,7 @@ static void mxf_write_system_item(AVFormatContext *s) avio_w8(pb, 0x04); // content package rate avio_w8(pb, 0x00); // content package type avio_wb16(pb, 0x00); // channel handle - avio_wb16(pb, frame); // continuity count + avio_wb16(pb, mxf->tc.start + frame); // continuity count if (mxf->essence_container_count > 1) avio_write(pb, multiple_desc_ul, 16); else { @@ -1591,7 +1594,7 @@ static void mxf_write_system_item(AVFormatContext *s) avio_wb64(pb, 0); // creation date/time stamp avio_w8(pb, 0x81); // SMPTE 12M time code - time_code = ff_framenum_to_12m_time_code(frame, mxf->timecode_drop_frame, mxf->timecode_base); + time_code = av_timecode_get_smpte_from_framenum(&mxf->tc, frame); avio_wb32(pb, time_code); avio_wb32(pb, 0); // binary group data avio_wb64(pb, 0); |