[FFmpeg-devel] [PATCH 12/12] lavf/sccenc: write proper timecode, use proper stream timebase and split lines properly
Baptiste Coudurier
baptiste.coudurier at gmail.com
Wed Jul 4 21:35:14 EEST 2018
---
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)
More information about the ffmpeg-devel
mailing list