OSDN Git Service

LinGui: SRT support
[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, %"PRId64" bytes discarded", (int64_t)in_size);
127             } else if ( ( retval == -1 ) && ( errno == E2BIG ) )
128             {
129                 hb_error( "Not enough space in output buffer");
130             }
131
132             break;                              
133         }
134         
135         case k_state_potential_new_entry:
136         {
137             const char endpoint[] = "\0";
138             const unsigned long potential_entry_number = strtol(line_buffer, (char**)&endpoint, 10);
139             hb_buffer_t *buffer = NULL;
140             /*
141              * Is this really new next entry begin?
142              */
143             if (potential_entry_number == pv->number_of_entries + 1) {
144                 /*
145                  * We found the next entry - or a really rare error condition
146                  */
147                 if( *pv->current_entry.text )
148                 {
149                     long length;
150                     char *p;
151                     uint64_t start_time = ( pv->current_entry.start + 
152                                             pv->subtitle->config.offset ) * 90;
153                     uint64_t stop_time = ( pv->current_entry.stop + 
154                                            pv->subtitle->config.offset ) * 90;
155
156                     if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
157                     {
158                         hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time);
159                         memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
160                         ++(pv->number_of_entries);
161                         pv->current_state = k_state_timecode;
162                         continue;
163                     }
164
165                     length = strlen( pv->current_entry.text );
166
167                     for( p = pv->current_entry.text; *p; p++)
168                     {
169                         if( *p == '\n' || *p == '\r' )
170                         {
171                             *p = ' ';
172                         }
173                     }
174
175                     buffer = hb_buffer_init( length + 1 );
176
177                     if( buffer )
178                     {
179                         buffer->start = start_time - pv->start_time;
180                         buffer->stop = stop_time - pv->start_time;
181
182                         memcpy( buffer->data, pv->current_entry.text, length + 1 );
183                     }
184                 }
185                 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
186                 ++(pv->number_of_entries);
187                 pv->current_state = k_state_timecode;
188                 if( buffer )
189                 {
190                     return buffer;
191                 }
192                 continue;
193             } else {
194                 /*
195                  * Well.. looks like we are in the wrong mode.. lets add the
196                  * newline we misinterpreted...
197                  */
198                 strncat(pv->current_entry.text, " ", 1024);
199                 pv->current_state = k_state_inEntry;
200             }
201             
202             break;
203         }
204         }
205     }
206     
207     return NULL;
208 }
209
210 static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
211 {
212     int retval = 1;
213     hb_work_private_t * pv;
214     hb_buffer_t *buffer;
215     int i;
216     hb_chapter_t * chapter;
217     hb_title_t *title = job->title;
218
219     pv = calloc( 1, sizeof( hb_work_private_t ) );
220     if( pv )
221     {
222         w->private_data = pv;
223
224         pv->job = job;
225
226         buffer = hb_buffer_init( 0 );
227         hb_fifo_push( w->fifo_in, buffer);
228         
229         pv->file = fopen( w->subtitle->config.src_filename, "r" );
230         
231         pv->current_state = k_state_potential_new_entry;
232         pv->number_of_entries = 0;
233         pv->current_time = 0;
234         pv->subtitle = w->subtitle;
235
236         /*
237          * Figure out the start and stop times from teh chapters being
238          * encoded - drop subtitle not in this range.
239          */
240         pv->start_time = 0;
241         for( i = 1; i < job->chapter_start; ++i )
242         {
243             chapter = hb_list_item( title->list_chapter, i - 1 );
244             if( chapter )
245             {
246                 pv->start_time += chapter->duration;
247             } else {
248                 hb_error( "Could not locate chapter %d for SRT start time", i );
249                 retval = 0;
250             }
251         }
252         chapter = hb_list_item( title->list_chapter, i - 1 );
253
254         if( chapter )
255         {
256             pv->stop_time = pv->start_time + chapter->duration;
257         } else {
258             hb_error( "Could not locate chapter %d for SRT stop time", i );
259             retval = 0;
260         }
261
262         hb_deep_log( 3, "SRT Start time %"PRId64", stop time %"PRId64, pv->start_time, pv->stop_time);
263
264         pv->iconv_context = iconv_open( "utf8", pv->subtitle->config.src_codeset );
265
266
267         if( pv->iconv_context == (iconv_t) -1 )
268         {
269             hb_error("Could not open the iconv library with those file formats\n");
270
271         } else {
272             memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
273             
274             pv->file = fopen( w->subtitle->config.src_filename, "r" );
275             
276             if( !pv->file )
277             {
278                 hb_error("Could not open the SRT subtitle file '%s'\n", 
279                          w->subtitle->config.src_filename);
280             } else {
281                 retval = 0;
282             }
283         }
284     } 
285
286     return retval;
287 }
288
289 static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
290                        hb_buffer_t ** buf_out )
291 {
292     hb_work_private_t * pv = w->private_data;
293     hb_buffer_t * in = *buf_in;
294     hb_buffer_t * out = NULL;
295
296     out = srt_read( pv );
297
298     if( out )
299     {
300         /*
301          * Keep a buffer in our input fifo so that we get run.
302          */
303         hb_fifo_push( w->fifo_in, in);
304         *buf_in = NULL;
305         *buf_out = out;
306     } else {
307         *buf_out = NULL;
308         return HB_WORK_OK;
309     }
310
311     return HB_WORK_OK;  
312 }
313
314 static void decsrtClose( hb_work_object_t * w )
315 {
316     hb_work_private_t * pv = w->private_data;
317     fclose( pv->file );
318     iconv_close(pv->iconv_context);
319     free( w->private_data );
320 }
321
322 hb_work_object_t hb_decsrtsub =
323 {
324     WORK_DECSRTSUB,
325     "SRT Subtitle Decoder",
326     decsrtInit,
327     decsrtWork,
328     decsrtClose
329 };