1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * SCC subtitle demuxer 3cabdff1aSopenharmony_ci * Copyright (c) 2017 Paul B Mahol 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci#include "avformat.h" 23cabdff1aSopenharmony_ci#include "internal.h" 24cabdff1aSopenharmony_ci#include "subtitles.h" 25cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 26cabdff1aSopenharmony_ci#include "libavutil/bprint.h" 27cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 28cabdff1aSopenharmony_ci 29cabdff1aSopenharmony_citypedef struct SCCContext { 30cabdff1aSopenharmony_ci FFDemuxSubtitlesQueue q; 31cabdff1aSopenharmony_ci} SCCContext; 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_cistatic int scc_probe(const AVProbeData *p) 34cabdff1aSopenharmony_ci{ 35cabdff1aSopenharmony_ci char buf[18]; 36cabdff1aSopenharmony_ci FFTextReader tr; 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci ff_text_init_buf(&tr, p->buf, p->buf_size); 39cabdff1aSopenharmony_ci 40cabdff1aSopenharmony_ci while (ff_text_peek_r8(&tr) == '\r' || ff_text_peek_r8(&tr) == '\n') 41cabdff1aSopenharmony_ci ff_text_r8(&tr); 42cabdff1aSopenharmony_ci 43cabdff1aSopenharmony_ci ff_text_read(&tr, buf, sizeof(buf)); 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_ci if (!memcmp(buf, "Scenarist_SCC V1.0", 18)) 46cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX; 47cabdff1aSopenharmony_ci 48cabdff1aSopenharmony_ci return 0; 49cabdff1aSopenharmony_ci} 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_cistatic int convert(uint8_t x) 52cabdff1aSopenharmony_ci{ 53cabdff1aSopenharmony_ci if (x >= 'a') 54cabdff1aSopenharmony_ci x -= 'a' - 10; 55cabdff1aSopenharmony_ci else if (x >= 'A') 56cabdff1aSopenharmony_ci x -= 'A' - 10; 57cabdff1aSopenharmony_ci else 58cabdff1aSopenharmony_ci x -= '0'; 59cabdff1aSopenharmony_ci return x; 60cabdff1aSopenharmony_ci} 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_cistatic int scc_read_header(AVFormatContext *s) 63cabdff1aSopenharmony_ci{ 64cabdff1aSopenharmony_ci SCCContext *scc = s->priv_data; 65cabdff1aSopenharmony_ci AVStream *st = avformat_new_stream(s, NULL); 66cabdff1aSopenharmony_ci AVPacket *sub = NULL; 67cabdff1aSopenharmony_ci ptrdiff_t len; 68cabdff1aSopenharmony_ci uint8_t out[4096]; 69cabdff1aSopenharmony_ci FFTextReader tr; 70cabdff1aSopenharmony_ci 71cabdff1aSopenharmony_ci ff_text_init_avio(s, &tr, s->pb); 72cabdff1aSopenharmony_ci 73cabdff1aSopenharmony_ci if (!st) 74cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 75cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, 1000); 76cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; 77cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_EIA_608; 78cabdff1aSopenharmony_ci 79cabdff1aSopenharmony_ci while (1) { 80cabdff1aSopenharmony_ci char *saveptr = NULL, *lline; 81cabdff1aSopenharmony_ci int hh, mm, ss, fs, i; 82cabdff1aSopenharmony_ci char line[4096]; 83cabdff1aSopenharmony_ci int64_t pos, ts; 84cabdff1aSopenharmony_ci 85cabdff1aSopenharmony_ci pos = ff_text_pos(&tr); 86cabdff1aSopenharmony_ci len = ff_subtitles_read_line(&tr, line, sizeof(line)); 87cabdff1aSopenharmony_ci if (len <= 13) { 88cabdff1aSopenharmony_ci if (ff_text_eof(&tr)) 89cabdff1aSopenharmony_ci break; 90cabdff1aSopenharmony_ci continue; 91cabdff1aSopenharmony_ci } 92cabdff1aSopenharmony_ci if (av_sscanf(line, "%d:%d:%d%*[:;]%d", &hh, &mm, &ss, &fs) != 4) 93cabdff1aSopenharmony_ci continue; 94cabdff1aSopenharmony_ci 95cabdff1aSopenharmony_ci ts = (hh * 3600LL + mm * 60LL + ss) * 1000LL + fs * 33LL; 96cabdff1aSopenharmony_ci if (sub) 97cabdff1aSopenharmony_ci sub->duration = ts - sub->pts; 98cabdff1aSopenharmony_ci 99cabdff1aSopenharmony_ci lline = line; 100cabdff1aSopenharmony_ci lline += 12; 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci for (i = 0; i < 4095; i += 3) { 103cabdff1aSopenharmony_ci char *ptr = av_strtok(lline, " ", &saveptr); 104cabdff1aSopenharmony_ci char c1, c2, c3, c4; 105cabdff1aSopenharmony_ci uint8_t o1, o2; 106cabdff1aSopenharmony_ci 107cabdff1aSopenharmony_ci if (!ptr) 108cabdff1aSopenharmony_ci break; 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_ci if (av_sscanf(ptr, "%c%c%c%c", &c1, &c2, &c3, &c4) != 4) 111cabdff1aSopenharmony_ci break; 112cabdff1aSopenharmony_ci o1 = convert(c2) | (convert(c1) << 4); 113cabdff1aSopenharmony_ci o2 = convert(c4) | (convert(c3) << 4); 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci lline = NULL; 116cabdff1aSopenharmony_ci 117cabdff1aSopenharmony_ci if (i > 12 && o1 == 0x94 && o2 == 0x20 && saveptr && 118cabdff1aSopenharmony_ci (av_strncasecmp(saveptr, "942f", 4) && !av_strncasecmp(saveptr, "942c", 4))) { 119cabdff1aSopenharmony_ci 120cabdff1aSopenharmony_ci sub = ff_subtitles_queue_insert(&scc->q, out, i, 0); 121cabdff1aSopenharmony_ci if (!sub) 122cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 123cabdff1aSopenharmony_ci 124cabdff1aSopenharmony_ci sub->pos = pos; 125cabdff1aSopenharmony_ci pos += i; 126cabdff1aSopenharmony_ci sub->pts = ts; 127cabdff1aSopenharmony_ci sub->duration = i * 11; 128cabdff1aSopenharmony_ci ts += sub->duration; 129cabdff1aSopenharmony_ci i = 0; 130cabdff1aSopenharmony_ci } 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci out[i+0] = 0xfc; 133cabdff1aSopenharmony_ci out[i+1] = o1; 134cabdff1aSopenharmony_ci out[i+2] = o2; 135cabdff1aSopenharmony_ci } 136cabdff1aSopenharmony_ci 137cabdff1aSopenharmony_ci sub = ff_subtitles_queue_insert(&scc->q, out, i, 0); 138cabdff1aSopenharmony_ci if (!sub) 139cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci sub->pos = pos; 142cabdff1aSopenharmony_ci sub->pts = ts; 143cabdff1aSopenharmony_ci } 144cabdff1aSopenharmony_ci 145cabdff1aSopenharmony_ci ff_subtitles_queue_finalize(s, &scc->q); 146cabdff1aSopenharmony_ci 147cabdff1aSopenharmony_ci return 0; 148cabdff1aSopenharmony_ci} 149cabdff1aSopenharmony_ci 150cabdff1aSopenharmony_ciconst AVInputFormat ff_scc_demuxer = { 151cabdff1aSopenharmony_ci .name = "scc", 152cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"), 153cabdff1aSopenharmony_ci .priv_data_size = sizeof(SCCContext), 154cabdff1aSopenharmony_ci .flags_internal = FF_FMT_INIT_CLEANUP, 155cabdff1aSopenharmony_ci .read_probe = scc_probe, 156cabdff1aSopenharmony_ci .read_header = scc_read_header, 157cabdff1aSopenharmony_ci .extensions = "scc", 158cabdff1aSopenharmony_ci .read_packet = ff_subtitles_read_packet, 159cabdff1aSopenharmony_ci .read_seek2 = ff_subtitles_read_seek, 160cabdff1aSopenharmony_ci .read_close = ff_subtitles_read_close, 161cabdff1aSopenharmony_ci}; 162