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