• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * SCC muxer
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 "libavutil/log.h"
25 #include "libavutil/intreadwrite.h"
26 
27 typedef struct SCCContext {
28     int prev_h, prev_m, prev_s, prev_f;
29     int inside;
30     int n;
31 } SCCContext;
32 
scc_write_header(AVFormatContext * avf)33 static int scc_write_header(AVFormatContext *avf)
34 {
35     SCCContext *scc = avf->priv_data;
36 
37     if (avf->nb_streams != 1 ||
38         avf->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
39         av_log(avf, AV_LOG_ERROR,
40                "SCC supports only a single subtitles stream.\n");
41         return AVERROR(EINVAL);
42     }
43     if (avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_EIA_608) {
44         av_log(avf, AV_LOG_ERROR,
45                "Unsupported subtitles codec: %s\n",
46                avcodec_get_name(avf->streams[0]->codecpar->codec_id));
47         return AVERROR(EINVAL);
48     }
49     avpriv_set_pts_info(avf->streams[0], 64, 1, 1000);
50     avio_printf(avf->pb, "Scenarist_SCC V1.0\n");
51 
52     scc->prev_h = scc->prev_m = scc->prev_s = scc->prev_f = -1;
53     scc->inside = 0;
54 
55     return 0;
56 }
57 
scc_write_packet(AVFormatContext * avf,AVPacket * pkt)58 static int scc_write_packet(AVFormatContext *avf, AVPacket *pkt)
59 {
60     SCCContext *scc = avf->priv_data;
61     int64_t pts = pkt->pts;
62     int i, h, m, s, f;
63 
64     if (pts == AV_NOPTS_VALUE) {
65         av_log(avf, AV_LOG_WARNING,
66                "Insufficient timestamps.\n");
67         return 0;
68     }
69 
70     h = (int)(pts / (3600000));
71     m = (int)(pts / (60000)) % 60;
72     s = (int)(pts /  1000) % 60;
73     f = (int)(pts %  1000) / 33;
74 
75     for (i = 0; i < pkt->size; i+=3) {
76         if (pkt->data[i] == 0xfc && ((pkt->data[i + 1] != 0x80 || pkt->data[i + 2] != 0x80)))
77             break;
78     }
79     if (i >= pkt->size)
80         return 0;
81 
82     if (!scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) {
83         avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f);
84         scc->inside = 1;
85     }
86     for (i = 0; i < pkt->size; i+=3) {
87         if (i + 3 > pkt->size)
88             break;
89 
90         if (pkt->data[i] != 0xfc || (pkt->data[i + 1] == 0x80 && pkt->data[i + 2] == 0x80))
91             continue;
92         if (!scc->inside) {
93             avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f);
94             scc->inside = 1;
95         }
96         if (scc->n > 0)
97             avio_printf(avf->pb, " ");
98         avio_printf(avf->pb, "%02x%02x", pkt->data[i + 1], pkt->data[i + 2]);
99         scc->n++;
100     }
101     if (scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) {
102         avio_printf(avf->pb, "\n");
103         scc->n = 0;
104         scc->inside = 0;
105     }
106 
107     scc->prev_h = h;
108     scc->prev_m = m;
109     scc->prev_s = s;
110     scc->prev_f = f;
111     return 0;
112 }
113 
114 AVOutputFormat ff_scc_muxer = {
115     .name           = "scc",
116     .long_name      = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"),
117     .extensions     = "scc",
118     .priv_data_size = sizeof(SCCContext),
119     .write_header   = scc_write_header,
120     .write_packet   = scc_write_packet,
121     .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
122     .subtitle_codec = AV_CODEC_ID_EIA_608,
123 };
124