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
46 unsigned long current_time;
47 unsigned long number_of_entries;
48 unsigned long last_entry_number;
49 unsigned long current_state;
50 srt_entry_t current_entry;
51 iconv_t *iconv_context;
52 hb_subtitle_t *subtitle;
53 uint64_t start_time; // In HB time
54 uint64_t stop_time; // In HB time
58 read_time_from_string( const char* timeString, struct start_and_end *result )
60 // for ex. 00:00:15,248 --> 00:00:16,545
62 long houres1, minutes1, seconds1, milliseconds1,
63 houres2, minutes2, seconds2, milliseconds2;
66 scanned = sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n",
67 &houres1, &minutes1, &seconds1, &milliseconds1,
68 &houres2, &minutes2, &seconds2, &milliseconds2);
74 milliseconds1 + seconds1*1000 + minutes1*60*1000 + houres1*60*60*1000;
76 milliseconds2 + seconds2*1000 + minutes2*60*1000 + houres2*60*60*1000;
80 static int utf8_fill( hb_work_private_t * pv )
82 int bytes, conversion = 0;
85 /* Align utf8 data to beginning of the buffer so that we can
86 * fill the buffer to its maximum */
87 memmove( pv->utf8_buf, pv->utf8_buf + pv->utf8_pos, pv->utf8_end - pv->utf8_pos );
88 pv->utf8_end -= pv->utf8_pos;
90 out_size = 2048 - pv->utf8_end;
94 size_t in_size, retval;
96 if( pv->end == pv->pos )
98 bytes = fread( pv->buf, 1, 1024, pv->file );
110 p = pv->buf + pv->pos;
111 q = pv->utf8_buf + pv->utf8_end;
112 in_size = pv->end - pv->pos;
114 retval = iconv( pv->iconv_context, &p, &in_size, &q, &out_size);
115 if( q != pv->utf8_buf + pv->utf8_pos )
118 pv->utf8_end = q - pv->utf8_buf;
119 pv->pos = p - pv->buf;
121 if ( !pv->utf8_bom_skipped )
123 uint8_t *buf = (uint8_t*)pv->utf8_buf;
124 if (buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
128 pv->utf8_bom_skipped = 1;
131 if( ( retval == -1 ) && ( errno == EINVAL ) )
133 /* Incomplete multibyte sequence, read more data */
134 memmove( pv->buf, p, pv->end - pv->pos );
137 bytes = fread( pv->buf + pv->end, 1, 1024 - pv->end, pv->file );
146 } else if ( ( retval == -1 ) && ( errno == EILSEQ ) )
148 hb_error( "Invalid byte for codeset in input, discard byte" );
149 /* Try the next byte of the input */
151 } else if ( ( retval == -1 ) && ( errno == E2BIG ) )
160 static int get_line( hb_work_private_t * pv, char *buf, int size )
165 /* Find newline in converted UTF-8 buffer */
166 for( i = 0; i < size - 1; i++ )
168 if( pv->utf8_pos >= pv->utf8_end )
170 if( !utf8_fill( pv ) )
178 c = pv->utf8_buf[pv->utf8_pos++];
192 * Read the SRT file and put the entries into the subtitle fifo for all to read
194 static hb_buffer_t *srt_read( hb_work_private_t *pv )
196 char line_buffer[1024];
197 int reprocess = 0, resync = 0;
204 while( reprocess || get_line( pv, line_buffer, sizeof( line_buffer ) ) )
207 switch (pv->current_state)
209 case k_state_timecode:
211 struct start_and_end timing;
214 result = read_time_from_string( line_buffer, &timing );
218 pv->current_state = k_state_potential_new_entry;
221 pv->current_entry.duration = timing.end - timing.start;
222 pv->current_entry.offset = timing.start - pv->current_time;
224 pv->current_time = timing.end;
226 pv->current_entry.start = timing.start;
227 pv->current_entry.stop = timing.end;
229 pv->current_state = k_state_inEntry;
233 case k_state_inEntry_or_new:
238 * Is this really new next entry begin?
240 entry_number = strtol(line_buffer, &endpoint, 10);
241 if (endpoint == line_buffer ||
242 (endpoint && *endpoint != '\n' && *endpoint != '\r'))
245 * Doesn't resemble an entry number
246 * must still be in an entry
251 pv->current_state = k_state_inEntry;
256 pv->current_state = k_state_potential_new_entry;
260 case k_state_inEntry:
265 // If the current line is empty, we assume this is the
266 // seperation betwene two entries. In case we are wrong,
267 // the mistake is corrected in the next state.
268 if (strcmp(line_buffer, "\n") == 0 || strcmp(line_buffer, "\r\n") == 0) {
269 pv->current_state = k_state_potential_new_entry;
273 q = pv->current_entry.text + pv->current_entry.pos;
274 len = strlen( line_buffer );
275 size = MIN(1024 - pv->current_entry.pos - 1, len );
276 memcpy(q, line_buffer, size);
277 pv->current_entry.pos += size;
278 pv->current_entry.text[pv->current_entry.pos] = '\0';
282 case k_state_potential_new_entry:
286 hb_buffer_t *buffer = NULL;
288 * Is this really new next entry begin?
290 entry_number = strtol(line_buffer, &endpoint, 10);
291 if (!resync && (*line_buffer == '\n' || *line_buffer == '\r'))
294 * Well.. looks like we are in the wrong mode.. lets add the
295 * newline we misinterpreted...
297 strncat(pv->current_entry.text, " ", 1024);
298 pv->current_state = k_state_inEntry_or_new;
301 if (endpoint == line_buffer ||
302 (endpoint && *endpoint != '\n' && *endpoint != '\r'))
305 * Well.. looks like we are in the wrong mode.. lets add the
306 * line we misinterpreted...
311 pv->current_state = k_state_inEntry;
316 * We found the next entry - or a really rare error condition
318 pv->last_entry_number = entry_number;
320 if( *pv->current_entry.text )
325 uint64_t start_time = ( pv->current_entry.start +
326 pv->subtitle->config.offset ) * 90;
327 uint64_t stop_time = ( pv->current_entry.stop +
328 pv->subtitle->config.offset ) * 90;
330 if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
332 hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time);
333 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
334 ++(pv->number_of_entries);
335 pv->current_state = k_state_timecode;
339 length = strlen( pv->current_entry.text );
341 for( q = p = pv->current_entry.text; *p; p++)
356 else if( *p != '\r' )
368 buffer = hb_buffer_init( length + 1 );
372 buffer->start = start_time - pv->start_time;
373 buffer->stop = stop_time - pv->start_time;
375 memcpy( buffer->data, pv->current_entry.text, length + 1 );
378 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
379 ++(pv->number_of_entries);
380 pv->current_state = k_state_timecode;
390 hb_buffer_t *buffer = NULL;
391 if( *pv->current_entry.text )
396 uint64_t start_time = ( pv->current_entry.start +
397 pv->subtitle->config.offset ) * 90;
398 uint64_t stop_time = ( pv->current_entry.stop +
399 pv->subtitle->config.offset ) * 90;
401 if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
403 hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time);
404 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
408 length = strlen( pv->current_entry.text );
410 for( q = p = pv->current_entry.text; *p; p++)
425 else if( *p != '\r' )
437 buffer = hb_buffer_init( length + 1 );
441 buffer->start = start_time - pv->start_time;
442 buffer->stop = stop_time - pv->start_time;
444 memcpy( buffer->data, pv->current_entry.text, length + 1 );
447 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
456 static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
459 hb_work_private_t * pv;
462 hb_chapter_t * chapter;
463 hb_title_t *title = job->title;
465 pv = calloc( 1, sizeof( hb_work_private_t ) );
468 w->private_data = pv;
472 buffer = hb_buffer_init( 0 );
473 hb_fifo_push( w->fifo_in, buffer);
475 pv->current_state = k_state_potential_new_entry;
476 pv->number_of_entries = 0;
477 pv->last_entry_number = 0;
478 pv->current_time = 0;
479 pv->subtitle = w->subtitle;
482 * Figure out the start and stop times from teh chapters being
483 * encoded - drop subtitle not in this range.
486 for( i = 1; i < job->chapter_start; ++i )
488 chapter = hb_list_item( title->list_chapter, i - 1 );
491 pv->start_time += chapter->duration;
493 hb_error( "Could not locate chapter %d for SRT start time", i );
497 pv->stop_time = pv->start_time;
498 for( i = job->chapter_start; i <= job->chapter_end; ++i )
500 chapter = hb_list_item( title->list_chapter, i - 1 );
503 pv->stop_time += chapter->duration;
505 hb_error( "Could not locate chapter %d for SRT start time", i );
510 hb_deep_log( 3, "SRT Start time %"PRId64", stop time %"PRId64, pv->start_time, pv->stop_time);
512 pv->iconv_context = iconv_open( "utf-8", pv->subtitle->config.src_codeset );
515 if( pv->iconv_context == (iconv_t) -1 )
517 hb_error("Could not open the iconv library with those file formats\n");
520 memset( &pv->current_entry, 0, sizeof( srt_entry_t ) );
522 pv->file = fopen( w->subtitle->config.src_filename, "r" );
526 hb_error("Could not open the SRT subtitle file '%s'\n",
527 w->subtitle->config.src_filename);
537 static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
538 hb_buffer_t ** buf_out )
540 hb_work_private_t * pv = w->private_data;
541 hb_buffer_t * in = *buf_in;
542 hb_buffer_t * out = NULL;
544 out = srt_read( pv );
549 * Keep a buffer in our input fifo so that we get run.
551 hb_fifo_push( w->fifo_in, in);
562 static void decsrtClose( hb_work_object_t * w )
564 hb_work_private_t * pv = w->private_data;
566 iconv_close(pv->iconv_context);
567 free( w->private_data );
570 hb_work_object_t hb_decsrtsub =
573 "SRT Subtitle Decoder",