OSDN Git Service

fix VC1 I-frame detection in ffmpeg_is_keyframe
[handbrake-jp/handbrake-jp-git.git] / libhb / decsrtsub.c
1 /* 
2    This file is part of the HandBrake source code.
3    Homepage: <http://handbrake.fr/>.
4    It may be used under the terms of the GNU General Public License. */
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <iconv.h>
10 #include <errno.h>
11 #include "hb.h"
12
13 struct start_and_end {
14     unsigned long start, end;
15 };
16
17 enum
18 {
19     k_state_inEntry,
20     k_state_potential_new_entry,
21     k_state_timecode,
22 };
23
24 typedef struct srt_entry_s {
25     long offset, duration;
26     long start, stop;
27     char text[1024];
28     int  pos;
29 } srt_entry_t;
30
31 /*
32  * Store all context in the work private struct,
33  */
34 struct hb_work_private_s
35 {
36     hb_job_t * job;
37     FILE     * file;
38     char       buf[1024];
39     int        pos;
40     int        end;
41     char       utf8_buf[2048];
42     int        utf8_pos;
43     int        utf8_end;
44     unsigned long current_time;
45     unsigned long number_of_entries;
46     unsigned long current_state;
47     srt_entry_t current_entry;
48     iconv_t *iconv_context;
49     hb_subtitle_t *subtitle;
50     uint64_t start_time;              // In HB time
51     uint64_t stop_time;               // In HB time
52 };
53
54 static struct start_and_end read_time_from_string( const char* timeString ) 
55 {
56     // for ex. 00:00:15,248 --> 00:00:16,545
57     
58     long houres1, minutes1, seconds1, milliseconds1,
59         houres2, minutes2, seconds2, milliseconds2;
60     
61     sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n", &houres1, &minutes1, &seconds1, &milliseconds1,
62            &houres2, &minutes2, &seconds2, &milliseconds2);
63     
64     struct start_and_end result = {
65         milliseconds1 + seconds1*1000 + minutes1*60*1000 + houres1*60*60*1000,
66         milliseconds2 + seconds2*1000 + minutes2*60*1000 + houres2*60*60*1000};
67     return result;
68 }
69
70 static int utf8_fill( hb_work_private_t * pv )
71 {
72     int bytes, conversion = 0;
73     size_t out_size;
74
75     /* Align utf8 data to beginning of the buffer so that we can
76      * fill the buffer to its maximum */
77     memmove( pv->utf8_buf, pv->utf8_buf + pv->utf8_pos, pv->utf8_end - pv->utf8_pos );
78     pv->utf8_end -= pv->utf8_pos;
79     pv->utf8_pos = 0;
80     out_size = 2048 - pv->utf8_end;
81     while( out_size )
82     {
83         char *p, *q;
84         size_t in_size, retval;
85
86         if( pv->end == pv->pos )
87         {
88             bytes = fread( pv->buf, 1, 1024, pv->file );
89             pv->pos = 0;
90             pv->end = bytes;
91             if( bytes == 0 )
92                 return 0;
93         }
94
95         p = pv->buf + pv->pos;
96         q = pv->utf8_buf + pv->utf8_end;
97         in_size = pv->end - pv->pos;
98
99         retval = iconv( pv->iconv_context, &p, &in_size, &q, &out_size);
100         if( q != pv->utf8_buf + pv->utf8_pos )
101             conversion = 1;
102
103         pv->utf8_end = q - pv->utf8_buf;
104         pv->pos = p - pv->buf;
105
106         if( ( retval == -1 ) && ( errno == EINVAL ) )
107         {
108             /* Incomplete multibyte sequence, read more data */
109             memmove( pv->buf, p, pv->end - pv->pos );
110             pv->end -= pv->pos;
111             pv->pos = 0;
112             bytes = fread( pv->buf + pv->end, 1, 1024 - pv->end, pv->file );
113             if( bytes == 0 )
114             {
115                 if( !conversion )
116                     return 0;
117                 else
118                     return 1;
119             }
120             pv->end += bytes;
121         } else if ( ( retval == -1 ) && ( errno == EILSEQ ) )
122         {
123             hb_error( "Invalid byte for codeset in input, discard byte" );
124             /* Try the next byte of the input */
125             pv->pos++;
126         } else if ( ( retval == -1 ) && ( errno == E2BIG ) )
127         {
128             /* buffer full */
129             return conversion;
130         }
131     }
132     return 1;
133 }
134
135 static int get_line( hb_work_private_t * pv, char *buf, int size )
136 {
137     int i;
138     char c;
139
140     /* Find newline in converted UTF-8 buffer */
141     for( i = 0; i < size - 1; i++ )
142     {
143         if( pv->utf8_pos >= pv->utf8_end )
144         {
145             if( !utf8_fill( pv ) )
146             {
147                 if( i )
148                     return 1;
149                 else
150                     return 0;
151             }
152         }
153         c = pv->utf8_buf[pv->utf8_pos++];
154         if( c == '\n' )
155         {
156             buf[i] = '\n';
157             buf[i+1] = '\0';
158             return 1;
159         }
160         buf[i] = c;
161     }
162     buf[0] = '\0';
163     return 1;
164 }
165
166 /*
167  * Read the SRT file and put the entries into the subtitle fifo for all to read
168  */
169 static hb_buffer_t *srt_read( hb_work_private_t *pv )
170 {
171     char line_buffer[1024];
172
173     if( !pv->file )
174     {
175         return NULL;
176     }
177     
178     while( get_line( pv, line_buffer, sizeof( line_buffer ) ) ) 
179     {
180         switch (pv->current_state)
181         {
182         case k_state_timecode:
183         {
184             struct start_and_end timing = read_time_from_string( line_buffer );
185             pv->current_entry.duration = timing.end - timing.start;
186             pv->current_entry.offset = timing.start - pv->current_time;
187             
188             pv->current_time = timing.end;
189
190             pv->current_entry.start = timing.start;
191             pv->current_entry.stop = timing.end;
192
193             pv->current_state = k_state_inEntry;
194             continue;                           
195         }
196         
197         case k_state_inEntry:
198         {
199             char *q;
200             int  size, len;
201
202             // If the current line is empty, we assume this is the
203             //  seperation betwene two entries. In case we are wrong,
204             //  the mistake is corrected in the next state.
205             if (strcmp(line_buffer, "\n") == 0 || strcmp(line_buffer, "\r\n") == 0) {
206                 pv->current_state = k_state_potential_new_entry;
207                 continue;
208             }
209             
210             q = pv->current_entry.text + pv->current_entry.pos;
211             len = strlen( line_buffer );
212             size = MIN(1024 - pv->current_entry.pos - 1, len );
213             memcpy(q, line_buffer, size);
214             pv->current_entry.pos += size;
215             pv->current_entry.text[pv->current_entry.pos] = '\0';
216             break;                              
217         }
218         
219         case k_state_potential_new_entry:
220         {
221             const char endpoint[] = "\0";
222             const unsigned long potential_entry_number = strtol(line_buffer, (char**)&endpoint, 10);
223             hb_buffer_t *buffer = NULL;
224             /*
225              * Is this really new next entry begin?
226              */
227             if (potential_entry_number == pv->number_of_entries + 1) 
228             {
229                 /*
230                  * We found the next entry - or a really rare error condition
231                  */
232                 if( *pv->current_entry.text )
233                 {
234                     long length;
235                     char *p;
236                     uint64_t start_time = ( pv->current_entry.start + 
237                                             pv->subtitle->config.offset ) * 90;
238                     uint64_t stop_time = ( pv->current_entry.stop + 
239                                            pv->subtitle->config.offset ) * 90;
240
241                     if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
242                     {
243                         hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time);
244                         memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
245                         ++(pv->number_of_entries);
246                         pv->current_state = k_state_timecode;
247                         continue;
248                     }
249
250                     length = strlen( pv->current_entry.text );
251
252                     for( p = pv->current_entry.text; *p; p++)
253                     {
254                         if( *p == '\n' || *p == '\r' )
255                         {
256                             *p = ' ';
257                         }
258                     }
259
260                     buffer = hb_buffer_init( length + 1 );
261
262                     if( buffer )
263                     {
264                         buffer->start = start_time - pv->start_time;
265                         buffer->stop = stop_time - pv->start_time;
266
267                         memcpy( buffer->data, pv->current_entry.text, length + 1 );
268                     }
269                 }
270                 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
271                 ++(pv->number_of_entries);
272                 pv->current_state = k_state_timecode;
273                 if( buffer )
274                 {
275                     return buffer;
276                 }
277                 continue;
278             } 
279             else 
280             {
281                 /*
282                  * Well.. looks like we are in the wrong mode.. lets add the
283                  * newline we misinterpreted...
284                  */
285                 strncat(pv->current_entry.text, " ", 1024);
286                 pv->current_state = k_state_inEntry;
287             }
288             
289             break;
290         }
291         }
292     }
293     
294     return NULL;
295 }
296
297 static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
298 {
299     int retval = 1;
300     hb_work_private_t * pv;
301     hb_buffer_t *buffer;
302     int i;
303     hb_chapter_t * chapter;
304     hb_title_t *title = job->title;
305
306     pv = calloc( 1, sizeof( hb_work_private_t ) );
307     if( pv )
308     {
309         w->private_data = pv;
310
311         pv->job = job;
312
313         buffer = hb_buffer_init( 0 );
314         hb_fifo_push( w->fifo_in, buffer);
315         
316         pv->file = fopen( w->subtitle->config.src_filename, "r" );
317         
318         pv->current_state = k_state_potential_new_entry;
319         pv->number_of_entries = 0;
320         pv->current_time = 0;
321         pv->subtitle = w->subtitle;
322
323         /*
324          * Figure out the start and stop times from teh chapters being
325          * encoded - drop subtitle not in this range.
326          */
327         pv->start_time = 0;
328         for( i = 1; i < job->chapter_start; ++i )
329         {
330             chapter = hb_list_item( title->list_chapter, i - 1 );
331             if( chapter )
332             {
333                 pv->start_time += chapter->duration;
334             } else {
335                 hb_error( "Could not locate chapter %d for SRT start time", i );
336                 retval = 0;
337             }
338         }
339         pv->stop_time = pv->start_time;
340         for( i = job->chapter_start; i <= job->chapter_end; ++i )
341         {
342             chapter = hb_list_item( title->list_chapter, i - 1 );
343             if( chapter )
344             {
345                 pv->stop_time += chapter->duration;
346             } else {
347                 hb_error( "Could not locate chapter %d for SRT start time", i );
348                 retval = 0;
349             }
350         }
351
352         hb_deep_log( 3, "SRT Start time %"PRId64", stop time %"PRId64, pv->start_time, pv->stop_time);
353
354         pv->iconv_context = iconv_open( "utf-8", pv->subtitle->config.src_codeset );
355
356
357         if( pv->iconv_context == (iconv_t) -1 )
358         {
359             hb_error("Could not open the iconv library with those file formats\n");
360
361         } else {
362             memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
363             
364             pv->file = fopen( w->subtitle->config.src_filename, "r" );
365             
366             if( !pv->file )
367             {
368                 hb_error("Could not open the SRT subtitle file '%s'\n", 
369                          w->subtitle->config.src_filename);
370             } else {
371                 retval = 0;
372             }
373         }
374     } 
375
376     return retval;
377 }
378
379 static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
380                        hb_buffer_t ** buf_out )
381 {
382     hb_work_private_t * pv = w->private_data;
383     hb_buffer_t * in = *buf_in;
384     hb_buffer_t * out = NULL;
385
386     out = srt_read( pv );
387
388     if( out )
389     {
390         /*
391          * Keep a buffer in our input fifo so that we get run.
392          */
393         hb_fifo_push( w->fifo_in, in);
394         *buf_in = NULL;
395         *buf_out = out;
396     } else {
397         *buf_out = NULL;
398         return HB_WORK_OK;
399     }
400
401     return HB_WORK_OK;  
402 }
403
404 static void decsrtClose( hb_work_object_t * w )
405 {
406     hb_work_private_t * pv = w->private_data;
407     fclose( pv->file );
408     iconv_close(pv->iconv_context);
409     free( w->private_data );
410 }
411
412 hb_work_object_t hb_decsrtsub =
413 {
414     WORK_DECSRTSUB,
415     "SRT Subtitle Decoder",
416     decsrtInit,
417     decsrtWork,
418     decsrtClose
419 };