WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / rendersub.c
blob3c17c4a01c592c6c7bfa343701e709d1982da802
1 /* rendersub.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 */
10 #include "hb.h"
11 #include "hbffmpeg.h"
12 #include <ass/ass.h>
14 struct hb_filter_private_s
16 // Common
17 int crop[4];
18 int type;
20 // VOBSUB
21 hb_list_t * sub_list; // List of active subs
23 // SSA
24 ASS_Library * ssa;
25 ASS_Renderer * renderer;
26 ASS_Track * ssaTrack;
27 uint8_t script_initialized;
29 // SRT
30 int line;
31 hb_buffer_t * current_sub;
34 // VOBSUB
35 static int vobsub_post_init( hb_filter_object_t * filter, hb_job_t * job );
37 static int vobsub_work( hb_filter_object_t * filter,
38 hb_buffer_t ** buf_in,
39 hb_buffer_t ** buf_out );
41 static void vobsub_close( hb_filter_object_t * filter );
44 // SSA
45 static int ssa_post_init( hb_filter_object_t * filter, hb_job_t * job );
47 static int ssa_work( hb_filter_object_t * filter,
48 hb_buffer_t ** buf_in,
49 hb_buffer_t ** buf_out );
51 static void ssa_close( hb_filter_object_t * filter );
54 // SRT
55 static int textsub_post_init( hb_filter_object_t * filter, hb_job_t * job );
56 static int cc608sub_post_init( hb_filter_object_t * filter, hb_job_t * job );
58 static int textsub_work( hb_filter_object_t * filter,
59 hb_buffer_t ** buf_in,
60 hb_buffer_t ** buf_out );
62 static void textsub_close( hb_filter_object_t * filter );
65 // PGS
66 static int pgssub_post_init( hb_filter_object_t * filter, hb_job_t * job );
68 static int pgssub_work ( hb_filter_object_t * filter,
69 hb_buffer_t ** buf_in,
70 hb_buffer_t ** buf_out );
72 static void pgssub_close( hb_filter_object_t * filter );
75 // Entry points
76 static int hb_rendersub_init( hb_filter_object_t * filter,
77 hb_filter_init_t * init );
79 static int hb_rendersub_post_init( hb_filter_object_t * filter, hb_job_t *job );
81 static int hb_rendersub_work( hb_filter_object_t * filter,
82 hb_buffer_t ** buf_in,
83 hb_buffer_t ** buf_out );
85 static void hb_rendersub_close( hb_filter_object_t * filter );
87 hb_filter_object_t hb_filter_render_sub =
89 .id = HB_FILTER_RENDER_SUB,
90 .enforce_order = 1,
91 .name = "Subtitle renderer",
92 .settings = NULL,
93 .init = hb_rendersub_init,
94 .post_init = hb_rendersub_post_init,
95 .work = hb_rendersub_work,
96 .close = hb_rendersub_close,
99 static void blend( hb_buffer_t *dst, hb_buffer_t *src, int left, int top )
101 int xx, yy;
102 int ww, hh;
103 int x0, y0;
104 uint8_t *y_in, *y_out;
105 uint8_t *u_in, *u_out;
106 uint8_t *v_in, *v_out;
107 uint8_t *a_in, alpha;
109 x0 = y0 = 0;
110 if( left < 0 )
112 x0 = -left;
114 if( top < 0 )
116 y0 = -top;
119 ww = src->f.width;
120 if( src->f.width - x0 > dst->f.width - left )
122 ww = dst->f.width - left + x0;
124 hh = src->f.height;
125 if( src->f.height - y0 > dst->f.height - top )
127 hh = dst->f.height - top + y0;
129 // Blend luma
130 for( yy = y0; yy < hh; yy++ )
132 y_in = src->plane[0].data + yy * src->plane[0].stride;
133 y_out = dst->plane[0].data + ( yy + top ) * dst->plane[0].stride;
134 a_in = src->plane[3].data + yy * src->plane[3].stride;
135 for( xx = x0; xx < ww; xx++ )
137 alpha = a_in[xx];
139 * Merge the luminance and alpha with the picture
141 y_out[left + xx] =
142 ( (uint16_t)y_out[left + xx] * ( 255 - alpha ) +
143 (uint16_t)y_in[xx] * alpha ) / 255;
147 // Blend U & V
148 // Assumes source and dest are the same PIX_FMT
149 int hshift = 0;
150 int wshift = 0;
151 if( dst->plane[1].height < dst->plane[0].height )
152 hshift = 1;
153 if( dst->plane[1].width < dst->plane[0].width )
154 wshift = 1;
156 for( yy = y0 >> hshift; yy < hh >> hshift; yy++ )
158 u_in = src->plane[1].data + yy * src->plane[1].stride;
159 u_out = dst->plane[1].data + ( yy + ( top >> hshift ) ) * dst->plane[1].stride;
160 v_in = src->plane[2].data + yy * src->plane[2].stride;
161 v_out = dst->plane[2].data + ( yy + ( top >> hshift ) ) * dst->plane[2].stride;
162 a_in = src->plane[3].data + ( yy << hshift ) * src->plane[3].stride;
164 for( xx = x0 >> wshift; xx < ww >> wshift; xx++ )
166 alpha = a_in[xx << wshift];
168 // Blend averge U and alpha
169 u_out[(left >> wshift) + xx] =
170 ( (uint16_t)u_out[(left >> wshift) + xx] * ( 255 - alpha ) +
171 (uint16_t)u_in[xx] * alpha ) / 255;
173 // Blend V and alpha
174 v_out[(left >> wshift) + xx] =
175 ( (uint16_t)v_out[(left >> wshift) + xx] * ( 255 - alpha ) +
176 (uint16_t)v_in[xx] * alpha ) / 255;
181 // Assumes that the input buffer has the same dimensions
182 // as the original title diminsions
183 static void ApplySub( hb_filter_private_t * pv, hb_buffer_t * buf, hb_buffer_t * sub )
185 int top, left, margin_top, margin_percent;
187 if ( !pv->ssa )
190 * Percent of height of picture that form a margin that subtitles
191 * should not be displayed within.
193 margin_percent = 2;
196 * If necessary, move the subtitle so it is not in a cropped zone.
197 * When it won't fit, we center it so we lose as much on both ends.
198 * Otherwise we try to leave a 20px or 2% margin around it.
200 margin_top = ( ( buf->f.height - pv->crop[0] - pv->crop[1] ) *
201 margin_percent ) / 100;
203 if( margin_top > 20 )
206 * A maximum margin of 20px regardless of height of the picture.
208 margin_top = 20;
211 if( sub->f.height > buf->f.height - pv->crop[0] - pv->crop[1] -
212 ( margin_top * 2 ) )
215 * The subtitle won't fit in the cropped zone, so center
216 * it vertically so we fit in as much as we can.
218 top = pv->crop[0] + ( buf->f.height - pv->crop[0] -
219 pv->crop[1] - sub->f.height ) / 2;
221 else if( sub->f.y < pv->crop[0] + margin_top )
224 * The subtitle fits in the cropped zone, but is currently positioned
225 * within our top margin, so move it outside of our margin.
227 top = pv->crop[0] + margin_top;
229 else if( sub->f.y > buf->f.height - pv->crop[1] - margin_top - sub->f.height )
232 * The subtitle fits in the cropped zone, and is not within the top
233 * margin but is within the bottom margin, so move it to be above
234 * the margin.
236 top = buf->f.height - pv->crop[1] - margin_top - sub->f.height;
238 else
241 * The subtitle is fine where it is.
243 top = sub->f.y;
246 if( sub->f.width > buf->f.width - pv->crop[2] - pv->crop[3] - 40 )
247 left = pv->crop[2] + ( buf->f.width - pv->crop[2] -
248 pv->crop[3] - sub->f.width ) / 2;
249 else if( sub->f.x < pv->crop[2] + 20 )
250 left = pv->crop[2] + 20;
251 else if( sub->f.x > buf->f.width - pv->crop[3] - 20 - sub->f.width )
252 left = buf->f.width - pv->crop[3] - 20 - sub->f.width;
253 else
254 left = sub->f.x;
256 else
258 top = sub->f.y;
259 left = sub->f.x;
262 blend( buf, sub, left, top );
265 // Assumes that the input buffer has the same dimensions
266 // as the original title diminsions
267 static void ApplyVOBSubs( hb_filter_private_t * pv, hb_buffer_t * buf )
269 int ii;
270 hb_buffer_t *sub, *next;
272 for( ii = 0; ii < hb_list_count(pv->sub_list); )
274 sub = hb_list_item( pv->sub_list, ii );
275 if (ii + 1 < hb_list_count(pv->sub_list))
276 next = hb_list_item( pv->sub_list, ii + 1 );
277 else
278 next = NULL;
280 if ((sub->s.stop != AV_NOPTS_VALUE && sub->s.stop <= buf->s.start) ||
281 (next != NULL && sub->s.stop == AV_NOPTS_VALUE && next->s.start <= buf->s.start))
283 // Subtitle stop is in the past, delete it
284 hb_list_rem( pv->sub_list, sub );
285 hb_buffer_close( &sub );
287 else if( sub->s.start <= buf->s.start )
289 // The subtitle has started before this frame and ends
290 // after it. Render the subtitle into the frame.
291 while ( sub )
293 ApplySub( pv, buf, sub );
294 sub = sub->next;
296 ii++;
298 else
300 // The subtitle starts in the future. No need to continue.
301 break;
306 static int vobsub_post_init( hb_filter_object_t * filter, hb_job_t * job )
308 hb_filter_private_t * pv = filter->private_data;
310 pv->sub_list = hb_list_init();
312 return 0;
315 static void vobsub_close( hb_filter_object_t * filter )
317 hb_filter_private_t * pv = filter->private_data;
319 if( !pv )
321 return;
324 if( pv->sub_list )
325 hb_list_empty( &pv->sub_list );
327 free( pv );
328 filter->private_data = NULL;
331 static int vobsub_work( hb_filter_object_t * filter,
332 hb_buffer_t ** buf_in,
333 hb_buffer_t ** buf_out )
335 hb_filter_private_t * pv = filter->private_data;
336 hb_buffer_t * in = *buf_in;
337 hb_buffer_t * sub;
339 if (in->s.flags & HB_BUF_FLAG_EOF)
341 *buf_in = NULL;
342 *buf_out = in;
343 return HB_FILTER_DONE;
346 // Get any pending subtitles and add them to the active
347 // subtitle list
348 while( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) )
350 hb_list_add( pv->sub_list, sub );
353 ApplyVOBSubs( pv, in );
354 *buf_in = NULL;
355 *buf_out = in;
357 return HB_FILTER_OK;
360 static uint8_t ssaAlpha( ASS_Image *frame, int x, int y )
362 unsigned frameA = ( frame->color ) & 0xff;
363 unsigned gliphA = frame->bitmap[y*frame->stride + x];
365 // Alpha for this pixel is the frame opacity (255 - frameA)
366 // multiplied by the gliph alfa (gliphA) for this pixel
367 unsigned alpha = (255 - frameA) * gliphA >> 8;
369 return (uint8_t)alpha;
372 static hb_buffer_t * RenderSSAFrame( hb_filter_private_t * pv, ASS_Image * frame )
374 hb_buffer_t *sub;
375 int xx, yy;
377 unsigned r = ( frame->color >> 24 ) & 0xff;
378 unsigned g = ( frame->color >> 16 ) & 0xff;
379 unsigned b = ( frame->color >> 8 ) & 0xff;
381 int yuv = hb_rgb2yuv((r << 16) | (g << 8) | b );
383 unsigned frameY = (yuv >> 16) & 0xff;
384 unsigned frameV = (yuv >> 8 ) & 0xff;
385 unsigned frameU = (yuv >> 0 ) & 0xff;
387 sub = hb_frame_buffer_init( AV_PIX_FMT_YUVA420P, frame->w, frame->h );
388 if( sub == NULL )
389 return NULL;
391 uint8_t *y_out, *u_out, *v_out, *a_out;
392 y_out = sub->plane[0].data;
393 u_out = sub->plane[1].data;
394 v_out = sub->plane[2].data;
395 a_out = sub->plane[3].data;
397 for( yy = 0; yy < frame->h; yy++ )
399 for( xx = 0; xx < frame->w; xx++ )
401 y_out[xx] = frameY;
402 if( ( yy & 1 ) == 0 )
404 u_out[xx>>1] = frameU;
405 v_out[xx>>1] = frameV;
407 a_out[xx] = ssaAlpha( frame, xx, yy );;
409 y_out += sub->plane[0].stride;
410 if( ( yy & 1 ) == 0 )
412 u_out += sub->plane[1].stride;
413 v_out += sub->plane[2].stride;
415 a_out += sub->plane[3].stride;
417 sub->f.width = frame->w;
418 sub->f.height = frame->h;
419 sub->f.x = frame->dst_x + pv->crop[2];
420 sub->f.y = frame->dst_y + pv->crop[0];
422 return sub;
425 static void ApplySSASubs( hb_filter_private_t * pv, hb_buffer_t * buf )
427 ASS_Image *frameList;
428 hb_buffer_t *sub;
430 frameList = ass_render_frame( pv->renderer, pv->ssaTrack,
431 buf->s.start / 90, NULL );
432 if ( !frameList )
433 return;
435 ASS_Image *frame;
436 for (frame = frameList; frame; frame = frame->next) {
437 sub = RenderSSAFrame( pv, frame );
438 if( sub )
440 ApplySub( pv, buf, sub );
441 hb_buffer_close( &sub );
446 static void ssa_log(int level, const char *fmt, va_list args, void *data)
448 if ( level < 5 ) // same as default verbosity when no callback is set
450 hb_valog( 1, "[ass]", fmt, args );
454 static int ssa_post_init( hb_filter_object_t * filter, hb_job_t * job )
456 hb_filter_private_t * pv = filter->private_data;
458 pv->ssa = ass_library_init();
459 if ( !pv->ssa ) {
460 hb_error( "decssasub: libass initialization failed\n" );
461 return 1;
464 // Redirect libass output to hb_log
465 ass_set_message_cb( pv->ssa, ssa_log, NULL );
467 // Load embedded fonts
468 hb_list_t * list_attachment = job->list_attachment;
469 int i;
470 for ( i = 0; i < hb_list_count(list_attachment); i++ )
472 hb_attachment_t * attachment = hb_list_item( list_attachment, i );
474 if ( attachment->type == FONT_TTF_ATTACH ||
475 attachment->type == FONT_OTF_ATTACH )
477 ass_add_font(
478 pv->ssa,
479 attachment->name,
480 attachment->data,
481 attachment->size );
485 ass_set_extract_fonts( pv->ssa, 1 );
486 ass_set_style_overrides( pv->ssa, NULL );
488 pv->renderer = ass_renderer_init( pv->ssa );
489 if ( !pv->renderer ) {
490 hb_log( "decssasub: renderer initialization failed\n" );
491 return 1;
494 ass_set_use_margins( pv->renderer, 0 );
495 ass_set_hinting( pv->renderer, ASS_HINTING_LIGHT ); // VLC 1.0.4 uses this
496 ass_set_font_scale( pv->renderer, 1.0 );
497 ass_set_line_spacing( pv->renderer, 1.0 );
499 // Setup default font family
501 // SSA v4.00 requires that "Arial" be the default font
502 const char *font = NULL;
503 const char *family = "Arial";
504 // NOTE: This can sometimes block for several *seconds*.
505 // It seems that process_fontdata() for some embedded fonts is slow.
506 ass_set_fonts( pv->renderer, font, family, /*haveFontConfig=*/1, NULL, 1 );
508 // Setup track state
509 pv->ssaTrack = ass_new_track( pv->ssa );
510 if ( !pv->ssaTrack ) {
511 hb_log( "decssasub: ssa track initialization failed\n" );
512 return 1;
515 int height = job->title->geometry.height - job->crop[0] - job->crop[1];
516 int width = job->title->geometry.width - job->crop[2] - job->crop[3];
517 ass_set_frame_size( pv->renderer, width, height);
519 double par = (double)job->par.num / job->par.den;
520 ass_set_aspect_ratio( pv->renderer, 1, par );
522 return 0;
525 static void ssa_close( hb_filter_object_t * filter )
527 hb_filter_private_t * pv = filter->private_data;
529 if( !pv )
531 return;
534 if ( pv->ssaTrack )
535 ass_free_track( pv->ssaTrack );
536 if ( pv->renderer )
537 ass_renderer_done( pv->renderer );
538 if ( pv->ssa )
539 ass_library_done( pv->ssa );
541 free( pv );
542 filter->private_data = NULL;
545 static int ssa_work( hb_filter_object_t * filter,
546 hb_buffer_t ** buf_in,
547 hb_buffer_t ** buf_out )
549 hb_filter_private_t * pv = filter->private_data;
550 hb_buffer_t * in = *buf_in;
551 hb_buffer_t * sub;
553 if (!pv->script_initialized)
555 // NOTE: The codec extradata is expected to be in MKV format
556 // I would like to initialize this in ssa_post_init, but when we are
557 // transcoding text subtitles to SSA, the extradata does not
558 // get initialized until the decoder is initialized. Since
559 // decoder initialization happens after filter initialization,
560 // we need to postpone this.
561 ass_process_codec_private(pv->ssaTrack,
562 (char*)filter->subtitle->extradata,
563 filter->subtitle->extradata_size);
564 pv->script_initialized = 1;
566 if (in->s.flags & HB_BUF_FLAG_EOF)
568 *buf_in = NULL;
569 *buf_out = in;
570 return HB_FILTER_DONE;
573 // Get any pending subtitles and add them to the active
574 // subtitle list
575 while( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) )
577 // Parse MKV-SSA packet
578 // SSA subtitles always have an explicit stop time, so we
579 // do not need to do special processing for stop == AV_NOPTS_VALUE
580 ass_process_chunk( pv->ssaTrack, (char*)sub->data, sub->size,
581 sub->s.start / 90,
582 (sub->s.stop - sub->s.start) / 90 );
583 hb_buffer_close(&sub);
586 ApplySSASubs( pv, in );
587 *buf_in = NULL;
588 *buf_out = in;
590 return HB_FILTER_OK;
593 static int cc608sub_post_init( hb_filter_object_t * filter, hb_job_t * job )
595 // Text subtitles for which we create a dummy ASS header need
596 // to have the header rewritten with the correct dimensions.
597 int height = job->title->geometry.height - job->crop[0] - job->crop[1];
598 int width = job->title->geometry.width - job->crop[2] - job->crop[3];
599 int safe_height = 0.8 * job->title->geometry.height;
600 // Use fixed widht font for CC
601 hb_subtitle_add_ssa_header(filter->subtitle, "Courier New",
602 .08 * safe_height, width, height);
603 return ssa_post_init(filter, job);
606 static int textsub_post_init( hb_filter_object_t * filter, hb_job_t * job )
608 // Text subtitles for which we create a dummy ASS header need
609 // to have the header rewritten with the correct dimensions.
610 int height = job->title->geometry.height - job->crop[0] - job->crop[1];
611 int width = job->title->geometry.width - job->crop[2] - job->crop[3];
612 hb_subtitle_add_ssa_header(filter->subtitle, "Arial",
613 .066 * job->title->geometry.height,
614 width, height);
615 return ssa_post_init(filter, job);
618 static void textsub_close( hb_filter_object_t * filter )
620 return ssa_close(filter);
623 static void process_sub(hb_filter_private_t *pv, hb_buffer_t *sub)
625 int64_t start, dur;
626 char *ssa, *tmp;
628 // libass expects every chunk to have a unique sequence number
629 // since we are repeating subs in some cases, we need to replace
630 // the sequence number.
631 tmp = strchr((char*)sub->data, ',');
632 if (tmp == NULL)
633 return;
635 ssa = hb_strdup_printf("%d%s", ++pv->line, tmp);
637 // Parse MKV-SSA packet
638 // SSA subtitles always have an explicit stop time, so we
639 // do not need to do special processing for stop == AV_NOPTS_VALUE
640 start = sub->s.start;
641 dur = sub->s.stop - sub->s.start;
642 ass_process_chunk(pv->ssaTrack, ssa, sub->size, start, dur);
643 free(ssa);
646 static int textsub_work(hb_filter_object_t * filter,
647 hb_buffer_t ** buf_in,
648 hb_buffer_t ** buf_out)
650 hb_filter_private_t * pv = filter->private_data;
651 hb_buffer_t * in = *buf_in;
652 hb_buffer_t * sub;
654 if (!pv->script_initialized)
656 ass_process_codec_private(pv->ssaTrack,
657 (char*)filter->subtitle->extradata,
658 filter->subtitle->extradata_size);
659 pv->script_initialized = 1;
662 if (in->s.flags & HB_BUF_FLAG_EOF)
664 *buf_in = NULL;
665 *buf_out = in;
666 return HB_FILTER_DONE;
669 int in_start_ms = in->s.start / 90;
671 // Get any pending subtitles and add them to the active
672 // subtitle list
673 while ((sub = hb_fifo_get(filter->subtitle->fifo_out)))
675 // libass expects times in ms. So to make the math easy,
676 // convert to ms immediately.
677 sub->s.start /= 90;
678 if (sub->s.stop != AV_NOPTS_VALUE)
680 sub->s.stop /= 90;
683 // Subtitle formats such as CC can have stop times
684 // that are not known until an "erase display" command
685 // is encountered in the stream. For these formats
686 // current_sub is the currently active subtitle for which
687 // we do not yet know the stop time. We do not currently
688 // support overlapping subtitles of this type.
689 if (pv->current_sub != NULL)
691 // Next sub start time tells us the stop time of the
692 // current sub when it is not known in advance.
693 pv->current_sub->s.stop = sub->s.start;
694 process_sub(pv, pv->current_sub);
695 hb_buffer_close(&pv->current_sub);
697 if (sub->s.start == sub->s.stop)
699 // Zero duration sub used to "clear" previous sub that had
700 // an unknown duration
701 hb_buffer_close(&sub);
703 else if (sub->s.stop == AV_NOPTS_VALUE)
705 // We don't know the duration of this sub. So we will
706 // apply it to every video frame until we see a "clear" sub.
707 pv->current_sub = sub;
708 pv->current_sub->s.stop = pv->current_sub->s.start;
710 else
712 // Duration of this subtitle is known, so we can just
713 // process it normally.
714 process_sub(pv, sub);
715 hb_buffer_close(&sub);
718 if (pv->current_sub != NULL && pv->current_sub->s.start <= in_start_ms)
720 // We don't know the duration of this subtitle, but we know
721 // that it started before the current video frame and that
722 // it is still active. So render it on this video frame.
723 pv->current_sub->s.start = pv->current_sub->s.stop;
724 pv->current_sub->s.stop = in_start_ms + 1;
725 process_sub(pv, pv->current_sub);
728 ApplySSASubs(pv, in);
729 *buf_in = NULL;
730 *buf_out = in;
732 return HB_FILTER_OK;
735 static void ApplyPGSSubs( hb_filter_private_t * pv, hb_buffer_t * buf )
737 int index;
738 hb_buffer_t * old_sub;
739 hb_buffer_t * sub;
741 // Each PGS subtitle supersedes anything that preceded it.
742 // Find the active subtitle (if there is one), and delete
743 // everything before it.
744 for( index = hb_list_count( pv->sub_list ) - 1; index > 0; index-- )
746 sub = hb_list_item( pv->sub_list, index);
747 if ( sub->s.start <= buf->s.start )
749 while ( index > 0 )
751 old_sub = hb_list_item( pv->sub_list, index - 1);
752 hb_list_rem( pv->sub_list, old_sub );
753 hb_buffer_close( &old_sub );
754 index--;
759 // Some PGS subtitles have no content and only serve to clear
760 // the screen. If any of these are at the front of our list,
761 // we can now get rid of them.
762 while ( hb_list_count( pv->sub_list ) > 0 )
764 sub = hb_list_item( pv->sub_list, 0 );
765 if (sub->f.width != 0 && sub->f.height != 0)
766 break;
768 hb_list_rem( pv->sub_list, sub );
769 hb_buffer_close( &sub );
772 // Check to see if there's an active subtitle, and apply it.
773 if ( hb_list_count( pv->sub_list ) > 0)
775 sub = hb_list_item( pv->sub_list, 0 );
776 if ( sub->s.start <= buf->s.start )
778 while ( sub )
780 ApplySub( pv, buf, sub );
781 sub = sub->sub;
787 static int pgssub_post_init( hb_filter_object_t * filter, hb_job_t * job )
789 hb_filter_private_t * pv = filter->private_data;
791 pv->sub_list = hb_list_init();
793 return 0;
796 static void pgssub_close( hb_filter_object_t * filter )
798 hb_filter_private_t * pv = filter->private_data;
800 if ( !pv )
802 return;
805 if ( pv->sub_list )
806 hb_list_empty( &pv->sub_list );
808 free( pv );
809 filter->private_data = NULL;
812 static int pgssub_work( hb_filter_object_t * filter,
813 hb_buffer_t ** buf_in,
814 hb_buffer_t ** buf_out)
816 hb_filter_private_t * pv = filter->private_data;
817 hb_buffer_t * in = *buf_in;
818 hb_buffer_t * sub;
820 if (in->s.flags & HB_BUF_FLAG_EOF)
822 *buf_in = NULL;
823 *buf_out = in;
824 return HB_FILTER_DONE;
827 // Get any pending subtitles and add them to the active
828 // subtitle list
829 while ( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) )
831 hb_list_add( pv->sub_list, sub );
834 ApplyPGSSubs( pv, in );
835 *buf_in = NULL;
836 *buf_out = in;
838 return HB_FILTER_OK;
841 static int hb_rendersub_init( hb_filter_object_t * filter,
842 hb_filter_init_t * init )
844 filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
845 hb_filter_private_t * pv = filter->private_data;
846 hb_subtitle_t *subtitle;
847 int ii;
849 // Find the subtitle we need
850 for( ii = 0; ii < hb_list_count(init->job->list_subtitle); ii++ )
852 subtitle = hb_list_item( init->job->list_subtitle, ii );
853 if( subtitle && subtitle->config.dest == RENDERSUB )
855 // Found it
856 filter->subtitle = subtitle;
857 pv->type = subtitle->source;
858 break;
861 if( filter->subtitle == NULL )
863 hb_log("rendersub: no subtitle marked for burn");
864 return 1;
866 return 0;
869 static int hb_rendersub_post_init( hb_filter_object_t * filter, hb_job_t *job )
871 hb_filter_private_t * pv = filter->private_data;
873 pv->crop[0] = job->crop[0];
874 pv->crop[1] = job->crop[1];
875 pv->crop[2] = job->crop[2];
876 pv->crop[3] = job->crop[3];
878 switch( pv->type )
880 case VOBSUB:
882 return vobsub_post_init( filter, job );
883 } break;
885 case SSASUB:
887 return ssa_post_init( filter, job );
888 } break;
890 case SRTSUB:
891 case UTF8SUB:
892 case TX3GSUB:
894 return textsub_post_init( filter, job );
895 } break;
897 case CC608SUB:
899 return cc608sub_post_init( filter, job );
900 } break;
902 case PGSSUB:
904 return pgssub_post_init( filter, job );
905 } break;
907 default:
909 hb_log("rendersub: unsupported subtitle format %d", pv->type );
910 return 1;
911 } break;
913 return 0;
916 static int hb_rendersub_work( hb_filter_object_t * filter,
917 hb_buffer_t ** buf_in,
918 hb_buffer_t ** buf_out )
920 hb_filter_private_t * pv = filter->private_data;
921 switch( pv->type )
923 case VOBSUB:
925 return vobsub_work( filter, buf_in, buf_out );
926 } break;
928 case SSASUB:
930 return ssa_work( filter, buf_in, buf_out );
931 } break;
933 case SRTSUB:
934 case CC608SUB:
935 case UTF8SUB:
936 case TX3GSUB:
938 return textsub_work( filter, buf_in, buf_out );
939 } break;
941 case PGSSUB:
943 return pgssub_work( filter, buf_in, buf_out );
944 } break;
946 default:
948 hb_error("rendersub: unsupported subtitle format %d", pv->type );
949 return 1;
950 } break;
954 static void hb_rendersub_close( hb_filter_object_t * filter )
956 hb_filter_private_t * pv = filter->private_data;
957 switch( pv->type )
959 case VOBSUB:
961 vobsub_close( filter );
962 } break;
964 case SSASUB:
966 ssa_close( filter );
967 } break;
969 case SRTSUB:
970 case CC608SUB:
971 case UTF8SUB:
972 case TX3GSUB:
974 textsub_close( filter );
975 } break;
977 case PGSSUB:
979 pgssub_close( filter );
980 } break;
982 default:
984 hb_error("rendersub: unsupported subtitle format %d", pv->type );
985 } break;