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
10 #include "decssasub.h"
12 #define MIN_BUFFERING (1024*1024*10)
13 #define MAX_BUFFERING (1024*1024*50)
15 struct hb_mux_object_s
22 int size
; // Size in bits
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)
36 hb_mux_data_t
* mux_data
;
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
61 struct hb_work_private_s
69 static int hb_bitvec_add_bits(hb_bitvec_t
*bv
, int bits
)
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));
81 for (ii
= words_cur
; ii
< words
; ii
++)
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
);
96 static void hb_bitvec_free(hb_bitvec_t
**_bv
)
98 hb_bitvec_t
*bv
= *_bv
;
104 static void hb_bitvec_set(hb_bitvec_t
*bv
, int n
)
107 return; // Error. Should never happen.
110 uint32_t bit
= 1 << (n
& 0x1F);
111 bv
->vec
[word
] |= bit
;
114 static void hb_bitvec_clr(hb_bitvec_t
*bv
, int n
)
117 return; // Error. Should never happen.
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
)
133 return 0; // Error. Should never happen.
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;;
144 int words
= (bv
->size
+ 31) >> 5;
145 for (ii
= 0; ii
< words
; ii
++)
146 result
|= bv
->vec
[ii
];
151 static int hb_bitvec_cmp(hb_bitvec_t
*bv1
, hb_bitvec_t
*bv2
)
153 if (bv1
->size
!= bv2
->size
)
157 int words
= (bv1
->size
+ 31) >> 5;
158 for (ii
= 0; ii
< words
; ii
++)
159 if (bv1
->vec
[ii
] != bv2
->vec
[ii
])
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
)
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
])
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
);
186 int words
= (bv1
->size
+ 31) >> 5;
187 memcpy(bv1
->vec
, bv2
->vec
, words
* sizeof(uint32_t));
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
,
223 if ( mux
->ntracks
+ 1 > mux
->max_tracks
)
225 int max_tracks
= mux
->max_tracks
? mux
->max_tracks
* 2 : 32;
227 tmp
= realloc(mux
->track
, max_tracks
* sizeof(hb_track_t
*));
230 hb_error("add_mux_track: realloc failed, too many tracks (>%d)",
235 mux
->max_tracks
= max_tracks
;
238 hb_track_t
*track
= calloc( sizeof( hb_track_t
), 1 );
239 track
->mux_data
= mux_data
;
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
);
247 hb_bitvec_set(mux
->allRdy
, t
);
250 static int mf_full( hb_track_t
* track
)
252 if ( track
->buffered_size
> MAX_BUFFERING
)
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..
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
];
291 free( track
->mf
.fifo
);
292 track
->mf
.fifo
= nfifo
;
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)];
311 track
->buffered_size
-= b
->size
;
312 mux
->buffered_size
-= b
->size
;
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
];
343 while ( ( buf
= mf_peek( track
) ) != NULL
&& buf
->s
.start
< mux
->pts
)
345 buf
= mf_pull( mux
, tk
);
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
;
360 hb_buffer_t
* buf
= *buf_in
;
362 hb_lock( mux
->mutex
);
365 hb_unlock( mux
->mutex
);
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
);
384 MoveToInternalFifos( pv
->track
, mux
, buf
);
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
);
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
);
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
)
444 if ( i
>= mux
->ntracks
)
447 hb_unlock( mux
->mutex
);
448 hb_bitvec_free(&more
);
452 mux
->pts
+= mux
->interleave
;
454 hb_bitvec_free(&more
);
456 hb_unlock( mux
->mutex
);
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
;
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
475 if( job
->pass_id
== HB_PASS_ENCODE
||
476 job
->pass_id
== HB_PASS_ENCODE_2ND
)
480 state
.state
= HB_STATE_MUXING
;
481 state
.param
.muxing
.progress
= 0;
482 hb_set_state( job
->h
, &state
);
487 mux
->m
->end( 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
)
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
);
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,
511 if( !i
&& job
->vquality
< 0 )
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
) /
530 for( i
= 0; i
< mux
->ntracks
; ++i
)
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
);
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
);
556 hb_unlock( mux
->mutex
);
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
);
574 if ( buf_in
== NULL
)
580 hb_buffer_close( &buf_in
);
585 w
->status
= w
->work( w
, &buf_in
, NULL
);
588 hb_buffer_close( &buf_in
);
593 hb_work_object_t
* hb_muxer_init( hb_job_t
* job
)
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
)
624 mux
->m
= hb_mux_avformat_init( job
);
627 hb_error( "No muxer selected, exiting" );
628 *job
->done_error
= HB_ERROR_INIT
;
632 /* Create file, write headers */
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
;
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
;
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
)
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
;
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
);
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
)
697 hb_work_object_t hb_muxer
=
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
;
718 static void update_style_atoms(style_context_t
*ctx
, int stop
)
720 uint8_t *style_entry
;
723 style_entry
= ctx
->style_atoms
+ 10 + (12 * ctx
->style_atom_count
);
725 if (ctx
->current_style
.flags
& HB_STYLE_FLAG_BOLD
)
727 if (ctx
->current_style
.flags
& HB_STYLE_FLAG_ITALIC
)
729 if (ctx
->current_style
.flags
& HB_STYLE_FLAG_UNDERLINE
)
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
;
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
,
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
;
788 hb_subtitle_style_t style
;
792 style_context_init(&ctx
, style_atoms
);
794 hb_ssa_style_init(&style
);
796 // Skip past the SSA preamble
798 for (ii
= 0; ii
< 8; ii
++)
800 tmp
= strchr(text
, ',');
805 in_pos
= text
- (char*)input
;
807 while (input
[in_pos
] != '\0')
810 text
= hb_ssa_to_text((char*)input
+ in_pos
, &consumed
, &style
);
814 // count UTF8 characters, and get length of text
816 for (ii
= 0; text
[ii
] != '\0'; ii
++)
818 if ((text
[ii
] & 0xc0) == 0x80)
821 hb_deep_log( 3, "mux: Counted %d UTF-8 chrs within subtitle",
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')
838 strcpy((char*)output
+out_pos
, text
);
842 update_style(&ctx
, &style
, out_pos
- utf8_count
);
844 // Return to default style at end of line, flushes any pending
846 hb_ssa_style_init(&style
);
847 update_style(&ctx
, &style
, out_pos
- utf8_count
);
849 // null terminate output string
852 if (ctx
.style_atom_count
> 0)
854 *stylesize
= 10 + (ctx
.style_atom_count
* 12);
856 memcpy(style_atoms
+ 4, "styl", 4);
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;