diff options
author | Matthieu Bouron <matthieu.bouron@gmail.com> | 2011-12-06 21:56:01 +0100 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2011-12-07 15:29:26 +0100 |
commit | 070a40f1b7e7f8224108b64639cce29ecd424ce8 (patch) | |
tree | 37d4102ef6596d0bb79e16afe2d502277b2388ae | |
parent | a99dbe014dbd01b227f97e7d7f0f72b3ae4f9be0 (diff) | |
download | ffmpeg-070a40f1b7e7f8224108b64639cce29ecd424ce8.tar.gz |
gxfenc: support timecode option
Reviewed-by: Baptiste Coudurier
-rw-r--r-- | libavformat/gxfenc.c | 104 | ||||
-rw-r--r-- | tests/ref/lavf/gxf | 2 |
2 files changed, 86 insertions, 20 deletions
diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index a274d8e290..02047c68d5 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -20,7 +20,9 @@ */ #include "libavutil/intfloat_readwrite.h" +#include "libavutil/opt.h" #include "libavutil/mathematics.h" +#include "libavcodec/timecode.h" #include "avformat.h" #include "internal.h" #include "gxf.h" @@ -29,6 +31,19 @@ #define GXF_AUDIO_PACKET_SIZE 65536 +#define GXF_TIMECODE(c, d, h, m, s, f) \ + ((c) << 30 | (d) << 29 | (h) << 24 | (m) << 16 | (s) << 8 | (f)) + +typedef struct GXFTimecode{ + int hh; + int mm; + int ss; + int ff; + int color; + int drop; + char *str; +} GXFTimecode; + typedef struct GXFStreamContext { AudioInterleaveContext aic; uint32_t track_type; @@ -49,6 +64,7 @@ typedef struct GXFStreamContext { } GXFStreamContext; typedef struct GXFContext { + AVClass *av_class; uint32_t nb_fields; uint16_t audio_tracks; uint16_t mpeg_tracks; @@ -67,6 +83,7 @@ typedef struct GXFContext { uint64_t *map_offsets; ///< offset of map packets unsigned map_offsets_nb; unsigned packet_count; + GXFTimecode tc; } GXFContext; static const struct { @@ -200,19 +217,21 @@ static int gxf_write_mpeg_auxiliary(AVIOContext *pb, AVStream *st) return size + 3; } -static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFStreamContext *sc) +static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFContext *gxf) { - avio_w8(pb, 0); /* fields */ - avio_w8(pb, 0); /* seconds */ - avio_w8(pb, 0); /* minutes */ - avio_w8(pb, 0); /* flags + hours */ + uint32_t timecode = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + avio_wl32(pb, timecode); /* reserved */ - avio_wb32(pb, 0); + avio_wl32(pb, 0); return 8; } static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, int index) { + GXFContext *gxf = s->priv_data; AVIOContext *pb = s->pb; int64_t pos; int mpeg = sc->track_type == 4 || sc->track_type == 9; @@ -236,7 +255,7 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, avio_w8(pb, TRACK_AUX); avio_w8(pb, 8); if (sc->track_type == 3) - gxf_write_timecode_auxiliary(pb, sc); + gxf_write_timecode_auxiliary(pb, gxf); else avio_wl64(pb, 0); } @@ -398,7 +417,9 @@ static int gxf_write_umf_material_description(AVFormatContext *s) int timecode_base = gxf->time_base.den == 60000 ? 60 : 50; int64_t timestamp = 0; AVDictionaryEntry *t; - uint32_t timecode; + uint64_t nb_fields; + uint32_t timecode_in; // timecode at mark in + uint32_t timecode_out; // timecode at mark out #if FF_API_TIMESTAMP if (s->timestamp) @@ -408,20 +429,29 @@ static int gxf_write_umf_material_description(AVFormatContext *s) if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) timestamp = ff_iso8601_to_unix_time(t->value); - // XXX drop frame - timecode = - gxf->nb_fields / (timecode_base * 3600) % 24 << 24 | // hours - gxf->nb_fields / (timecode_base * 60) % 60 << 16 | // minutes - gxf->nb_fields / timecode_base % 60 << 8 | // seconds - gxf->nb_fields % timecode_base; // fields + timecode_in = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + nb_fields = gxf->nb_fields + + gxf->tc.hh * (timecode_base * 3600) + + gxf->tc.mm * (timecode_base * 60) + + gxf->tc.ss * timecode_base + + gxf->tc.ff; + + timecode_out = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + nb_fields / (timecode_base * 3600) % 24, + nb_fields / (timecode_base * 60) % 60, + nb_fields / timecode_base % 60, + nb_fields % timecode_base); avio_wl32(pb, gxf->flags); avio_wl32(pb, gxf->nb_fields); /* length of the longest track */ avio_wl32(pb, gxf->nb_fields); /* length of the shortest track */ avio_wl32(pb, 0); /* mark in */ avio_wl32(pb, gxf->nb_fields); /* mark out */ - avio_wl32(pb, 0); /* timecode mark in */ - avio_wl32(pb, timecode); /* timecode mark out */ + avio_wl32(pb, timecode_in); /* timecode mark in */ + avio_wl32(pb, timecode_out); /* timecode mark out */ avio_wl64(pb, timestamp); /* modification time */ avio_wl64(pb, timestamp); /* creation time */ avio_wl16(pb, 0); /* reserved */ @@ -496,9 +526,9 @@ static int gxf_write_umf_media_mpeg(AVIOContext *pb, AVStream *st) return 32; } -static int gxf_write_umf_media_timecode(AVIOContext *pb, GXFStreamContext *sc) +static int gxf_write_umf_media_timecode(AVIOContext *pb, int drop) { - avio_wl32(pb, 1); /* non drop frame */ + avio_wl32(pb, drop); /* drop frame */ avio_wl32(pb, 0); /* reserved */ avio_wl32(pb, 0); /* reserved */ avio_wl32(pb, 0); /* reserved */ @@ -578,7 +608,7 @@ static int gxf_write_umf_media_description(AVFormatContext *s) avio_wl32(pb, 0); /* reserved */ if (sc == &gxf->timecode_track) - gxf_write_umf_media_timecode(pb, sc); /* 8 0bytes */ + gxf_write_umf_media_timecode(pb, gxf->tc.drop); else { AVStream *st = s->streams[i]; switch (st->codec->codec_id) { @@ -641,6 +671,25 @@ static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc) sc->fields = vsc->fields; } +static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, int fields) +{ + char c; + + if (sscanf(tc->str, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) { + av_log(s, AV_LOG_ERROR, "unable to parse timecode, " + "syntax: hh:mm:ss[:;.]ff\n"); + return -1; + } + + tc->color = 0; + tc->drop = c != ':'; + + if (fields == 2) + tc->ff = tc->ff * 2; + + return 0; +} + static int gxf_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; @@ -759,6 +808,10 @@ static int gxf_write_header(AVFormatContext *s) if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0) return -1; + if (gxf->tc.str) { + gxf_init_timecode(s, &gxf->tc, vsc->fields); + } + gxf_init_timecode_track(&gxf->timecode_track, vsc); gxf->flags |= 0x200000; // time code track is non-drop frame @@ -946,6 +999,18 @@ static int gxf_interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *pk av_interleave_packet_per_dts, gxf_compare_field_nb); } +static const AVOption options[] = { + { TIMECODE_OPT(GXFContext, AV_OPT_FLAG_ENCODING_PARAM) }, + { NULL } +}; + +static const AVClass gxf_muxer_class = { + .class_name = "GXF muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_gxf_muxer = { .name = "gxf", .long_name = NULL_IF_CONFIG_SMALL("GXF format"), @@ -957,4 +1022,5 @@ AVOutputFormat ff_gxf_muxer = { .write_packet = gxf_write_packet, .write_trailer = gxf_write_trailer, .interleave_packet = gxf_interleave_packet, + .priv_class = &gxf_muxer_class, }; diff --git a/tests/ref/lavf/gxf b/tests/ref/lavf/gxf index 68bc9785a2..57839c9c9e 100644 --- a/tests/ref/lavf/gxf +++ b/tests/ref/lavf/gxf @@ -1,3 +1,3 @@ -346d38d330ab5cb0caa6b5537167bc0d *./tests/data/lavf/lavf.gxf +1c1693cf2358025f1e37ac76e1da925a *./tests/data/lavf/lavf.gxf 796392 ./tests/data/lavf/lavf.gxf ./tests/data/lavf/lavf.gxf CRC=0x102918fd |