1 /*
2 * filter_extsub.c
3 *
4 * Copyright (C) Thomas Oestreich - January 2002
5 *
6 * This file is part of transcode, a video stream processing tool
7 *
8 * transcode is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * transcode is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Make; see the file COPYING. If not, write to
20 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23
24 #define MOD_NAME "filter_extsub.so"
25 #define MOD_VERSION "0.3.5 (2003-10-15)"
26 #define MOD_CAP "DVD subtitle overlay plugin"
27 #define MOD_AUTHOR "Thomas Oestreich"
28
29 #include "transcode.h"
30 #include "encoder.h"
31 #include "filter.h"
32 #include "libtc/optstr.h"
33 #include "libtcvideo/tcvideo.h"
34
35 #include <stdint.h>
36
37 #include "dl_loader.h"
38 #include "import/magic.h"
39
40 #include "subtitle_buffer.h"
41 #include "subproc.h"
42
43 #define BUFFER_SIZE SIZE_RGB_FRAME
44 #define SUBTITLE_BUFFER 100
45
46 static transfer_t import_para;
47
48 static pthread_t thread1;
49
50 static double f_pts, f_time;
51
52 //infos on activated subtitle
53 static char *sub_frame;
54 static char *vid_frame;
55 static char *tmp_frame;
56
57 static double sub_pts1=-1.0f, sub_pts2=-1.0f;
58 static int sub_xpos, sub_ypos;
59 static int sub_xlen, sub_ylen;
60 static int sub_id=0;
61 static int sub_forced=0;
62 static int sub_colour[4];
63 static int sub_alpha[4];
64
65 static int codec;
66 static int vshift=0, tshift=0, post=0;
67
68 static unsigned int color1=0, color2=255;
69 static int forced=0;
70
71 static double aa_weight, aa_bias;
72
73 static TCVHandle tcvhandle = 0;
74
75
76 //-------------------------------------------------------------------
77 //
78 // retrieve a valid subtitle
79 //
80 //-------------------------------------------------------------------
81
subtitle_retrieve(void)82 static int subtitle_retrieve(void)
83 {
84
85 sframe_list_t *sptr = NULL;
86 sub_info_t sub;
87 int n;
88
89 // get a subtitle from buffer
90 pthread_mutex_lock(&sframe_list_lock);
91
92 if(sframe_fill_level(TC_BUFFER_EMPTY)) {
93 pthread_mutex_unlock(&sframe_list_lock);
94 return(-1);
95 }
96
97 //not beeing empty does not mean ready to go!!!!!
98
99 if(sframe_fill_level(TC_BUFFER_READY)) {
100
101 pthread_mutex_unlock(&sframe_list_lock);
102
103 if((sptr = sframe_retrieve())==NULL) {
104 //this shouldn't happen
105 tc_log_error(MOD_NAME, "internal error (S)");
106 return(-1);
107 }
108 } else {
109 pthread_mutex_unlock(&sframe_list_lock);
110 return(-1);
111 }
112
113 //room for title pixels
114 sub.frame = sub_frame;
115
116 // conversion
117 if(subproc_feedme(sptr->video_buf, sptr->video_size, sptr->id, sptr->pts, &sub)<0) {
118 // problems, drop this subtitle
119 if(verbose & TC_DEBUG) tc_log_warn(MOD_NAME, "subtitle dropped");
120 sframe_remove(sptr);
121 pthread_cond_signal(&sframe_list_full_cv);
122 return(-1);
123 }
124
125 //save data
126
127 sub_id = sptr->id;
128 sub_pts1 = sptr->pts * f_time;
129 sub_pts2 = sub_pts1 + sub.time/100.0;
130
131 sub_forced = sub.forced;
132 sub_xpos = sub.x;
133 sub_ypos = sub.y;
134 sub_xlen = sub.w;
135 sub_ylen = sub.h;
136
137 for(n=0; n<4;++n) sub_alpha[n] = sub.alpha[n];
138
139 //release packet buffer
140 sframe_remove(sptr);
141 pthread_cond_signal(&sframe_list_full_cv);
142
143 if(verbose & TC_STATS)
144 tc_log_info(MOD_NAME, "got SUBTITLE %d with forced=%d, pts=%.3f dtime=%.3f",
145 sub_id, sub_forced, sub_pts1, sub_pts2-sub_pts1);
146 return(0);
147 }
148
149 //-------------------------------------------------------------------
150 //
151 // assign colors to subtitle colors 0-4
152 //
153 //-------------------------------------------------------------------
154
155 static int color_set_done=0;
156 static int anti_alias_done=0;
157 static int skip_anti_alias=0;
158
159 static unsigned int ca=2, cb=3;
160
get_subtitle_colors(void)161 static void get_subtitle_colors(void) {
162
163 int y;
164
165 for(y=0; y<sub_ylen*sub_xlen; ++y) ++sub_colour[(uint8_t) sub_frame[y]];
166
167 if(sub_colour[0] || sub_colour[1] || sub_colour[2] || sub_colour[3]) {
168
169 if(sub_colour[1]>sub_colour[2] && sub_colour[1]>sub_colour[3]) {
170 ca = 1;
171 cb = (sub_colour[2]>sub_colour[3]) ? 2:3;
172 }
173
174 if(sub_colour[2]>sub_colour[1] && sub_colour[2]>sub_colour[3]) {
175 ca = 2;
176 cb = (sub_colour[1]>sub_colour[3]) ? 1:3;
177 }
178
179 if(sub_colour[3]>sub_colour[1] && sub_colour[3]>sub_colour[2]) {
180 ca = 3;
181 cb = (sub_colour[1]>sub_colour[2]) ? 1:2;
182 }
183 }
184 color_set_done=1;
185
186 if(verbose & TC_DEBUG) {
187 tc_log_info(MOD_NAME, "color dis: 0=%d, 1=%d, 2=%d, 3=%d, ca=%d, cb=%d",
188 sub_colour[0], sub_colour[1], sub_colour[2], sub_colour[3],
189 ca, cb);
190 tc_log_info(MOD_NAME, "alpha dis: 0=%d, 1=%d, 2=%d, 3=%d, ca=%d, cb=%d",
191 sub_alpha[0], sub_alpha[1], sub_alpha[2], sub_alpha[3],
192 ca, cb);
193 }
194
195 }
196
197 //-------------------------------------------------------------------
198 //
199 // anti-alias subtitle bitmap
200 //
201 //-------------------------------------------------------------------
202
anti_alias_subtitle(int black)203 static void anti_alias_subtitle(int black) {
204
205 int back_col=black;
206 int n;
207
208 if(color1<=black) color1=black+1;
209 if(color2<=black) color2=black+1;
210
211 for(n=0; n<sub_ylen*sub_xlen; ++n) {
212
213 if(sub_frame[n] == ca) {
214 sub_frame[n] = color1 & 0xff;
215 back_col=black;
216 goto next_pix;
217 }
218
219 if(sub_frame[n] == cb) {
220 sub_frame[n] = color2 & 0xff;
221 back_col=255;
222 goto next_pix;
223 }
224
225 if(back_col==255) {
226 sub_frame[n] = 255 & 0xff;
227 } else sub_frame[n]=black;
228
229 next_pix:
230 continue;
231 }
232
233 if(!skip_anti_alias) {
234 tcv_antialias(tcvhandle, sub_frame, tmp_frame, sub_xlen, sub_ylen, 1,
235 aa_weight, aa_bias);
236 ac_memcpy(sub_frame, tmp_frame, sub_xlen * sub_ylen);
237 }
238
239 anti_alias_done=1;
240
241 }
242
243 //-------------------------------------------------------------------
244 //
245 // YUV overlay
246 //
247 //-------------------------------------------------------------------
248
subtitle_overlay_yuv(char * vid_frame,int w,int h)249 static void subtitle_overlay_yuv(char *vid_frame, int w, int h)
250 {
251
252 int x, y, n, m;
253
254 int eff_sub_ylen, off;
255
256 if(verbose & TC_STATS)
257 tc_log_info(MOD_NAME, "SUBTITLE id=%d, x=%d, y=%d, w=%d, h=%d, t=%f",
258 sub_id, sub_xpos, sub_ypos, sub_xlen, sub_ylen,
259 sub_pts2-sub_pts1);
260
261 if(color_set_done==0) get_subtitle_colors();
262
263 //check:
264 eff_sub_ylen = (sub_ylen+vshift>h) ? h-vshift:sub_ylen;
265
266 off = (vshift>0) ? vshift:0;
267
268 if(eff_sub_ylen<0 || off>eff_sub_ylen) {
269 tc_log_info(MOD_NAME, "invalid subtitle shift parameter");
270 return;
271 }
272
273 if(!anti_alias_done) anti_alias_subtitle(16);
274
275 //black the frame
276 if(0) {
277 for(y=0; y<w*h; ++y) vid_frame[y]=255;
278 for(y=0; y<w*h/4; ++y) vid_frame[w*h+y]=80;
279 for(y=0; y<w*h/4; ++y) vid_frame[w*h*5/4+y]=80;
280 }
281
282 n=0;
283
284 for(y=0; y<eff_sub_ylen-off; ++y) {
285
286 m = sub_xpos + (y+h-eff_sub_ylen)*w+vshift*w;
287
288 for(x=0; x<sub_xlen; ++x) {
289
290 if(sub_frame[n] != 16) {
291 //Y
292 vid_frame[m] = sub_frame[n] & 0xff;
293 if(0) {
294 //U
295 if(x%2==0 && y%2==0)
296 vid_frame[w*h + x/2+ sub_xpos/2 + (y+h-eff_sub_ylen-vshift)*w/4] = 0x80 & 0xff;
297 //V
298 if(x%2==0 && y%2==0)
299 vid_frame[(w*h*5)/4 + x/2 + sub_xpos/2 + (y+h-eff_sub_ylen-vshift)*w/4] = 0x80 & 0xff;
300 }
301 }
302 //16=transparent
303 ++m;
304 ++n;
305 } // progress columns
306 } //progress rows
307 }
308
309 //-------------------------------------------------------------------
310 //
311 // RGB overlay
312 //
313 //-------------------------------------------------------------------
314
subtitle_overlay_rgb(char * vid_frame,int w,int h)315 static void subtitle_overlay_rgb(char *vid_frame, int w, int h)
316 {
317
318 int x, y, n, m;
319
320 int eff_sub_ylen, off;
321
322 if(verbose & TC_STATS)
323 tc_log_info(MOD_NAME, "SUBTITLE id=%d, x=%d, y=%d, w=%d, h=%d, t=%f",
324 sub_id, sub_xpos, sub_ypos, sub_xlen, sub_ylen,
325 sub_pts2-sub_pts1);
326
327 if(color_set_done==0) get_subtitle_colors();
328
329 n=0;
330
331 //check:
332 eff_sub_ylen = (sub_ylen+vshift>h) ? h-vshift:sub_ylen;
333 eff_sub_ylen = sub_ylen;
334
335 off = (vshift<0) ? -vshift:0;
336
337 if(eff_sub_ylen<0 || off>eff_sub_ylen) {
338 tc_log_warn(MOD_NAME, "invalid subtitle shift parameter");
339 return;
340 }
341
342 if(!anti_alias_done) anti_alias_subtitle(0);
343
344 for(y=0; y<eff_sub_ylen-off; ++y) {
345
346 m = sub_xpos*3 + (eff_sub_ylen-y+vshift+(off?0:vshift))*w*3;
347
348 for(x=0; x<sub_xlen; ++x) {
349
350 if(sub_frame[n] !=0) {
351 vid_frame[m++]= sub_frame[n] & 0xff;
352 vid_frame[m++]= sub_frame[n] & 0xff;
353 vid_frame[m++]= sub_frame[n] & 0xff;
354 } else {
355 m=m+3;
356 }
357 ++n;
358 }
359 }
360 }
361
362 //-------------------------------------------------------------------
363 //
364 // subtitle overlay wrapper
365 //
366 //-------------------------------------------------------------------
367
subtitle_overlay(char * vid_frame,int w,int h)368 static void subtitle_overlay(char *vid_frame, int w, int h)
369 {
370 if(codec == CODEC_RGB) subtitle_overlay_rgb(vid_frame, w, h);
371 if(codec == CODEC_YUV) subtitle_overlay_yuv(vid_frame, w, h);
372 }
373
374 /*-------------------------------------------------
375 *
376 * single function interface
377 *
378 *-------------------------------------------------*/
379
is_optstr(char * buf)380 static int is_optstr (char *buf) {
381 if (strchr(buf, 'h')) return 1;
382 if (strchr(buf, '='))
383 return 1;
384 return 0;
385 }
386
tc_filter(frame_list_t * ptr_,char * options)387 int tc_filter(frame_list_t *ptr_, char *options)
388 {
389 vframe_list_t *ptr = (vframe_list_t *)ptr_;
390 static vob_t *vob=NULL;
391 int pre=0, vid=0;
392 int subtitles=0;
393 int n=0;
394
395 if (ptr->tag & TC_FILTER_GET_CONFIG) {
396 optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYOE", "1");
397 optstr_param (options, "track", "Subtitle track to render", "%d", "0", "0", "255");
398 optstr_param (options, "forced", "Render only forced subtitles", "%d", "0", "0", "1" );
399 optstr_param (options, "vertshift", "offset of subtitle with respect to bottom of frame in rows", "%d", "0", "0", "height");
400 optstr_param (options, "timeshift", "global display start time correction in msec", "%d", "0", "0", "-1");
401 optstr_param (options, "antialias", "anti-aliasing the rendered text (0=off,1=on)", "%d", "1", "0", "1");
402 optstr_param (options, "pre", "Run as a pre filter", "%d", "1", "0", "1");
403 optstr_param (options, "color1", "Make a subtitle color visible with given intensity", "%d", "0", "0", "255");
404 optstr_param (options, "color2", "Make a subtitle color visible with given intensity", "%d", "0", "0", "255");
405 optstr_param (options, "ca", "Shuffle the color assignment by choosing another subtitle color", "%d", "0", "0", "3");
406 optstr_param (options, "cb", "Shuffle the color assignment by choosing another subtitle color", "%d", "0", "0", "3");
407
408 return 0;
409 }
410
411 //----------------------------------
412 //
413 // filter init
414 //
415 //----------------------------------
416
417 if(ptr->tag & TC_FILTER_INIT) {
418
419 if((vob = tc_get_vob())==NULL) return(-1);
420
421 // filter init ok.
422
423 if(verbose) tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
424
425 if(verbose) tc_log_info(MOD_NAME, "options=%s", options);
426
427 //------------------------------------------------------------
428
429 if(options != NULL) {
430 if (!is_optstr(options)) {
431 n=sscanf(options,"%d:%d:%d:%d:%d:%d:%d:%d:%d", &vob->s_track, &vshift, &tshift, &skip_anti_alias, &post, &color1, &color2, &ca, &cb);
432 } else { // new options
433 optstr_get (options, "track", "%d", &vob->s_track);
434 optstr_get (options, "forced", "%d", &forced);
435 optstr_get (options, "vertshift", "%d", &vshift);
436 optstr_get (options, "timeshift", "%d", &tshift);
437 if (optstr_get (options, "antialias", "%d", &skip_anti_alias)>=0) skip_anti_alias = !skip_anti_alias;
438 if (optstr_get (options, "pre", "%d", &post)>=0) post = !post;
439 optstr_get (options, "color1", "%d", &color1);
440 optstr_get (options, "color2", "%d", &color2);
441 if (optstr_get (options, "ca", "%d", &ca)>=0) n = 9;
442 if (optstr_get (options, "cb", "%d", &cb)>=0) n = 9;
443 if (optstr_lookup (options, "help")) return (-1);
444 }
445 }
446 if (!skip_anti_alias) {
447 if (!(tcvhandle = tcv_init())) {
448 tc_log_error(MOD_NAME, "antialiasing initialization failed");
449 return TC_EXPORT_ERROR;
450 }
451 }
452
453 if (vob->im_v_codec == CODEC_YUV)
454 vshift = -vshift;
455
456 if(n>8) color_set_done=1;
457
458 if(verbose) tc_log_info(MOD_NAME, "extracting subtitle 0x%x", vob->s_track+0x20);
459
460 // start subtitle stream
461 import_para.flag=TC_SUBEX;
462
463 if(tcv_import(TC_IMPORT_OPEN, &import_para, vob)<0) {
464 tc_log_error(MOD_NAME, "popen subtitle stream");
465 }
466
467 //------------------------------------------------------------
468
469 subproc_init(NULL, "title", subtitles, (unsigned short)vob->s_track);
470
471 // buffer allocation:
472
473 sframe_alloc(SUBTITLE_BUFFER, import_para.fd);
474
475 // start thread
476 if(pthread_create(&thread1, NULL, (void *) subtitle_reader, NULL)!=0)
477 tc_log_error(MOD_NAME, "failed to start subtitle import thread");
478
479 // misc:
480
481 if (post) {
482 f_time = 1./vob->ex_fps;
483 } else {
484 f_time = 1./vob->fps;
485 }
486
487 codec = vob->im_v_codec;
488
489 if ((sub_frame = malloc(BUFFER_SIZE))==NULL) {
490 tc_log_perror(MOD_NAME, "out of memory");
491 return(TC_EXPORT_ERROR);
492 } else
493 memset(sub_frame, 0, BUFFER_SIZE);
494
495 if ((vid_frame = malloc(BUFFER_SIZE))==NULL) {
496 tc_log_perror(MOD_NAME, "out of memory");
497 return(TC_EXPORT_ERROR);
498 } else
499 memset(vid_frame, 0, BUFFER_SIZE);
500
501 if ((tmp_frame = malloc(BUFFER_SIZE))==NULL) {
502 tc_log_perror(MOD_NAME, "out of memory");
503 return(TC_EXPORT_ERROR);
504 } else
505 memset(tmp_frame, 0, BUFFER_SIZE);
506
507 aa_weight = vob->aa_weight;
508 aa_bias = vob->aa_bias;
509
510 return(0);
511 }
512
513 //----------------------------------
514 //
515 // filter close
516 //
517 //----------------------------------
518
519 if(ptr->tag & TC_FILTER_CLOSE) {
520
521 void * status;
522
523 pthread_cancel(thread1);
524 #ifdef BROKEN_PTHREADS // Used to be MacOSX specific; kernel 2.6 as well?
525 pthread_cond_signal(&sframe_list_full_cv);
526 #endif
527 pthread_join(thread1, &status);
528
529 import_para.flag=TC_SUBEX;
530
531 if(import_para.fd!=NULL) pclose(import_para.fd);
532
533 import_para.fd=NULL;
534
535 tcv_free(tcvhandle);
536 tcvhandle = 0;
537
538 //FIXME: module already removed by main process
539
540 // if(tca_import(TC_IMPORT_CLOSE, &import_para, vob)<0) {
541 // tc_log_error(MOD_NAME, "pclose subtitle stream");
542 //}
543
544 if(vid_frame) free(vid_frame);
545 if(sub_frame) free(sub_frame);
546
547 return(0);
548 }
549
550 //----------------------------------
551 //
552 // filter frame routine
553 //
554 //----------------------------------
555
556 // tag variable indicates, if we are called before
557 // transcodes internal video/audo frame processing routines
558 // or after and determines video/audio context
559
560 if(verbose & TC_STATS)
561 tc_log_info(MOD_NAME, "%s/%s %s %s",
562 vob->mod_path, MOD_NAME, MOD_VERSION, MOD_CAP);
563
564 if(post) {
565 pre = (ptr->tag & TC_POST_S_PROCESS)? 1:0;
566 } else {
567 pre = (ptr->tag & TC_PRE_S_PROCESS)? 1:0;
568 }
569 vid = (ptr->tag & TC_VIDEO)? 1:0;
570
571 if(pre==0 || vid==0) return(0);
572
573 //-------------------------------------------------------------------------
574 //
575 // below is a very fuzzy concept of rendering the subtitle on the movie
576 //
577 // (1) check if we have a valid subtitle and render it
578 // (2) if (1) fails try to get a new one
579 // (3) buffer and display the new one if it's showtime, if not return
580 //
581 // repeat steps throughout the movie
582
583 // calculate current frame video PTS in [s]
584 // adjust for dropped frames so far
585 // adjust for user shift (in milliseconds)
586
587 f_pts = f_time*(ptr->id-tc_get_frames_dropped()+vob->psu_offset) + tshift/1000.;
588
589 if(verbose & TC_DEBUG)
590 tc_log_info(MOD_NAME, "frame=%06d pts=%.3f sub1=%.3f sub2=%.3f",
591 ptr->id, f_pts, sub_pts1, sub_pts2);
592
593 //overlay now?
594 if(sub_pts1 <= f_pts && f_pts <= sub_pts2) {
595 if(!forced || sub_forced)
596 subtitle_overlay(ptr->video_buf, ptr->v_width, ptr->v_height);
597 return(0);
598 }
599
600 //get a new subtitle, if the last one has expired:
601
602 //reset anti-alias info
603 anti_alias_done=0;
604
605 if(f_pts > sub_pts2) {
606 if(subtitle_retrieve()<0) {
607 if(verbose & TC_STATS)
608 tc_log_info(MOD_NAME, "no subtitle available at this time");
609 return(0);
610 }
611 }
612
613 //overlay now?
614 if(sub_pts1 < f_pts && f_pts < sub_pts2)
615 if(!forced || sub_forced)
616 subtitle_overlay(ptr->video_buf, ptr->v_width, ptr->v_height);
617 return(0);
618 }
619