OSDN Git Service

gitattirubes test 2
[handbrake-jp/handbrake-jp.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_inEntry_or_new,
21     k_state_potential_new_entry,
22     k_state_timecode,
23 };
24
25 typedef struct srt_entry_s {
26     long offset, duration;
27     long start, stop;
28     char text[1024];
29     int  pos;
30 } srt_entry_t;
31
32 /*
33  * Store all context in the work private struct,
34  */
35 struct hb_work_private_s
36 {
37     hb_job_t * job;
38     FILE     * file;
39     char       buf[1024];
40     int        pos;
41     int        end;
42     char       utf8_buf[2048];
43     int        utf8_pos;
44     int        utf8_end;
45     unsigned long current_time;
46     unsigned long number_of_entries;
47     unsigned long last_entry_number;
48     unsigned long current_state;
49     srt_entry_t current_entry;
50     iconv_t *iconv_context;
51     hb_subtitle_t *subtitle;
52     uint64_t start_time;              // In HB time
53     uint64_t stop_time;               // In HB time
54 };
55
56 static int 
57 read_time_from_string( const char* timeString, struct start_and_end *result )
58 {
59     // for ex. 00:00:15,248 --> 00:00:16,545
60     
61     long houres1, minutes1, seconds1, milliseconds1,
62          houres2, minutes2, seconds2, milliseconds2;
63     int scanned;
64     
65     scanned = sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n",
66                     &houres1, &minutes1, &seconds1, &milliseconds1,
67                     &houres2, &minutes2, &seconds2, &milliseconds2);
68     if (scanned != 8)
69     {
70         return 0;
71     }
72     result->start =
73         milliseconds1 + seconds1*1000 + minutes1*60*1000 + houres1*60*60*1000;
74     result->end =
75         milliseconds2 + seconds2*1000 + minutes2*60*1000 + houres2*60*60*1000;
76     return 1;
77 }
78
79 static int utf8_fill( hb_work_private_t * pv )
80 {
81     int bytes, conversion = 0;
82     size_t out_size;
83
84     /* Align utf8 data to beginning of the buffer so that we can
85      * fill the buffer to its maximum */
86     memmove( pv->utf8_buf, pv->utf8_buf + pv->utf8_pos, pv->utf8_end - pv->utf8_pos );
87     pv->utf8_end -= pv->utf8_pos;
88     pv->utf8_pos = 0;
89     out_size = 2048 - pv->utf8_end;
90     while( out_size )
91     {
92         char *p, *q;
93         size_t in_size, retval;
94
95         if( pv->end == pv->pos )
96         {
97             bytes = fread( pv->buf, 1, 1024, pv->file );
98             pv->pos = 0;
99             pv->end = bytes;
100             if( bytes == 0 )
101             {
102                 if( conversion )
103                     return 1;
104                 else
105                     return 0;
106             }
107         }
108
109         p = pv->buf + pv->pos;
110         q = pv->utf8_buf + pv->utf8_end;
111         in_size = pv->end - pv->pos;
112
113         retval = iconv( pv->iconv_context, &p, &in_size, &q, &out_size);
114         if( q != pv->utf8_buf + pv->utf8_pos )
115             conversion = 1;
116
117         pv->utf8_end = q - pv->utf8_buf;
118         pv->pos = p - pv->buf;
119
120         if( ( retval == -1 ) && ( errno == EINVAL ) )
121         {
122             /* Incomplete multibyte sequence, read more data */
123             memmove( pv->buf, p, pv->end - pv->pos );
124             pv->end -= pv->pos;
125             pv->pos = 0;
126             bytes = fread( pv->buf + pv->end, 1, 1024 - pv->end, pv->file );
127             if( bytes == 0 )
128             {
129                 if( !conversion )
130                     return 0;
131                 else
132                     return 1;
133             }
134             pv->end += bytes;
135         } else if ( ( retval == -1 ) && ( errno == EILSEQ ) )
136         {
137             hb_error( "Invalid byte for codeset in input, discard byte" );
138             /* Try the next byte of the input */
139             pv->pos++;
140         } else if ( ( retval == -1 ) && ( errno == E2BIG ) )
141         {
142             /* buffer full */
143             return conversion;
144         }
145     }
146     return 1;
147 }
148
149 static int get_line( hb_work_private_t * pv, char *buf, int size )
150 {
151     int i;
152     char c;
153
154     /* Find newline in converted UTF-8 buffer */
155     for( i = 0; i < size - 1; i++ )
156     {
157         if( pv->utf8_pos >= pv->utf8_end )
158         {
159             if( !utf8_fill( pv ) )
160             {
161                 if( i )
162                     return 1;
163                 else
164                     return 0;
165             }
166         }
167         c = pv->utf8_buf[pv->utf8_pos++];
168         if( c == '\n' )
169         {
170             buf[i] = '\n';
171             buf[i+1] = '\0';
172             return 1;
173         }
174         buf[i] = c;
175     }
176     buf[0] = '\0';
177     return 1;
178 }
179
180 /*
181  * Read the SRT file and put the entries into the subtitle fifo for all to read
182  */
183 static hb_buffer_t *srt_read( hb_work_private_t *pv )
184 {
185     char line_buffer[1024];
186     int reprocess = 0, resync = 0;
187
188     if( !pv->file )
189     {
190         return NULL;
191     }
192     
193     while( reprocess || get_line( pv, line_buffer, sizeof( line_buffer ) ) ) 
194     {
195         reprocess = 0;
196         switch (pv->current_state)
197         {
198         case k_state_timecode:
199         {
200             struct start_and_end timing;
201             int result;
202
203             result = read_time_from_string( line_buffer, &timing );
204             if (!result)
205             {
206                 resync = 1;
207                 pv->current_state = k_state_potential_new_entry;
208                 continue;
209             }
210             pv->current_entry.duration = timing.end - timing.start;
211             pv->current_entry.offset = timing.start - pv->current_time;
212             
213             pv->current_time = timing.end;
214
215             pv->current_entry.start = timing.start;
216             pv->current_entry.stop = timing.end;
217
218             pv->current_state = k_state_inEntry;
219             continue;
220         }
221
222         case k_state_inEntry_or_new:
223         {
224             char *endpoint;
225             long entry_number;
226             /*
227              * Is this really new next entry begin?
228              */
229             entry_number = strtol(line_buffer, &endpoint, 10);
230             if (endpoint == line_buffer ||
231                 (endpoint && *endpoint != '\n' && *endpoint != '\r'))
232             {
233                 /*
234                  * Doesn't resemble an entry number
235                  * must still be in an entry
236                  */
237                 if (!resync)
238                 {
239                     reprocess = 1;
240                     pv->current_state = k_state_inEntry;
241                 }
242                 continue;
243             }
244             reprocess = 1;
245             pv->current_state = k_state_potential_new_entry;
246             break;
247         }
248
249         case k_state_inEntry:
250         {
251             char *q;
252             int  size, len;
253
254             // If the current line is empty, we assume this is the
255             //  seperation betwene two entries. In case we are wrong,
256             //  the mistake is corrected in the next state.
257             if (strcmp(line_buffer, "\n") == 0 || strcmp(line_buffer, "\r\n") == 0) {
258                 pv->current_state = k_state_potential_new_entry;
259                 continue;
260             }
261             
262             q = pv->current_entry.text + pv->current_entry.pos;
263             len = strlen( line_buffer );
264             size = MIN(1024 - pv->current_entry.pos - 1, len );
265             memcpy(q, line_buffer, size);
266             pv->current_entry.pos += size;
267             pv->current_entry.text[pv->current_entry.pos] = '\0';
268             break;
269         }
270
271         case k_state_potential_new_entry:
272         {
273             char *endpoint;
274             long entry_number;
275             hb_buffer_t *buffer = NULL;
276             /*
277              * Is this really new next entry begin?
278              */
279             entry_number = strtol(line_buffer, &endpoint, 10);
280             if (!resync && (*line_buffer == '\n' || *line_buffer == '\r'))
281             {
282                 /*
283                  * Well.. looks like we are in the wrong mode.. lets add the
284                  * newline we misinterpreted...
285                  */
286                 strncat(pv->current_entry.text, " ", 1024);
287                 pv->current_state = k_state_inEntry_or_new;
288                 continue;
289             }
290             if (endpoint == line_buffer ||
291                 (endpoint && *endpoint != '\n' && *endpoint != '\r'))
292             {
293                 /*
294                  * Well.. looks like we are in the wrong mode.. lets add the
295                  * line we misinterpreted...
296                  */
297                 if (!resync)
298                 {
299                     reprocess = 1;
300                     pv->current_state = k_state_inEntry;
301                 }
302                 continue;
303             }
304             /*
305              * We found the next entry - or a really rare error condition
306              */
307             pv->last_entry_number = entry_number;
308             resync = 0;
309             if( *pv->current_entry.text )
310             {
311                 long length;
312                 char *p, *q;
313                 int  line = 1;
314                 uint64_t start_time = ( pv->current_entry.start + 
315                                         pv->subtitle->config.offset ) * 90;
316                 uint64_t stop_time = ( pv->current_entry.stop + 
317                                        pv->subtitle->config.offset ) * 90;
318
319                 if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
320                 {
321                     hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time);
322                     memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
323                     ++(pv->number_of_entries);
324                     pv->current_state = k_state_timecode;
325                     continue;
326                 }
327
328                 length = strlen( pv->current_entry.text );
329
330                 for( q = p = pv->current_entry.text; *p; p++)
331                 {
332                     if( *p == '\n' )
333                     {
334                         if ( line == 1 )
335                         {
336                             *q = *p;
337                             line = 2;
338                         }
339                         else
340                         {
341                             *q = ' ';
342                         }
343                         q++;
344                     }
345                     else if( *p != '\r' )
346                     {
347                         *q = *p;
348                         q++;
349                     }
350                     else
351                     {
352                         length--;
353                     }
354                 }
355                 *q = '\0';
356
357                 buffer = hb_buffer_init( length + 1 );
358
359                 if( buffer )
360                 {
361                     buffer->start = start_time - pv->start_time;
362                     buffer->stop = stop_time - pv->start_time;
363
364                     memcpy( buffer->data, pv->current_entry.text, length + 1 );
365                 }
366             }
367             memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
368             ++(pv->number_of_entries);
369             pv->current_state = k_state_timecode;
370             if( buffer )
371             {
372                 return buffer;
373             }
374             continue;
375         } 
376         }
377     }
378
379     hb_buffer_t *buffer = NULL;
380     if( *pv->current_entry.text )
381     {
382         long length;
383         char *p, *q;
384         int  line = 1;
385         uint64_t start_time = ( pv->current_entry.start + 
386                                 pv->subtitle->config.offset ) * 90;
387         uint64_t stop_time = ( pv->current_entry.stop + 
388                                pv->subtitle->config.offset ) * 90;
389
390         if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
391         {
392             hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time);
393             memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
394             return NULL;
395         }
396
397         length = strlen( pv->current_entry.text );
398
399         for( q = p = pv->current_entry.text; *p; p++)
400         {
401             if( *p == '\n' )
402             {
403                 if ( line == 1 )
404                 {
405                     *q = *p;
406                     line = 2;
407                 }
408                 else
409                 {
410                     *q = ' ';
411                 }
412                 q++;
413             }
414             else if( *p != '\r' )
415             {
416                 *q = *p;
417                 q++;
418             }
419             else
420             {
421                 length--;
422             }
423         }
424         *q = '\0';
425
426         buffer = hb_buffer_init( length + 1 );
427
428         if( buffer )
429         {
430             buffer->start = start_time - pv->start_time;
431             buffer->stop = stop_time - pv->start_time;
432
433             memcpy( buffer->data, pv->current_entry.text, length + 1 );
434         }
435     }
436     memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
437     if( buffer )
438     {
439         return buffer;
440     }
441     
442     return NULL;
443 }
444
445 static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
446 {
447     int retval = 1;
448     hb_work_private_t * pv;
449     hb_buffer_t *buffer;
450     int i;
451     hb_chapter_t * chapter;
452     hb_title_t *title = job->title;
453
454     pv = calloc( 1, sizeof( hb_work_private_t ) );
455     if( pv )
456     {
457         w->private_data = pv;
458
459         pv->job = job;
460
461         buffer = hb_buffer_init( 0 );
462         hb_fifo_push( w->fifo_in, buffer);
463         
464         pv->file = fopen( w->subtitle->config.src_filename, "r" );
465         
466         pv->current_state = k_state_potential_new_entry;
467         pv->number_of_entries = 0;
468         pv->last_entry_number = 0;
469         pv->current_time = 0;
470         pv->subtitle = w->subtitle;
471
472         /*
473          * Figure out the start and stop times from teh chapters being
474          * encoded - drop subtitle not in this range.
475          */
476         pv->start_time = 0;
477         for( i = 1; i < job->chapter_start; ++i )
478         {
479             chapter = hb_list_item( title->list_chapter, i - 1 );
480             if( chapter )
481             {
482                 pv->start_time += chapter->duration;
483             } else {
484                 hb_error( "Could not locate chapter %d for SRT start time", i );
485                 retval = 0;
486             }
487         }
488         pv->stop_time = pv->start_time;
489         for( i = job->chapter_start; i <= job->chapter_end; ++i )
490         {
491             chapter = hb_list_item( title->list_chapter, i - 1 );
492             if( chapter )
493             {
494                 pv->stop_time += chapter->duration;
495             } else {
496                 hb_error( "Could not locate chapter %d for SRT start time", i );
497                 retval = 0;
498             }
499         }
500
501         hb_deep_log( 3, "SRT Start time %"PRId64", stop time %"PRId64, pv->start_time, pv->stop_time);
502
503         pv->iconv_context = iconv_open( "utf-8", pv->subtitle->config.src_codeset );
504
505
506         if( pv->iconv_context == (iconv_t) -1 )
507         {
508             hb_error("Could not open the iconv library with those file formats\n");
509
510         } else {
511             memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
512             
513             pv->file = fopen( w->subtitle->config.src_filename, "r" );
514             
515             if( !pv->file )
516             {
517                 hb_error("Could not open the SRT subtitle file '%s'\n", 
518                          w->subtitle->config.src_filename);
519             } else {
520                 retval = 0;
521             }
522         }
523     } 
524
525     return retval;
526 }
527
528 static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
529                        hb_buffer_t ** buf_out )
530 {
531     hb_work_private_t * pv = w->private_data;
532     hb_buffer_t * in = *buf_in;
533     hb_buffer_t * out = NULL;
534
535     out = srt_read( pv );
536
537     if( out )
538     {
539         /*
540          * Keep a buffer in our input fifo so that we get run.
541          */
542         hb_fifo_push( w->fifo_in, in);
543         *buf_in = NULL;
544         *buf_out = out;
545     } else {
546         *buf_out = NULL;
547         return HB_WORK_OK;
548     }
549
550     return HB_WORK_OK;  
551 }
552
553 static void decsrtClose( hb_work_object_t * w )
554 {
555     hb_work_private_t * pv = w->private_data;
556     fclose( pv->file );
557     iconv_close(pv->iconv_context);
558     free( w->private_data );
559 }
560
561 hb_work_object_t hb_decsrtsub =
562 {
563     WORK_DECSRTSUB,
564     "SRT Subtitle Decoder",
565     decsrtInit,
566     decsrtWork,
567     decsrtClose
568 };