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. */
13 struct start_and_end {
14 unsigned long start, end;
20 k_state_inEntry_or_new,
21 k_state_potential_new_entry,
25 typedef struct srt_entry_s {
26 long offset, duration;
33 * Store all context in the work private struct,
35 struct hb_work_private_s
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
57 read_time_from_string( const char* timeString, struct start_and_end *result )
59 // for ex. 00:00:15,248 --> 00:00:16,545
61 long houres1, minutes1, seconds1, milliseconds1,
62 houres2, minutes2, seconds2, milliseconds2;
65 scanned = sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n",
66 &houres1, &minutes1, &seconds1, &milliseconds1,
67 &houres2, &minutes2, &seconds2, &milliseconds2);
73 milliseconds1 + seconds1*1000 + minutes1*60*1000 + houres1*60*60*1000;
75 milliseconds2 + seconds2*1000 + minutes2*60*1000 + houres2*60*60*1000;
79 static int utf8_fill( hb_work_private_t * pv )
81 int bytes, conversion = 0;
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;
89 out_size = 2048 - pv->utf8_end;
93 size_t in_size, retval;
95 if( pv->end == pv->pos )
97 bytes = fread( pv->buf, 1, 1024, pv->file );
109 p = pv->buf + pv->pos;
110 q = pv->utf8_buf + pv->utf8_end;
111 in_size = pv->end - pv->pos;
113 retval = iconv( pv->iconv_context, &p, &in_size, &q, &out_size);
114 if( q != pv->utf8_buf + pv->utf8_pos )
117 pv->utf8_end = q - pv->utf8_buf;
118 pv->pos = p - pv->buf;
120 if( ( retval == -1 ) && ( errno == EINVAL ) )
122 /* Incomplete multibyte sequence, read more data */
123 memmove( pv->buf, p, pv->end - pv->pos );
126 bytes = fread( pv->buf + pv->end, 1, 1024 - pv->end, pv->file );
135 } else if ( ( retval == -1 ) && ( errno == EILSEQ ) )
137 hb_error( "Invalid byte for codeset in input, discard byte" );
138 /* Try the next byte of the input */
140 } else if ( ( retval == -1 ) && ( errno == E2BIG ) )
149 static int get_line( hb_work_private_t * pv, char *buf, int size )
154 /* Find newline in converted UTF-8 buffer */
155 for( i = 0; i < size - 1; i++ )
157 if( pv->utf8_pos >= pv->utf8_end )
159 if( !utf8_fill( pv ) )
167 c = pv->utf8_buf[pv->utf8_pos++];
181 * Read the SRT file and put the entries into the subtitle fifo for all to read
183 static hb_buffer_t *srt_read( hb_work_private_t *pv )
185 char line_buffer[1024];
186 int reprocess = 0, resync = 0;
193 while( reprocess || get_line( pv, line_buffer, sizeof( line_buffer ) ) )
196 switch (pv->current_state)
198 case k_state_timecode:
200 struct start_and_end timing;
203 result = read_time_from_string( line_buffer, &timing );
207 pv->current_state = k_state_potential_new_entry;
210 pv->current_entry.duration = timing.end - timing.start;
211 pv->current_entry.offset = timing.start - pv->current_time;
213 pv->current_time = timing.end;
215 pv->current_entry.start = timing.start;
216 pv->current_entry.stop = timing.end;
218 pv->current_state = k_state_inEntry;
222 case k_state_inEntry_or_new:
227 * Is this really new next entry begin?
229 entry_number = strtol(line_buffer, &endpoint, 10);
230 if (endpoint == line_buffer ||
231 (endpoint && *endpoint != '\n' && *endpoint != '\r'))
234 * Doesn't resemble an entry number
235 * must still be in an entry
240 pv->current_state = k_state_inEntry;
245 pv->current_state = k_state_potential_new_entry;
249 case k_state_inEntry:
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;
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';
271 case k_state_potential_new_entry:
275 hb_buffer_t *buffer = NULL;
277 * Is this really new next entry begin?
279 entry_number = strtol(line_buffer, &endpoint, 10);
280 if (!resync && (*line_buffer == '\n' || *line_buffer == '\r'))
283 * Well.. looks like we are in the wrong mode.. lets add the
284 * newline we misinterpreted...
286 strncat(pv->current_entry.text, " ", 1024);
287 pv->current_state = k_state_inEntry_or_new;
290 if (endpoint == line_buffer ||
291 (endpoint && *endpoint != '\n' && *endpoint != '\r'))
294 * Well.. looks like we are in the wrong mode.. lets add the
295 * line we misinterpreted...
300 pv->current_state = k_state_inEntry;
305 * We found the next entry - or a really rare error condition
307 pv->last_entry_number = entry_number;
309 if( *pv->current_entry.text )
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;
319 if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
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;
328 length = strlen( pv->current_entry.text );
330 for( q = p = pv->current_entry.text; *p; p++)
345 else if( *p != '\r' )
357 buffer = hb_buffer_init( length + 1 );
361 buffer->start = start_time - pv->start_time;
362 buffer->stop = stop_time - pv->start_time;
364 memcpy( buffer->data, pv->current_entry.text, length + 1 );
367 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
368 ++(pv->number_of_entries);
369 pv->current_state = k_state_timecode;
379 hb_buffer_t *buffer = NULL;
380 if( *pv->current_entry.text )
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;
390 if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
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 ) );
397 length = strlen( pv->current_entry.text );
399 for( q = p = pv->current_entry.text; *p; p++)
414 else if( *p != '\r' )
426 buffer = hb_buffer_init( length + 1 );
430 buffer->start = start_time - pv->start_time;
431 buffer->stop = stop_time - pv->start_time;
433 memcpy( buffer->data, pv->current_entry.text, length + 1 );
436 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
445 static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
448 hb_work_private_t * pv;
451 hb_chapter_t * chapter;
452 hb_title_t *title = job->title;
454 pv = calloc( 1, sizeof( hb_work_private_t ) );
457 w->private_data = pv;
461 buffer = hb_buffer_init( 0 );
462 hb_fifo_push( w->fifo_in, buffer);
464 pv->file = fopen( w->subtitle->config.src_filename, "r" );
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;
473 * Figure out the start and stop times from teh chapters being
474 * encoded - drop subtitle not in this range.
477 for( i = 1; i < job->chapter_start; ++i )
479 chapter = hb_list_item( title->list_chapter, i - 1 );
482 pv->start_time += chapter->duration;
484 hb_error( "Could not locate chapter %d for SRT start time", i );
488 pv->stop_time = pv->start_time;
489 for( i = job->chapter_start; i <= job->chapter_end; ++i )
491 chapter = hb_list_item( title->list_chapter, i - 1 );
494 pv->stop_time += chapter->duration;
496 hb_error( "Could not locate chapter %d for SRT start time", i );
501 hb_deep_log( 3, "SRT Start time %"PRId64", stop time %"PRId64, pv->start_time, pv->stop_time);
503 pv->iconv_context = iconv_open( "utf-8", pv->subtitle->config.src_codeset );
506 if( pv->iconv_context == (iconv_t) -1 )
508 hb_error("Could not open the iconv library with those file formats\n");
511 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
513 pv->file = fopen( w->subtitle->config.src_filename, "r" );
517 hb_error("Could not open the SRT subtitle file '%s'\n",
518 w->subtitle->config.src_filename);
528 static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
529 hb_buffer_t ** buf_out )
531 hb_work_private_t * pv = w->private_data;
532 hb_buffer_t * in = *buf_in;
533 hb_buffer_t * out = NULL;
535 out = srt_read( pv );
540 * Keep a buffer in our input fifo so that we get run.
542 hb_fifo_push( w->fifo_in, in);
553 static void decsrtClose( hb_work_object_t * w )
555 hb_work_private_t * pv = w->private_data;
557 iconv_close(pv->iconv_context);
558 free( w->private_data );
561 hb_work_object_t hb_decsrtsub =
564 "SRT Subtitle Decoder",