OSDN Git Service

- patch ffmpeg to skip somewhat expensive .d (dependency) file generation.
[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 } srt_entry_t;
29
30 /*
31  * Store all context in the work private struct,
32  */
33 struct hb_work_private_s
34 {
35     hb_job_t *job;
36     FILE *file;
37     unsigned long current_time;
38     unsigned long number_of_entries;
39     unsigned long current_state;
40     srt_entry_t current_entry;
41     iconv_t *iconv_context;
42     hb_subtitle_t *subtitle;
43     uint64_t start_time;              // In HB time
44     uint64_t stop_time;               // In HB time
45 };
46
47 static struct start_and_end read_time_from_string( const char* timeString ) 
48 {
49     // for ex. 00:00:15,248 --> 00:00:16,545
50     
51     long houres1, minutes1, seconds1, milliseconds1,
52         houres2, minutes2, seconds2, milliseconds2;
53     
54     sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n", &houres1, &minutes1, &seconds1, &milliseconds1,
55            &houres2, &minutes2, &seconds2, &milliseconds2);
56     
57     struct start_and_end result = {
58         milliseconds1 + seconds1*1000 + minutes1*60*1000 + houres1*60*60*1000,
59         milliseconds2 + seconds2*1000 + minutes2*60*1000 + houres2*60*60*1000};
60     return result;
61 }
62
63 /*
64  * Read the SRT file and put the entries into the subtitle fifo for all to read
65  */
66 static hb_buffer_t *srt_read( hb_work_private_t *pv )
67 {
68
69     char line_buffer[1024];
70
71     if( !pv->file )
72     {
73         return NULL;
74     }
75     
76     while( fgets( line_buffer, sizeof( line_buffer ), pv->file ) ) 
77     {
78         switch (pv->current_state)
79         {
80         case k_state_timecode:
81         {
82             struct start_and_end timing = read_time_from_string( line_buffer );
83             pv->current_entry.duration = timing.end - timing.start;
84             pv->current_entry.offset = timing.start - pv->current_time;
85             
86             pv->current_time = timing.end;
87
88             pv->current_entry.start = timing.start;
89             pv->current_entry.stop = timing.end;
90
91             pv->current_state = k_state_inEntry;
92             continue;                           
93         }
94         
95         case k_state_inEntry:
96         {
97             char *p, *q;
98             size_t in_size;
99             size_t out_size;
100             size_t retval;
101
102             // If the current line is empty, we assume this is the
103             //  seperation betwene two entries. In case we are wrong,
104             //  the mistake is corrected in the next state.
105             if (strcmp(line_buffer, "\n") == 0 || strcmp(line_buffer, "\r\n") == 0) {
106                 pv->current_state = k_state_potential_new_entry;
107                 continue;
108             }
109             
110
111             for( q = pv->current_entry.text; (q < pv->current_entry.text+1024) && *q; q++);
112             
113             p = line_buffer;
114
115             in_size = strlen(line_buffer);
116             out_size = (pv->current_entry.text+1024) - q;
117
118             retval = iconv( pv->iconv_context, &p, &in_size, &q, &out_size);
119             *q = '\0';
120
121             if( ( retval == -1 ) && ( errno == EINVAL ) )
122             {
123                 hb_error( "Invalid shift sequence" );
124             } else if ( ( retval == -1 ) && ( errno == EILSEQ ) )
125             {
126                 hb_error( "Invalid byte for codeset in input, %d bytes discarded",
127                           in_size);
128             } else if ( ( retval == -1 ) && ( errno == E2BIG ) )
129             {
130                 hb_error( "Not enough space in output buffer");
131             }
132
133             break;                              
134         }
135         
136         case k_state_potential_new_entry:
137         {
138             const char endpoint[] = "\0";
139             const unsigned long potential_entry_number = strtol(line_buffer, (char**)&endpoint, 10);
140             hb_buffer_t *buffer = NULL;
141             /*
142              * Is this really new next entry begin?
143              */
144             if (potential_entry_number == pv->number_of_entries + 1) {
145                 /*
146                  * We found the next entry - or a really rare error condition
147                  */
148                 if( *pv->current_entry.text )
149                 {
150                     long length;
151                     char *p;
152                     uint64_t start_time = ( pv->current_entry.start + 
153                                             pv->subtitle->config.offset ) * 90;
154                     uint64_t stop_time = ( pv->current_entry.stop + 
155                                            pv->subtitle->config.offset ) * 90;
156
157                     if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
158                     {
159                         hb_deep_log( 3, "Discarding SRT at time start %lld, stop %lld", start_time, stop_time);
160                         memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
161                         ++(pv->number_of_entries);
162                         pv->current_state = k_state_timecode;
163                         continue;
164                     }
165
166                     length = strlen( pv->current_entry.text );
167
168                     for( p = pv->current_entry.text; *p; p++)
169                     {
170                         if( *p == '\n' || *p == '\r' )
171                         {
172                             *p = ' ';
173                         }
174                     }
175
176                     buffer = hb_buffer_init( length + 1 );
177
178                     if( buffer )
179                     {
180                         buffer->start = start_time - pv->start_time;
181                         buffer->stop = stop_time - pv->start_time;
182
183                         memcpy( buffer->data, pv->current_entry.text, length + 1 );
184                     }
185                 }
186                 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
187                 ++(pv->number_of_entries);
188                 pv->current_state = k_state_timecode;
189                 if( buffer )
190                 {
191                     return buffer;
192                 }
193                 continue;
194             } else {
195                 /*
196                  * Well.. looks like we are in the wrong mode.. lets add the
197                  * newline we misinterpreted...
198                  */
199                 strncat(pv->current_entry.text, " ", 1024);
200                 pv->current_state = k_state_inEntry;
201             }
202             
203             break;
204         }
205         }
206     }
207     
208     return NULL;
209 }
210
211 static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
212 {
213     int retval = 1;
214     hb_work_private_t * pv;
215     hb_buffer_t *buffer;
216     int i;
217     hb_chapter_t * chapter;
218     hb_title_t *title = job->title;
219
220     pv = calloc( 1, sizeof( hb_work_private_t ) );
221     if( pv )
222     {
223         w->private_data = pv;
224
225         pv->job = job;
226
227         buffer = hb_buffer_init( 0 );
228         hb_fifo_push( w->fifo_in, buffer);
229         
230         pv->file = fopen( w->subtitle->config.src_filename, "r" );
231         
232         pv->current_state = k_state_potential_new_entry;
233         pv->number_of_entries = 0;
234         pv->current_time = 0;
235         pv->subtitle = w->subtitle;
236
237         /*
238          * Figure out the start and stop times from teh chapters being
239          * encoded - drop subtitle not in this range.
240          */
241         pv->start_time = 0;
242         for( i = 1; i < job->chapter_start; ++i )
243         {
244             chapter = hb_list_item( title->list_chapter, i - 1 );
245             if( chapter )
246             {
247                 pv->start_time += chapter->duration;
248             } else {
249                 hb_error( "Could not locate chapter %d for SRT start time", i );
250                 retval = 0;
251             }
252         }
253         chapter = hb_list_item( title->list_chapter, i - 1 );
254
255         if( chapter )
256         {
257             pv->stop_time = pv->start_time + chapter->duration;
258         } else {
259             hb_error( "Could not locate chapter %d for SRT stop time", i );
260             retval = 0;
261         }
262
263         hb_deep_log( 3, "SRT Start time %lld, stop time %lld", pv->start_time, pv->stop_time);
264
265         pv->iconv_context = iconv_open( "utf8", pv->subtitle->config.src_codeset );
266
267
268         if( pv->iconv_context == (iconv_t) -1 )
269         {
270             hb_error("Could not open the iconv library with those file formats\n");
271
272         } else {
273             memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
274             
275             pv->file = fopen( w->subtitle->config.src_filename, "r" );
276             
277             if( !pv->file )
278             {
279                 hb_error("Could not open the SRT subtitle file '%s'\n", 
280                          w->subtitle->config.src_filename);
281             } else {
282                 retval = 0;
283             }
284         }
285     } 
286
287     return retval;
288 }
289
290 static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
291                        hb_buffer_t ** buf_out )
292 {
293     hb_work_private_t * pv = w->private_data;
294     hb_buffer_t * in = *buf_in;
295     hb_buffer_t * out = NULL;
296
297     out = srt_read( pv );
298
299     if( out )
300     {
301         /*
302          * Keep a buffer in our input fifo so that we get run.
303          */
304         hb_fifo_push( w->fifo_in, in);
305         *buf_in = NULL;
306         *buf_out = out;
307     } else {
308         printf("\nSRT Done\n");
309         *buf_out = NULL;
310         return HB_WORK_OK;
311     }
312
313     return HB_WORK_OK;  
314 }
315
316 static void decsrtClose( hb_work_object_t * w )
317 {
318     hb_work_private_t * pv = w->private_data;
319     fclose( pv->file );
320     iconv_close(pv->iconv_context);
321     free( w->private_data );
322 }
323
324 hb_work_object_t hb_decsrtsub =
325 {
326     WORK_DECSRTSUB,
327     "SRT Subtitle Decoder",
328     decsrtInit,
329     decsrtWork,
330     decsrtClose
331 };