From 45ff6ef50e2ada0c473ee662f84957186e06a232 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Tue, 24 Jan 2017 16:34:29 +0100 Subject: avformat: add Scenarist Closed Captions demuxer Fixes #4767. Signed-off-by: Paul B Mahol --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/sccdec.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 2 +- 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 libavformat/sccdec.c (limited to 'libavformat') diff --git a/libavformat/Makefile b/libavformat/Makefile index 036e7a9a71..e2919259bc 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -434,6 +434,7 @@ OBJS-$(CONFIG_SAMI_DEMUXER) += samidec.o subtitles.o OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o OBJS-$(CONFIG_SAP_MUXER) += sapenc.o OBJS-$(CONFIG_SBG_DEMUXER) += sbgdec.o +OBJS-$(CONFIG_SCC_DEMUXER) += sccdec.o subtitles.o OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o OBJS-$(CONFIG_SDS_DEMUXER) += sdsdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index b8590cec99..9c1f6dd923 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -273,6 +273,7 @@ void av_register_all(void) REGISTER_DEMUXER (SAMI, sami); REGISTER_MUXDEMUX(SAP, sap); REGISTER_DEMUXER (SBG, sbg); + REGISTER_DEMUXER (SCC, scc); REGISTER_DEMUXER (SDP, sdp); REGISTER_DEMUXER (SDR2, sdr2); REGISTER_DEMUXER (SDS, sds); diff --git a/libavformat/sccdec.c b/libavformat/sccdec.c new file mode 100644 index 0000000000..a4ce5572a4 --- /dev/null +++ b/libavformat/sccdec.c @@ -0,0 +1,183 @@ +/* + * SCC subtitle demuxer + * Copyright (c) 2017 Paul B Mahol + * + * This file is part of FFmpeg. + * + * 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. + * + * 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct SCCContext { + FFDemuxSubtitlesQueue q; +} SCCContext; + +static int scc_probe(AVProbeData *p) +{ + char buf[128]; + FFTextReader tr; + + ff_text_init_buf(&tr, p->buf, p->buf_size); + + while (ff_text_peek_r8(&tr) == '\r' || ff_text_peek_r8(&tr) == '\n') + ff_text_r8(&tr); + + if (ff_subtitles_read_line(&tr, buf, sizeof(buf)) < 0) + return 0; + + if (!memcmp(buf, "Scenarist_SCC V1.0", 18)) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int convert(uint8_t x) +{ + if (x >= 'a') + x -= 87; + else if (x >= 'A') + x -= 55; + else + x -= '0'; + return x; +} + +static int scc_read_header(AVFormatContext *s) +{ + SCCContext *scc = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + char line[4096], line2[4096]; + int count = 0, ret = 0; + ptrdiff_t len2, len; + uint8_t out[4096]; + FFTextReader tr; + + ff_text_init_avio(s, &tr, s->pb); + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codecpar->codec_id = AV_CODEC_ID_EIA_608; + + while (!ff_text_eof(&tr)) { + const int64_t pos = ff_text_pos(&tr); + char *saveptr = NULL, *lline; + int hh1, mm1, ss1, fs1, i; + int hh2, mm2, ss2, fs2; + int64_t ts_start, ts_end; + AVPacket *sub; + + if (count == 0) { + while (!ff_text_eof(&tr)) { + len = ff_subtitles_read_line(&tr, line, sizeof(line)); + if (len > 13) + break; + } + } + + if (!strncmp(line, "Scenarist_SCC V1.0", 18)) + continue; + if (sscanf(line, "%d:%d:%d:%d", &hh1, &mm1, &ss1, &fs1) != 4 && + sscanf(line, "%d:%d:%d;%d", &hh1, &mm1, &ss1, &fs1) != 4) + continue; + + ts_start = (hh1 * 3600LL + mm1 * 60LL + ss1) * 1000LL + fs1 * 33; + + while (!ff_text_eof(&tr)) { + len2 = ff_subtitles_read_line(&tr, line2, sizeof(line2)); + if (len2 > 13) + break; + } + if (sscanf(line2, "%d:%d:%d:%d", &hh2, &mm2, &ss2, &fs2) != 4 && + sscanf(line2, "%d:%d:%d;%d", &hh2, &mm2, &ss2, &fs2) != 4) + continue; + + ts_end = (hh2 * 3600LL + mm2 * 60LL + ss2) * 1000LL + fs2 * 33; + count++; + + lline = (char *)&line; + lline += 12; + + for (i = 0; i < 4095; i += 3) { + char *ptr = av_strtok(lline, " ", &saveptr); + char c1, c2, c3, c4; + + if (!ptr) + break; + + if (sscanf(ptr, "%c%c%c%c", &c1, &c2, &c3, &c4) != 4) + break; + + lline = NULL; + out[i+0] = 0xfc; + out[i+1] = convert(c2) | (convert(c1) << 4); + out[i+2] = convert(c4) | (convert(c3) << 4); + } + out[i] = 0; + + sub = ff_subtitles_queue_insert(&scc->q, out, i, 0); + if (!sub) + return AVERROR(ENOMEM); + + sub->pos = pos; + sub->pts = ts_start; + sub->duration = FFMAX(1200, ts_end - ts_start); + memmove(line, line2, sizeof(line)); + FFSWAP(ptrdiff_t, len, len2); + } + + ff_subtitles_queue_finalize(s, &scc->q); + + return ret; +} + +static int scc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SCCContext *scc = s->priv_data; + return ff_subtitles_queue_read_packet(&scc->q, pkt); +} + +static int scc_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + SCCContext *scc = s->priv_data; + return ff_subtitles_queue_seek(&scc->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int scc_read_close(AVFormatContext *s) +{ + SCCContext *scc = s->priv_data; + ff_subtitles_queue_clean(&scc->q); + return 0; +} + +AVInputFormat ff_scc_demuxer = { + .name = "scc", + .long_name = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"), + .priv_data_size = sizeof(SCCContext), + .read_probe = scc_probe, + .read_header = scc_read_header, + .read_packet = scc_read_packet, + .read_seek2 = scc_read_seek, + .read_close = scc_read_close, + .extensions = "scc", +}; diff --git a/libavformat/version.h b/libavformat/version.h index 84ce1fd970..edd666befc 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 57 -#define LIBAVFORMAT_VERSION_MINOR 64 +#define LIBAVFORMAT_VERSION_MINOR 65 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- cgit v1.2.3