--- libavformat/sccenc.c | 90 ++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 36 deletions(-)
diff --git a/libavformat/sccenc.c b/libavformat/sccenc.c index f3cf3d599c..dd6a4b36d8 100644 --- a/libavformat/sccenc.c +++ b/libavformat/sccenc.c @@ -23,16 +23,22 @@ #include "internal.h" #include "libavutil/log.h" #include "libavutil/intreadwrite.h" +#include "libavutil/timecode.h" +#include "libavutil/opt.h" typedef struct SCCContext { - int prev_h, prev_m, prev_s, prev_f; + AVClass *av_class; + int64_t expected_pts; int inside; int n; + AVTimecode tc; + char *timecode_start; } SCCContext; static int scc_write_header(AVFormatContext *avf) { SCCContext *scc = avf->priv_data; + AVStream *st = avf->streams[0]; if (avf->nb_streams != 1 || avf->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { @@ -46,11 +52,19 @@ static int scc_write_header(AVFormatContext *avf) avcodec_get_name(avf->streams[0]->codecpar->codec_id)); return AVERROR(EINVAL); } - avpriv_set_pts_info(avf->streams[0], 64, 1, 1000); - avio_printf(avf->pb, "Scenarist_SCC V1.0\n"); - scc->prev_h = scc->prev_m = scc->prev_s = scc->prev_f = -1; - scc->inside = 0; + if (!(st->time_base.den == 30000 && st->time_base.num == 1001)) { + av_log(avf, AV_LOG_ERROR, "Unsupported frame rate: %d/%d\n", + st->time_base.den, st->time_base.num); + return AVERROR(EINVAL); + } + + if (av_timecode_init_from_string(&scc->tc, (AVRational){30000, 1001}, scc->timecode_start, avf) < 0) + return -1; + + avio_printf(avf->pb, "Scenarist_SCC V1.0"); + + scc->expected_pts = -2; return 0; } @@ -59,58 +73,60 @@ static int scc_write_packet(AVFormatContext *avf, AVPacket *pkt) { SCCContext *scc = avf->priv_data; int64_t pts = pkt->pts; - int i, h, m, s, f; + char tcbuf[AV_TIMECODE_STR_SIZE]; + int i; if (pts == AV_NOPTS_VALUE) { - av_log(avf, AV_LOG_WARNING, - "Insufficient timestamps.\n"); - return 0; + av_log(avf, AV_LOG_WARNING, "Insufficient timestamps\n"); + return -1; } - h = (int)(pts / (3600000)); - m = (int)(pts / (60000)) % 60; - s = (int)(pts / 1000) % 60; - f = (int)(pts % 1000) / 33; + if (!av_timecode_make_string(&scc->tc, tcbuf, pts)) + return -1; - for (i = 0; i < pkt->size; i+=3) { - if (pkt->data[i] == 0xfc && ((pkt->data[i + 1] != 0x80 || pkt->data[i + 2] != 0x80))) - break; - } - if (i >= pkt->size) - return 0; - - if (!scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) { - avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f); - scc->inside = 1; - } for (i = 0; i < pkt->size; i+=3) { if (i + 3 > pkt->size) break; if (pkt->data[i] != 0xfc || (pkt->data[i + 1] == 0x80 && pkt->data[i + 2] == 0x80)) continue; - if (!scc->inside) { - avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f); - scc->inside = 1; + + if (pts > scc->expected_pts+1 || AV_RB16(&pkt->data[i + 1]) == 0x942c) { + avio_printf(avf->pb, "\r\n\n%s\t", tcbuf); + scc->expected_pts = pts; + scc->n = 0; } + if (scc->n > 0) avio_printf(avf->pb, " "); + avio_printf(avf->pb, "%02x%02x", pkt->data[i + 1], pkt->data[i + 2]); + scc->expected_pts += 1; scc->n++; } - if (scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) { - avio_printf(avf->pb, "\n"); - scc->n = 0; - scc->inside = 0; - } - scc->prev_h = h; - scc->prev_m = m; - scc->prev_s = s; - scc->prev_f = f; return 0; } +static int scc_write_trailer(AVFormatContext *avf) +{ + avio_printf(avf->pb, "\n\n"); + return 0; +} + +static const AVOption scc_options[] = { + { "timecode_start", "Set the SCC file initial timecode", + offsetof(SCCContext, timecode_start), AV_OPT_TYPE_STRING, {.str = "00:00:00;00"}, CHAR_MIN, CHAR_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { NULL }, +}; + +static const AVClass scc_muxer_class = { + .class_name = "scc muxer", + .item_name = av_default_item_name, + .option = scc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_scc_muxer = { .name = "scc", .long_name = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"), @@ -118,6 +134,8 @@ AVOutputFormat ff_scc_muxer = { .priv_data_size = sizeof(SCCContext), .write_header = scc_write_header, .write_packet = scc_write_packet, + .write_trailer = scc_write_trailer, .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, .subtitle_codec = AV_CODEC_ID_EIA_608, + .priv_class = &scc_muxer_class, }; -- 2.17.0 (Apple Git-106) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel