WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / muxcommon.c
blobafda745bee9be60bc9f5a8e64e0f677d5806e146
1 /* muxcommon.c
3 Copyright (c) 2003-2015 HandBrake Team
4 This file is part of the HandBrake source code
5 Homepage: <http://handbrake.fr/>.
6 It may be used under the terms of the GNU General Public License v2.
7 For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8 */
9 #include "hb.h"
10 #include "decssasub.h"
12 #define MIN_BUFFERING (1024*1024*10)
13 #define MAX_BUFFERING (1024*1024*50)
15 struct hb_mux_object_s
17 HB_MUX_COMMON;
20 typedef struct
22 int size; // Size in bits
23 uint32_t * vec;
24 } hb_bitvec_t;
26 typedef struct
28 hb_buffer_t **fifo;
29 uint32_t in; // number of bufs put into fifo
30 uint32_t out; // number of bufs taken out of fifo
31 uint32_t flen; // fifo length (must be power of two)
32 } mux_fifo_t;
34 typedef struct
36 hb_mux_data_t * mux_data;
37 uint64_t frames;
38 uint64_t bytes;
39 mux_fifo_t mf;
40 int buffered_size;
41 } hb_track_t;
43 typedef struct
45 hb_lock_t * mutex;
46 int ref;
47 int done;
48 hb_mux_object_t * m;
49 double pts; // end time of next muxing chunk
50 double interleave; // size in 90KHz ticks of media chunks we mux
51 uint32_t max_tracks; // total number of tracks allocated
52 uint32_t ntracks; // total number of tracks we're muxing
53 hb_bitvec_t * eof; // bitmask of track with eof
54 hb_bitvec_t * rdy; // bitmask of tracks ready to output
55 hb_bitvec_t * allEof; // valid bits in eof (all tracks)
56 hb_bitvec_t * allRdy; // valid bits in rdy (audio & video tracks)
57 hb_track_t ** track; // tracks to mux 'max_tracks' elements
58 int buffered_size;
59 } hb_mux_t;
61 struct hb_work_private_s
63 hb_job_t * job;
64 int track;
65 hb_mux_t * mux;
69 static int hb_bitvec_add_bits(hb_bitvec_t *bv, int bits)
71 int ii;
72 int words_cur = (bv->size + 31) >> 5;
73 int words = (bv->size + bits + 31) >> 5;
74 if (words > words_cur)
76 uint32_t *tmp = realloc(bv->vec, words * sizeof(uint32_t));
77 if (tmp == NULL)
79 return -1;
81 for (ii = words_cur; ii < words; ii++)
82 tmp[ii] = 0;
83 bv->vec = tmp;
85 bv->size += bits;
86 return 0;
89 static hb_bitvec_t* hb_bitvec_new(int size)
91 hb_bitvec_t *bv = calloc(sizeof(hb_bitvec_t), 1);
92 hb_bitvec_add_bits(bv, size);
93 return bv;
96 static void hb_bitvec_free(hb_bitvec_t **_bv)
98 hb_bitvec_t *bv = *_bv;
99 free(bv->vec);
100 free(bv);
101 *_bv = NULL;
104 static void hb_bitvec_set(hb_bitvec_t *bv, int n)
106 if (n >= bv->size)
107 return; // Error. Should never happen.
109 int word = n >> 5;
110 uint32_t bit = 1 << (n & 0x1F);
111 bv->vec[word] |= bit;
114 static void hb_bitvec_clr(hb_bitvec_t *bv, int n)
116 if (n >= bv->size)
117 return; // Error. Should never happen.
119 int word = n >> 5;
120 uint32_t bit = 1 << (n & 0x1F);
121 bv->vec[word] &= ~bit;
124 static void hb_bitvec_zero(hb_bitvec_t *bv)
126 int words = (bv->size + 31) >> 5;
127 memset(bv->vec, 0, words * sizeof(uint32_t));
130 static int hb_bitvec_bit(hb_bitvec_t *bv, int n)
132 if (n >= bv->size)
133 return 0; // Error. Should never happen.
135 int word = n >> 5;
136 uint32_t bit = 1 << (n & 0x1F);
137 return !!(bv->vec[word] & bit);
140 static int hb_bitvec_any(hb_bitvec_t *bv)
142 uint32_t result = 0;;
143 int ii;
144 int words = (bv->size + 31) >> 5;
145 for (ii = 0; ii < words; ii++)
146 result |= bv->vec[ii];
148 return !!result;
151 static int hb_bitvec_cmp(hb_bitvec_t *bv1, hb_bitvec_t *bv2)
153 if (bv1->size != bv2->size)
154 return 0;
156 int ii;
157 int words = (bv1->size + 31) >> 5;
158 for (ii = 0; ii < words; ii++)
159 if (bv1->vec[ii] != bv2->vec[ii])
160 return 0;
161 return 1;
164 static int hb_bitvec_and_cmp(hb_bitvec_t *bv1, hb_bitvec_t *bv2, hb_bitvec_t *bv3)
166 if (bv1->size != bv2->size)
167 return 0;
169 int ii;
170 int words = (bv1->size + 31) >> 5;
171 for (ii = 0; ii < words; ii++)
172 if ((bv1->vec[ii] & bv2->vec[ii]) != bv3->vec[ii])
173 return 0;
174 return 1;
177 static int hb_bitvec_cpy(hb_bitvec_t *bv1, hb_bitvec_t *bv2)
179 if (bv1->size < bv2->size)
181 int result = hb_bitvec_add_bits(bv1, bv2->size - bv1->size);
182 if (result < 0)
183 return result;
186 int words = (bv1->size + 31) >> 5;
187 memcpy(bv1->vec, bv2->vec, words * sizeof(uint32_t));
189 return 0;
192 // The muxer handles two different kinds of media: Video and audio tracks
193 // are continuous: once they start they generate continuous, consecutive
194 // sequence of bufs until they end. The muxer will time align all continuous
195 // media tracks so that their data will be well interleaved in the output file.
196 // (Smooth, low latency playback with minimal player buffering requires that
197 // data that's going to be presented close together in time also be close
198 // together in the output file). Since HB's audio and video encoders run at
199 // different speeds, the time-aligning involves buffering *all* the continuous
200 // media tracks until a frame with a timestamp beyond the current alignment
201 // point arrives on the slowest fifo (usually the video encoder).
203 // The other kind of media, subtitles, close-captions, vobsubs and
204 // similar tracks, are intermittent. They generate frames sporadically or on
205 // human time scales (seconds) rather than near the video frame rate (milliseconds).
206 // If intermittent sources were treated like continuous sources huge sections of
207 // audio and video would get buffered waiting for the next subtitle to show up.
208 // To keep this from happening the muxer doesn't wait for intermittent tracks
209 // (essentially it assumes that they will always go through the HB processing
210 // pipeline faster than the associated video). They are still time aligned and
211 // interleaved at the appropriate point in the output file.
213 // This routine adds another track for the muxer to process. The media input
214 // stream will be read from HandBrake fifo 'fifo'. Buffers read from that
215 // stream will be time-aligned with all the other media streams then passed
216 // to the container-specific 'mux' routine with argument 'mux_data' (see
217 // routine OutputTrackChunk). 'is_continuous' must be 1 for an audio or video
218 // track and 0 otherwise (see above).
220 static void add_mux_track( hb_mux_t *mux, hb_mux_data_t *mux_data,
221 int is_continuous )
223 if ( mux->ntracks + 1 > mux->max_tracks )
225 int max_tracks = mux->max_tracks ? mux->max_tracks * 2 : 32;
226 hb_track_t **tmp;
227 tmp = realloc(mux->track, max_tracks * sizeof(hb_track_t*));
228 if (tmp == NULL)
230 hb_error("add_mux_track: realloc failed, too many tracks (>%d)",
231 max_tracks);
232 return;
234 mux->track = tmp;
235 mux->max_tracks = max_tracks;
238 hb_track_t *track = calloc( sizeof( hb_track_t ), 1 );
239 track->mux_data = mux_data;
240 track->mf.flen = 8;
241 track->mf.fifo = calloc( sizeof(track->mf.fifo[0]), track->mf.flen );
243 int t = mux->ntracks++;
244 mux->track[t] = track;
245 hb_bitvec_set(mux->allEof, t);
246 if (is_continuous)
247 hb_bitvec_set(mux->allRdy, t);
250 static int mf_full( hb_track_t * track )
252 if ( track->buffered_size > MAX_BUFFERING )
253 return 1;
255 return 0;
258 static void mf_push( hb_mux_t * mux, int tk, hb_buffer_t *buf )
260 hb_track_t * track = mux->track[tk];
261 uint32_t mask = track->mf.flen - 1;
262 uint32_t in = track->mf.in;
264 hb_buffer_reduce( buf, buf->size );
265 if ( track->buffered_size > MAX_BUFFERING )
267 hb_bitvec_cpy(mux->rdy, mux->allRdy);
269 if ( ( ( in + 1 ) & mask ) == ( track->mf.out & mask ) )
271 // fifo is full - expand it to double the current size.
272 // This is a bit tricky because when we change the size
273 // it changes the modulus (mask) used to convert the in
274 // and out counters to fifo indices. Since existing items
275 // will be referenced at a new location after the expand
276 // we can't just realloc the fifo. If there were
277 // hundreds of fifo entries it would be worth it to have code
278 // for each of the four possible before/after configurations
279 // but these fifos are small so we just allocate a new chunk
280 // of memory then do element by element copies using the old &
281 // new masks then free the old fifo's memory..
282 track->mf.flen *= 2;
283 uint32_t nmask = track->mf.flen - 1;
284 hb_buffer_t **nfifo = malloc( track->mf.flen * sizeof(*nfifo) );
285 int indx = track->mf.out;
286 while ( indx != track->mf.in )
288 nfifo[indx & nmask] = track->mf.fifo[indx & mask];
289 ++indx;
291 free( track->mf.fifo );
292 track->mf.fifo = nfifo;
293 mask = nmask;
295 track->mf.fifo[in & mask] = buf;
296 track->mf.in = in + 1;
297 track->buffered_size += buf->size;
298 mux->buffered_size += buf->size;
301 static hb_buffer_t *mf_pull( hb_mux_t * mux, int tk )
303 hb_track_t *track =mux->track[tk];
304 hb_buffer_t *b = NULL;
305 if ( track->mf.out != track->mf.in )
307 // the fifo isn't empty
308 b = track->mf.fifo[track->mf.out & (track->mf.flen - 1)];
309 ++track->mf.out;
311 track->buffered_size -= b->size;
312 mux->buffered_size -= b->size;
314 return b;
317 static hb_buffer_t *mf_peek( hb_track_t *track )
319 return track->mf.out == track->mf.in ?
320 NULL : track->mf.fifo[track->mf.out & (track->mf.flen - 1)];
323 static void MoveToInternalFifos( int tk, hb_mux_t *mux, hb_buffer_t * buf )
325 // move all the buffers on the track's fifo to our internal
326 // fifo so that (a) we don't deadlock in the reader and
327 // (b) we can control how data from multiple tracks is
328 // interleaved in the output file.
329 mf_push( mux, tk, buf );
330 if ( buf->s.start >= mux->pts )
332 // buffer is past our next interleave point so
333 // note that this track is ready to be output.
334 hb_bitvec_set(mux->rdy, tk);
338 static void OutputTrackChunk( hb_mux_t *mux, int tk, hb_mux_object_t *m )
340 hb_track_t *track = mux->track[tk];
341 hb_buffer_t *buf;
343 while ( ( buf = mf_peek( track ) ) != NULL && buf->s.start < mux->pts )
345 buf = mf_pull( mux, tk );
346 track->frames += 1;
347 track->bytes += buf->size;
348 m->mux( m, track->mux_data, buf );
352 static int muxWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
353 hb_buffer_t ** buf_out )
355 hb_work_private_t * pv = w->private_data;
356 hb_job_t * job = pv->job;
357 hb_mux_t * mux = pv->mux;
358 hb_track_t * track;
359 int i;
360 hb_buffer_t * buf = *buf_in;
362 hb_lock( mux->mutex );
363 if ( mux->done )
365 hb_unlock( mux->mutex );
366 return HB_WORK_DONE;
369 if (buf->s.flags & HB_BUF_FLAG_EOF)
371 // EOF - mark this track as done
372 hb_buffer_close( &buf );
373 hb_bitvec_set(mux->eof, pv->track);
374 hb_bitvec_set(mux->rdy, pv->track);
376 else if ((job->pass_id != HB_PASS_ENCODE &&
377 job->pass_id != HB_PASS_ENCODE_2ND) ||
378 hb_bitvec_bit(mux->eof, pv->track))
380 hb_buffer_close( &buf );
382 else
384 MoveToInternalFifos( pv->track, mux, buf );
386 *buf_in = NULL;
388 if (!hb_bitvec_and_cmp(mux->rdy, mux->allRdy, mux->allRdy) &&
389 !hb_bitvec_and_cmp(mux->eof, mux->allEof, mux->allEof))
391 hb_unlock( mux->mutex );
392 return HB_WORK_OK;
395 hb_bitvec_t *more;
396 more = hb_bitvec_new(0);
397 hb_bitvec_cpy(more, mux->rdy);
398 // all tracks have at least 'interleave' ticks of data. Output
399 // all that we can in 'interleave' size chunks.
400 while ((hb_bitvec_and_cmp(mux->rdy, mux->allRdy, mux->allRdy) &&
401 hb_bitvec_any(more) && mux->buffered_size > MIN_BUFFERING ) ||
402 (hb_bitvec_cmp(mux->eof, mux->allEof)))
404 hb_bitvec_zero(more);
405 for ( i = 0; i < mux->ntracks; ++i )
407 track = mux->track[i];
408 OutputTrackChunk( mux, i, mux->m );
409 if ( mf_full( track ) )
411 // If the track's fifo is still full, advance
412 // the currint interleave point and try again.
413 hb_bitvec_cpy(mux->rdy, mux->allRdy);
414 break;
417 // if the track is at eof or still has data that's past
418 // our next interleave point then leave it marked as rdy.
419 // Otherwise clear rdy.
420 if (hb_bitvec_bit(mux->eof, i) &&
421 (track->mf.out == track->mf.in ||
422 track->mf.fifo[(track->mf.in-1) & (track->mf.flen-1)]->s.start
423 < mux->pts + mux->interleave))
425 hb_bitvec_clr(mux->rdy, i);
427 if ( track->mf.out != track->mf.in )
429 hb_bitvec_set(more, i);
433 // if all the tracks are at eof we're just purging their
434 // remaining data -- keep going until all internal fifos are empty.
435 if (hb_bitvec_cmp(mux->eof, mux->allEof))
437 for ( i = 0; i < mux->ntracks; ++i )
439 if ( mux->track[i]->mf.out != mux->track[i]->mf.in )
441 break;
444 if ( i >= mux->ntracks )
446 mux->done = 1;
447 hb_unlock( mux->mutex );
448 hb_bitvec_free(&more);
449 return HB_WORK_DONE;
452 mux->pts += mux->interleave;
454 hb_bitvec_free(&more);
456 hb_unlock( mux->mutex );
457 return HB_WORK_OK;
460 void muxClose( hb_work_object_t * w )
462 hb_work_private_t * pv = w->private_data;
463 hb_mux_t * mux = pv->mux;
464 hb_job_t * job = pv->job;
465 hb_track_t * track;
466 int i;
468 hb_lock( mux->mutex );
469 if ( --mux->ref == 0 )
471 // Update state before closing muxer. Closing the muxer
472 // may initiate optimization which can take a while and
473 // we want the muxing state to be visible while this is
474 // happening.
475 if( job->pass_id == HB_PASS_ENCODE ||
476 job->pass_id == HB_PASS_ENCODE_2ND )
478 /* Update the UI */
479 hb_state_t state;
480 state.state = HB_STATE_MUXING;
481 state.param.muxing.progress = 0;
482 hb_set_state( job->h, &state );
485 if( mux->m )
487 mux->m->end( mux->m );
488 free( mux->m );
491 // we're all done muxing -- print final stats and cleanup.
492 if( job->pass_id == HB_PASS_ENCODE ||
493 job->pass_id == HB_PASS_ENCODE_2ND )
495 hb_stat_t sb;
496 uint64_t bytes_total, frames_total;
498 if (!hb_stat(job->file, &sb))
500 hb_deep_log( 2, "mux: file size, %"PRId64" bytes", (uint64_t) sb.st_size );
502 bytes_total = 0;
503 frames_total = 0;
504 for( i = 0; i < mux->ntracks; ++i )
506 track = mux->track[i];
507 hb_log( "mux: track %d, %"PRId64" frames, %"PRId64" bytes, %.2f kbps, fifo %d",
508 i, track->frames, track->bytes,
509 90000.0 * track->bytes / mux->pts / 125,
510 track->mf.flen );
511 if( !i && job->vquality < 0 )
513 /* Video */
514 hb_deep_log( 2, "mux: video bitrate error, %+"PRId64" bytes",
515 (int64_t)(track->bytes - mux->pts * job->vbitrate * 125 / 90000) );
517 bytes_total += track->bytes;
518 frames_total += track->frames;
521 if( bytes_total && frames_total )
523 hb_deep_log( 2, "mux: overhead, %.2f bytes per frame",
524 (float) ( sb.st_size - bytes_total ) /
525 frames_total );
530 for( i = 0; i < mux->ntracks; ++i )
532 hb_buffer_t * b;
533 track = mux->track[i];
534 while ( (b = mf_pull( mux, i )) != NULL )
536 hb_buffer_close( &b );
538 if( track->mux_data )
540 free( track->mux_data );
541 free( track->mf.fifo );
543 free( track );
545 free(mux->track);
546 hb_unlock( mux->mutex );
547 hb_lock_close( &mux->mutex );
548 hb_bitvec_free(&mux->eof);
549 hb_bitvec_free(&mux->rdy);
550 hb_bitvec_free(&mux->allEof);
551 hb_bitvec_free(&mux->allRdy);
552 free( mux );
554 else
556 hb_unlock( mux->mutex );
558 free( pv );
559 w->private_data = NULL;
562 static void mux_loop( void * _w )
564 hb_work_object_t * w = _w;
565 hb_work_private_t * pv = w->private_data;
566 hb_job_t * job = pv->job;
567 hb_buffer_t * buf_in;
569 while ( !*job->die && w->status != HB_WORK_DONE )
571 buf_in = hb_fifo_get_wait( w->fifo_in );
572 if ( pv->mux->done )
573 break;
574 if ( buf_in == NULL )
575 continue;
576 if ( *job->die )
578 if( buf_in )
580 hb_buffer_close( &buf_in );
582 break;
585 w->status = w->work( w, &buf_in, NULL );
586 if( buf_in )
588 hb_buffer_close( &buf_in );
593 hb_work_object_t * hb_muxer_init( hb_job_t * job )
595 int i;
596 hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 );
597 hb_work_object_t * w;
598 hb_work_object_t * muxer;
600 // The bit vectors must be allocated before hb_thread_init for the
601 // audio and subtitle muxer jobs below.
602 int bit_vec_size = 1 + hb_list_count(job->list_audio) +
603 hb_list_count(job->list_subtitle);
604 mux->rdy = hb_bitvec_new(bit_vec_size);
605 mux->eof = hb_bitvec_new(bit_vec_size);
606 mux->allRdy = hb_bitvec_new(bit_vec_size);
607 mux->allEof = hb_bitvec_new(bit_vec_size);
609 mux->mutex = hb_lock_init();
611 // set up to interleave track data in blocks of 1 video frame time.
612 // (the best case for buffering and playout latency). The container-
613 // specific muxers can reblock this into bigger chunks if necessary.
614 mux->interleave = 90000. * (double)job->vrate.den / job->vrate.num;
615 mux->pts = mux->interleave;
617 /* Get a real muxer */
618 if( job->pass_id == HB_PASS_ENCODE || job->pass_id == HB_PASS_ENCODE_2ND )
620 switch( job->mux )
622 case HB_MUX_AV_MP4:
623 case HB_MUX_AV_MKV:
624 mux->m = hb_mux_avformat_init( job );
625 break;
626 default:
627 hb_error( "No muxer selected, exiting" );
628 *job->done_error = HB_ERROR_INIT;
629 *job->die = 1;
630 return NULL;
632 /* Create file, write headers */
633 if( mux->m )
635 mux->m->init( mux->m );
639 /* Initialize the work objects that will receive fifo data */
641 muxer = hb_get_work( job->h, WORK_MUX );
642 muxer->private_data = calloc( sizeof( hb_work_private_t ), 1 );
643 muxer->private_data->job = job;
644 muxer->private_data->mux = mux;
645 mux->ref++;
646 muxer->private_data->track = mux->ntracks;
647 muxer->fifo_in = job->fifo_mpeg4;
648 add_mux_track( mux, job->mux_data, 1 );
649 muxer->done = &muxer->private_data->mux->done;
651 for( i = 0; i < hb_list_count( job->list_audio ); i++ )
653 hb_audio_t *audio = hb_list_item( job->list_audio, i );
655 w = hb_get_work( job->h, WORK_MUX );
656 w->private_data = calloc( sizeof( hb_work_private_t ), 1 );
657 w->private_data->job = job;
658 w->private_data->mux = mux;
659 mux->ref++;
660 w->private_data->track = mux->ntracks;
661 w->fifo_in = audio->priv.fifo_out;
662 add_mux_track( mux, audio->priv.mux_data, 1 );
663 w->done = &job->done;
664 hb_list_add( job->list_work, w );
665 w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY );
668 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
670 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
672 if (subtitle->config.dest != PASSTHRUSUB)
673 continue;
675 w = hb_get_work( job->h, WORK_MUX );
676 w->private_data = calloc( sizeof( hb_work_private_t ), 1 );
677 w->private_data->job = job;
678 w->private_data->mux = mux;
679 mux->ref++;
680 w->private_data->track = mux->ntracks;
681 w->fifo_in = subtitle->fifo_out;
682 add_mux_track( mux, subtitle->mux_data, 0 );
683 w->done = &job->done;
684 hb_list_add( job->list_work, w );
685 w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY );
687 return muxer;
690 // muxInit does nothing because the muxer has a special initializer
691 // that takes care of initializing all muxer work objects
692 static int muxInit( hb_work_object_t * w, hb_job_t * job )
694 return 0;
697 hb_work_object_t hb_muxer =
699 WORK_MUX,
700 "Muxer",
701 muxInit,
702 muxWork,
703 muxClose
706 #define TX3G_STYLES (HB_STYLE_FLAG_BOLD | \
707 HB_STYLE_FLAG_ITALIC | \
708 HB_STYLE_FLAG_UNDERLINE)
710 typedef struct style_context_s
712 uint8_t * style_atoms;
713 int style_atom_count;
714 hb_subtitle_style_t current_style;
715 int style_start;
716 } style_context_t;
718 static void update_style_atoms(style_context_t *ctx, int stop)
720 uint8_t *style_entry;
721 uint8_t face = 0;
723 style_entry = ctx->style_atoms + 10 + (12 * ctx->style_atom_count);
725 if (ctx->current_style.flags & HB_STYLE_FLAG_BOLD)
726 face |= 1;
727 if (ctx->current_style.flags & HB_STYLE_FLAG_ITALIC)
728 face |= 2;
729 if (ctx->current_style.flags & HB_STYLE_FLAG_UNDERLINE)
730 face |= 4;
732 style_entry[0] = (ctx->style_start >> 8) & 0xff; // startChar
733 style_entry[1] = ctx->style_start & 0xff;
734 style_entry[2] = (stop >> 8) & 0xff; // endChar
735 style_entry[3] = stop & 0xff;
736 style_entry[4] = 0; // font-ID msb
737 style_entry[5] = 1; // font-ID lsb
738 style_entry[6] = face; // face-style-flags
739 style_entry[7] = 24; // font-size
740 style_entry[8] = (ctx->current_style.fg_rgb >> 16) & 0xff; // r
741 style_entry[9] = (ctx->current_style.fg_rgb >> 8) & 0xff; // g
742 style_entry[10] = (ctx->current_style.fg_rgb) & 0xff; // b
743 style_entry[11] = ctx->current_style.fg_alpha; // a
745 ctx->style_atom_count++;
748 static void update_style(style_context_t *ctx,
749 hb_subtitle_style_t *style, int pos)
751 if (ctx->style_start < pos)
753 // do we need to add a style atom?
754 if (((ctx->current_style.flags ^ style->flags) & TX3G_STYLES) ||
755 ctx->current_style.fg_rgb != style->fg_rgb ||
756 ctx->current_style.fg_alpha != style->fg_alpha)
758 update_style_atoms(ctx, pos - 1);
759 ctx->current_style = *style;
760 ctx->style_start = pos;
763 else
765 ctx->current_style = *style;
766 ctx->style_start = pos;
770 static void style_context_init(style_context_t *ctx, uint8_t *style_atoms)
772 memset(ctx, 0, sizeof(*ctx));
773 ctx->style_atoms = style_atoms;
774 ctx->style_start = INT_MAX;
778 * Copy the input to output removing markup and adding markup to the style
779 * atom where appropriate.
781 void hb_muxmp4_process_subtitle_style(uint8_t *input,
782 uint8_t *output,
783 uint8_t *style_atoms, uint16_t *stylesize)
785 uint16_t utf8_count = 0; // utf8 count from start of subtitle
786 int consumed, in_pos = 0, out_pos = 0, len, ii, lines;
787 style_context_t ctx;
788 hb_subtitle_style_t style;
789 char *text, *tmp;
791 *stylesize = 0;
792 style_context_init(&ctx, style_atoms);
794 hb_ssa_style_init(&style);
796 // Skip past the SSA preamble
797 text = (char*)input;
798 for (ii = 0; ii < 8; ii++)
800 tmp = strchr(text, ',');
801 if (tmp == NULL)
802 break;
803 text = tmp + 1;
805 in_pos = text - (char*)input;
807 while (input[in_pos] != '\0')
809 lines = 1;
810 text = hb_ssa_to_text((char*)input + in_pos, &consumed, &style);
811 if (text == NULL)
812 break;
814 // count UTF8 characters, and get length of text
815 len = 0;
816 for (ii = 0; text[ii] != '\0'; ii++)
818 if ((text[ii] & 0xc0) == 0x80)
820 utf8_count++;
821 hb_deep_log( 3, "mux: Counted %d UTF-8 chrs within subtitle",
822 utf8_count);
824 // By default tx3g only supports 2 lines of text
825 // To support more lines, we must enable the virtical placement
826 // flag in the tx3g atom and add tbox atoms to the sample
827 // data to set the vertical placement for each subtitle.
828 // Although tbox defines a rectangle, the QT spec says
829 // that only the vertical placement is honored (bummer).
830 if (text[ii] == '\n')
832 lines++;
833 if (lines > 2)
834 text[ii] = ' ';
836 len++;
838 strcpy((char*)output+out_pos, text);
839 free(text);
840 out_pos += len;
841 in_pos += consumed;
842 update_style(&ctx, &style, out_pos - utf8_count);
844 // Return to default style at end of line, flushes any pending
845 // style changes
846 hb_ssa_style_init(&style);
847 update_style(&ctx, &style, out_pos - utf8_count);
849 // null terminate output string
850 output[out_pos] = 0;
852 if (ctx.style_atom_count > 0)
854 *stylesize = 10 + (ctx.style_atom_count * 12);
856 memcpy(style_atoms + 4, "styl", 4);
858 style_atoms[0] = 0;
859 style_atoms[1] = 0;
860 style_atoms[2] = (*stylesize >> 8) & 0xff;
861 style_atoms[3] = *stylesize & 0xff;
862 style_atoms[8] = (ctx.style_atom_count >> 8) & 0xff;
863 style_atoms[9] = ctx.style_atom_count & 0xff;