1 /*****************************************************************************
2  * stl.c: EBU STL demuxer
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  * $Id: 238b515cc66f481b889d219a14b1e871e7440aff $
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
31 
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_demux.h>
35 
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 static int  Open (vlc_object_t *);
40 static void Close(vlc_object_t *);
41 
42 vlc_module_begin()
43     set_description(N_("EBU STL subtitles parser"))
44     set_category(CAT_INPUT)
45     set_subcategory(SUBCAT_INPUT_DEMUX)
46     set_capability("demux", 1)
47     set_callbacks(Open, Close)
48     add_shortcut("stl", "subtitle")
49 vlc_module_end()
50 
51 /*****************************************************************************
52  * Local definitions/prototypes
53  *****************************************************************************/
54 typedef struct {
55     mtime_t start;
56     mtime_t stop;
57     size_t  blocknumber;
58     size_t  count;
59 } stl_entry_t;
60 
61 struct demux_sys_t {
62     size_t      count;
63     stl_entry_t *index;
64 
65     es_out_id_t *es;
66 
67     size_t      current;
68     int64_t     next_date;
69     bool        b_slave;
70     bool        b_first_time;
71 };
72 
ParseInteger(uint8_t * data,size_t size)73 static size_t ParseInteger(uint8_t *data, size_t size)
74 {
75     char tmp[16];
76     assert(size < sizeof(tmp));
77     memcpy(tmp, data, size);
78     tmp[size] = '\0';
79 
80     return strtol(tmp, NULL, 10);
81 }
ParseTimeCode(uint8_t * data,double fps)82 static int64_t ParseTimeCode(uint8_t *data, double fps)
83 {
84     return INT64_C(1000000) * (data[0] * 3600 +
85                                data[1] *   60 +
86                                data[2] *    1 +
87                                data[3] /  fps);
88 }
ParseTextTimeCode(uint8_t * data,double fps)89 static int64_t ParseTextTimeCode(uint8_t *data, double fps)
90 {
91     uint8_t tmp[4];
92     for (int i = 0; i < 4; i++)
93         tmp[i] = ParseInteger(&data[2 * i], 2);
94     return ParseTimeCode(tmp, fps);
95 }
96 
Control(demux_t * demux,int query,va_list args)97 static int Control(demux_t *demux, int query, va_list args)
98 {
99     demux_sys_t *sys = demux->p_sys;
100     switch(query) {
101     case DEMUX_CAN_SEEK:
102         return vlc_stream_vaControl(demux->s, query, args);
103     case DEMUX_GET_LENGTH: {
104         int64_t *l = va_arg(args, int64_t *);
105         *l = sys->count > 0 ? sys->index[sys->count-1].stop : 0;
106         return VLC_SUCCESS;
107     }
108     case DEMUX_GET_TIME: {
109         int64_t *t = va_arg(args, int64_t *);
110         *t = sys->next_date - var_GetInteger(demux->obj.parent, "spu-delay");
111         if( *t < 0 )
112             *t = sys->next_date;
113         return VLC_SUCCESS;
114     }
115     case DEMUX_SET_NEXT_DEMUX_TIME: {
116         sys->b_slave = true;
117         sys->next_date = va_arg(args, int64_t);
118         return VLC_SUCCESS;
119     }
120     case DEMUX_SET_TIME: {
121         int64_t t = va_arg(args, int64_t);
122         for( size_t i = 0; i + 1< sys->count; i++ )
123         {
124             if( sys->index[i + 1].start >= t &&
125                 vlc_stream_Seek(demux->s, 1024 + 128LL * sys->index[i].blocknumber) == VLC_SUCCESS )
126             {
127                 sys->current = i;
128                 sys->next_date = t;
129                 sys->b_first_time = true;
130                 return VLC_SUCCESS;
131             }
132         }
133         break;
134     }
135     case DEMUX_SET_POSITION:
136     {
137         double f = va_arg( args, double );
138         if(sys->count && sys->index[sys->count-1].stop > 0)
139         {
140             int64_t i64 = f * sys->index[sys->count-1].stop;
141             return demux_Control(demux, DEMUX_SET_TIME, i64);
142         }
143         break;
144     }
145     case DEMUX_GET_POSITION:
146     {
147         double *pf = va_arg(args, double *);
148         if(sys->current >= sys->count)
149         {
150             *pf = 1.0;
151         }
152         else if(sys->count > 0 && sys->index[sys->count-1].stop > 0)
153         {
154             *pf = sys->next_date - var_GetInteger(demux->obj.parent, "spu-delay");
155             if(*pf < 0)
156                *pf = sys->next_date;
157             *pf /= sys->index[sys->count-1].stop;
158         }
159         else
160         {
161             *pf = 0.0;
162         }
163         return VLC_SUCCESS;
164     }
165     default:
166         break;
167     }
168     return VLC_EGENERIC;
169 }
170 
Demux(demux_t * demux)171 static int Demux(demux_t *demux)
172 {
173     demux_sys_t *sys = demux->p_sys;
174 
175     int64_t i_barrier = sys->next_date - var_GetInteger(demux->obj.parent, "spu-delay");
176     if(i_barrier < 0)
177         i_barrier = sys->next_date;
178 
179     while(sys->current < sys->count &&
180           sys->index[sys->current].start <= i_barrier)
181     {
182         stl_entry_t *s = &sys->index[sys->current];
183 
184         if (!sys->b_slave && sys->b_first_time)
185         {
186             es_out_SetPCR(demux->out, VLC_TS_0 + i_barrier);
187             sys->b_first_time = false;
188         }
189 
190         /* Might be a gap in block # */
191         const uint64_t i_pos = 1024 + 128LL * s->blocknumber;
192         if(i_pos != vlc_stream_Tell(demux->s) &&
193            vlc_stream_Seek( demux->s, i_pos ) != VLC_SUCCESS )
194             return VLC_DEMUXER_EOF;
195 
196         block_t *b = vlc_stream_Block(demux->s, 128);
197         if (b && b->i_buffer == 128)
198         {
199             b->i_dts =
200             b->i_pts = VLC_TS_0 + s->start;
201             if (s->stop > s->start)
202                 b->i_length = s->stop - s->start;
203             es_out_Send(demux->out, sys->es, b);
204         }
205         else
206         {
207             if(b)
208                 block_Release(b);
209             return VLC_DEMUXER_EOF;
210         }
211         sys->current++;
212     }
213 
214     if (!sys->b_slave)
215     {
216         es_out_SetPCR(demux->out, VLC_TS_0 + i_barrier);
217         sys->next_date += CLOCK_FREQ / 8;
218     }
219 
220     return sys->current < sys->count ? VLC_DEMUXER_SUCCESS : VLC_DEMUXER_EOF;
221 }
222 
Open(vlc_object_t * object)223 static int Open(vlc_object_t *object)
224 {
225     demux_t *demux = (demux_t*)object;
226 
227     const uint8_t *peek;
228     if (vlc_stream_Peek(demux->s, &peek, 11) != 11)
229         return VLC_EGENERIC;
230 
231     bool is_stl_25 = !memcmp(&peek[3], "STL25.01", 8);
232     bool is_stl_30 = !memcmp(&peek[3], "STL30.01", 8);
233     if (!is_stl_25 && !is_stl_30)
234         return VLC_EGENERIC;
235     const double fps = is_stl_25 ? 25 : 30;
236 
237     uint8_t header[1024];
238     if (vlc_stream_Read(demux->s, header, sizeof(header)) != sizeof(header)) {
239         msg_Err(demux, "Incomplete EBU STL header");
240         return VLC_EGENERIC;
241     }
242     const int cct = ParseInteger(&header[12], 2);
243     const mtime_t program_start = ParseTextTimeCode(&header[256], fps);
244     const size_t tti_count = ParseInteger(&header[238], 5);
245     if (!tti_count)
246         return VLC_EGENERIC;
247     msg_Dbg(demux, "Detected EBU STL : CCT=%d TTI=%zu start=%8.8s %"PRId64, cct, tti_count, &header[256], program_start);
248 
249     demux_sys_t *sys = malloc(sizeof(*sys));
250     if(!sys)
251         return VLC_EGENERIC;
252 
253     sys->b_slave   = false;
254     sys->b_first_time = true;
255     sys->next_date = 0;
256     sys->current   = 0;
257     sys->count     = 0;
258     sys->index     = calloc(tti_count, sizeof(*sys->index));
259     if(!sys->index)
260     {
261         free(sys);
262         return VLC_EGENERIC;
263     }
264 
265     bool comment = false;
266     stl_entry_t *s = &sys->index[0];
267     s->count = 0;
268 
269     for (size_t i = 0; i < tti_count; i++) {
270         uint8_t tti[16];
271         if (vlc_stream_Read(demux->s, tti, 16) != 16 ||
272             vlc_stream_Read(demux->s, NULL, 112) != 112) {
273             msg_Warn(demux, "Incomplete EBU STL file");
274             break;
275         }
276         const int ebn = tti[3];
277         if (ebn >= 0xf0 && ebn <= 0xfd)
278             continue;
279         if (ebn == 0xfe)
280             continue;
281 
282         if (s->count <= 0) {
283             comment  = tti[15] != 0;
284             s->start = ParseTimeCode(&tti[5], fps) - program_start;
285             s->stop  = ParseTimeCode(&tti[9], fps) - program_start;
286             s->blocknumber = i;
287         }
288         s->count++;
289         if (ebn == 0xff && !comment)
290             s = &sys->index[++sys->count];
291         if (ebn == 0xff && sys->count < tti_count)
292             s->count = 0;
293     }
294 
295     demux->p_sys = sys;
296     if (sys->count == 0 ||
297         vlc_stream_Seek(demux->s, 1024 + 128LL * sys->index[0].blocknumber) != VLC_SUCCESS)
298     {
299         Close(object);
300         return VLC_EGENERIC;
301     }
302 
303     es_format_t fmt;
304     es_format_Init(&fmt, SPU_ES, VLC_CODEC_EBU_STL);
305     fmt.i_extra = sizeof(header);
306     fmt.p_extra = header;
307 
308     sys->es = es_out_Add(demux->out, &fmt);
309     fmt.i_extra = 0;
310     fmt.p_extra = NULL;
311     es_format_Clean(&fmt);
312 
313     if(sys->es == NULL)
314     {
315         Close(object);
316         return VLC_EGENERIC;
317     }
318 
319     demux->p_sys      = sys;
320     demux->pf_demux   = Demux;
321     demux->pf_control = Control;
322     return VLC_SUCCESS;
323 }
324 
Close(vlc_object_t * object)325 static void Close(vlc_object_t *object)
326 {
327     demux_t *demux = (demux_t*)object;
328     demux_sys_t *sys = demux->p_sys;
329 
330     free(sys->index);
331     free(sys);
332 }
333 
334