X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fgxfenc.c;h=47b64efc9f724912039a56353cb5687b506c8a25;hb=7b2dba1c50c92c76f147727be7c27872252e29f6;hp=3d56eba28755e5441daad5fe69a84894d9f54f24;hpb=2f9d6ffda78725e11b351a7a5d69414a32010810;p=ffmpeg diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index 3d56eba2875..47b64efc9f7 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -19,15 +19,31 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/intfloat_readwrite.h" +#include "libavutil/intfloat.h" +#include "libavutil/opt.h" #include "libavutil/mathematics.h" +#include "libavcodec/timecode.h" #include "avformat.h" +#include "internal.h" #include "gxf.h" #include "riff.h" #include "audiointerleave.h" #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; @@ -48,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; @@ -66,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 { @@ -199,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; @@ -235,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); } @@ -397,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) @@ -407,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 */ @@ -495,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 */ @@ -520,8 +551,8 @@ static int gxf_write_umf_media_dv(AVIOContext *pb, GXFStreamContext *sc) static int gxf_write_umf_media_audio(AVIOContext *pb, GXFStreamContext *sc) { - avio_wl64(pb, av_dbl2int(1)); /* sound level to begin to */ - avio_wl64(pb, av_dbl2int(1)); /* sound level to begin to */ + avio_wl64(pb, av_double2int(1)); /* sound level to begin to */ + avio_wl64(pb, av_double2int(1)); /* sound level to begin to */ avio_wl32(pb, 0); /* number of fields over which to ramp up sound level */ avio_wl32(pb, 0); /* number of fields over which to ramp down sound level */ avio_wl32(pb, 0); /* reserved */ @@ -577,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) { @@ -640,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; @@ -677,7 +727,7 @@ static int gxf_write_header(AVFormatContext *s) } sc->track_type = 2; sc->sample_rate = st->codec->sample_rate; - av_set_pts_info(st, 64, 1, sc->sample_rate); + avpriv_set_pts_info(st, 64, 1, sc->sample_rate); sc->sample_size = 16; sc->frame_rate_index = -2; sc->lines_index = -2; @@ -707,7 +757,7 @@ static int gxf_write_header(AVFormatContext *s) "gxf muxer only accepts PAL or NTSC resolutions currently\n"); return -1; } - av_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den); + avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den); if (gxf_find_lines_index(st) < 0) sc->lines_index = -1; sc->sample_size = st->codec->bit_rate; @@ -758,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 @@ -945,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"), @@ -956,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, };