OSDN Git Service

Fix potential crash in libbluray
[handbrake-jp/handbrake-jp-git.git] / libhb / decssasub.c
1 /* 
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. */
5
6 /*
7  * Converts SSA subtitles to either:
8  * (1) TEXTSUB format: UTF-8 subtitles with limited HTML-style markup (<b>, <i>, <u>), or
9  * (2) PICTURESUB format, using libass.
10  * 
11  * SSA format references:
12  *   http://www.matroska.org/technical/specs/subtitles/ssa.html
13  *   http://moodub.free.fr/video/ass-specs.doc
14  *   vlc-1.0.4/modules/codec/subtitles/subsass.c:ParseSSAString
15  * 
16  * libass references:
17  *   libass-0.9.9/ass.h
18  *   vlc-1.0.4/modules/codec/libass.c
19  * 
20  * @author David Foster (davidfstr)
21  */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include "hb.h"
26
27 #include <ass/ass.h>
28
29 struct hb_work_private_s
30 {
31     // If decoding to PICTURESUB format:
32     ASS_Library *ssa;
33     ASS_Renderer *renderer;
34     ASS_Track *ssaTrack;
35     int readOrder;
36 };
37
38 typedef enum {
39     BOLD        = 0x01,
40     ITALIC      = 0x02,
41     UNDERLINE   = 0x04
42 } StyleSet;
43
44 // "<b></b>".len + "<i></i>".len + "<u></u>".len
45 #define MAX_OVERHEAD_PER_OVERRIDE (7 * 3)
46
47 #define SSA_2_HB_TIME(hr,min,sec,centi) \
48     ( 90L * ( hr    * 1000L * 60 * 60 +\
49               min   * 1000L * 60 +\
50               sec   * 1000L +\
51               centi * 10L ) )
52
53 static StyleSet ssa_parse_style_override( uint8_t *pos, StyleSet prevStyles )
54 {
55     StyleSet nextStyles = prevStyles;
56     for (;;)
57     {
58         // Skip over leading '{' or last '\\'
59         pos++;
60         
61         // Scan for next \code
62         while ( *pos != '\\' && *pos != '}' && *pos != '\0' ) pos++;
63         if ( *pos != '\\' )
64         {
65             // End of style override block
66             break;
67         }
68         
69         // If next chars are \[biu][01], interpret it
70         if ( strchr("biu", pos[1]) && strchr("01", pos[2]) )
71         {
72             StyleSet styleID =
73                 pos[1] == 'b' ? BOLD :
74                 pos[1] == 'i' ? ITALIC :
75                 pos[1] == 'u' ? UNDERLINE : 0;
76             int enabled = (pos[2] == '1');
77             
78             if (enabled)
79             {
80                 nextStyles |= styleID;
81             }
82             else
83             {
84                 nextStyles &= ~styleID;
85             }
86         }
87     }
88     return nextStyles;
89 }
90
91 static void ssa_append_html_tags_for_style_change(
92     uint8_t **dst, StyleSet prevStyles, StyleSet nextStyles )
93 {
94     #define APPEND(str) { \
95         char *src = str; \
96         while (*src) { *(*dst)++ = *src++; } \
97     }
98
99     // Reverse-order close all previous styles
100     if (prevStyles & UNDERLINE) APPEND("</u>");
101     if (prevStyles & ITALIC)    APPEND("</i>");
102     if (prevStyles & BOLD)      APPEND("</b>");
103     
104     // Forward-order open all next styles
105     if (nextStyles & BOLD)      APPEND("<b>");
106     if (nextStyles & ITALIC)    APPEND("<i>");
107     if (nextStyles & UNDERLINE) APPEND("<u>");
108     
109     #undef APPEND
110 }
111
112 static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int in_sequence );
113 static hb_buffer_t *ssa_decode_line_to_picture( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence );
114
115 /*
116  * Decodes a single SSA packet to one or more TEXTSUB or PICTURESUB subtitle packets.
117  * 
118  * SSA packet format:
119  * ( Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text CR LF ) +
120  *             1      2     3   4     5    6       7       8       9      10
121  */
122 static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in )
123 {
124     // Store NULL after the end of the buffer to make using string processing safe
125     hb_buffer_realloc( in, in->size + 1 );
126     in->data[in->size] = '\0';
127     
128     hb_buffer_t *out_list = NULL;
129     hb_buffer_t **nextPtr = &out_list;
130     
131     const char *EOL = "\r\n";
132     char *curLine, *curLine_parserData;
133     for ( curLine = strtok_r( (char *) in->data, EOL, &curLine_parserData );
134           curLine;
135           curLine = strtok_r( NULL, EOL, &curLine_parserData ) )
136     {
137         // Skip empty lines and spaces between adjacent CR and LF
138         if (curLine[0] == '\0')
139             continue;
140         
141         // Decode an individual SSA line
142         hb_buffer_t *out;
143         if ( w->subtitle->config.dest == PASSTHRUSUB ) {
144             out = ssa_decode_line_to_utf8( (uint8_t *) curLine, strlen( curLine ), in->sequence );
145             if ( out == NULL )
146                 continue;
147             
148             // We shouldn't be storing the extra NULL character,
149             // but the MP4 muxer expects this, unfortunately.
150             if ( out->size > 0 && out->data[out->size - 1] != '\0' ) {
151                 // NOTE: out->size remains unchanged
152                 hb_buffer_realloc( out, out->size + 1 );
153                 out->data[out->size] = '\0';
154             }
155             
156             // If the input packet was non-empty, do not pass through
157             // an empty output packet (even if the subtitle was empty),
158             // as this would be interpreted as an end-of-stream
159             if ( in->size > 0 && out->size == 0 ) {
160                 hb_buffer_close(&out);
161                 continue;
162             }
163         } else if ( w->subtitle->config.dest == RENDERSUB ) {
164             out = ssa_decode_line_to_picture( w, (uint8_t *) curLine, strlen( curLine ), in->sequence );
165             if ( out == NULL )
166                 continue;
167         }
168         
169         // Append 'out' to 'out_list'
170         *nextPtr = out;
171         nextPtr = &out->next;
172     }
173     
174     return out_list;
175 }
176
177 /*
178  * Parses the start and stop time from the specified SSA packet.
179  * 
180  * Returns true if parsing failed; false otherwise.
181  */
182 static int parse_timing_from_ssa_packet( char *in_data, int64_t *in_start, int64_t *in_stop )
183 {
184     /*
185      * Parse Start and End fields for timing information
186      */
187     int start_hr, start_min, start_sec, start_centi;
188     int   end_hr,   end_min,   end_sec,   end_centi;
189     int numPartsRead = sscanf( (char *) in_data, "Dialogue: %*128[^,],"
190         "%d:%d:%d.%d,"  // Start
191         "%d:%d:%d.%d,", // End
192         &start_hr, &start_min, &start_sec, &start_centi,
193           &end_hr,   &end_min,   &end_sec,   &end_centi );
194     if ( numPartsRead != 8 )
195         return 1;
196     
197     *in_start = SSA_2_HB_TIME(start_hr, start_min, start_sec, start_centi);
198     *in_stop  = SSA_2_HB_TIME(  end_hr,   end_min,   end_sec,   end_centi);
199     
200     return 0;
201 }
202
203 static uint8_t *find_field( uint8_t *pos, uint8_t *end, int fieldNum )
204 {
205     int curFieldID = 1;
206     while (pos < end)
207     {
208         if ( *pos++ == ',' )
209         {
210             curFieldID++;
211             if ( curFieldID == fieldNum )
212                 return pos;
213         }
214     }
215     return NULL;
216 }
217
218 /*
219  * SSA line format:
220  *   Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
221  *             1      2     3   4     5    6       7       8       9      10
222  */
223 static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int in_sequence )
224 {
225     uint8_t *pos = in_data;
226     uint8_t *end = in_data + in_size;
227     
228     // Parse values for in->start and in->stop
229     int64_t in_start, in_stop;
230     if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) )
231         goto fail;
232     
233     uint8_t *textFieldPos = find_field( pos, end, 10 );
234     if ( textFieldPos == NULL )
235         goto fail;
236     
237     // Count the number of style overrides in the Text field
238     int numStyleOverrides = 0;
239     pos = textFieldPos;
240     while ( pos < end )
241     {
242         if (*pos++ == '{')
243         {
244             numStyleOverrides++;
245         }
246     }
247     
248     int maxOutputSize = (end - textFieldPos) + ((numStyleOverrides + 1) * MAX_OVERHEAD_PER_OVERRIDE);
249     hb_buffer_t *out = hb_buffer_init( maxOutputSize );
250     if ( out == NULL )
251         return NULL;
252     
253     /*
254      * The Text field contains plain text marked up with:
255      * (1) '\n' -> space
256      * (2) '\N' -> newline
257      * (3) curly-brace control codes like '{\k44}' -> HTML tags / strip
258      * 
259      * Perform the above conversions and copy it to the output packet
260      */
261     StyleSet prevStyles = 0;
262     uint8_t *dst = out->data;
263     pos = textFieldPos;
264     while ( pos < end )
265     {
266         if ( pos[0] == '\\' && pos[1] == 'n' )
267         {
268             *dst++ = ' ';
269             pos += 2;
270         }
271         else if ( pos[0] == '\\' && pos[1] == 'N' )
272         {
273             *dst++ = '\n';
274             pos += 2;
275         }
276         else if ( pos[0] == '{' )
277         {
278             // Parse SSA style overrides and append appropriate HTML style tags
279             StyleSet nextStyles = ssa_parse_style_override( pos, prevStyles );
280             ssa_append_html_tags_for_style_change( &dst, prevStyles, nextStyles );
281             prevStyles = nextStyles;
282             
283             // Skip past SSA control code
284             while ( pos < end && *pos != '}' ) pos++;
285             if    ( pos < end && *pos == '}' ) pos++;
286         }
287         else
288         {
289             // Copy raw character
290             *dst++ = *pos++;
291         }
292     }
293     
294     // Append closing HTML style tags
295     ssa_append_html_tags_for_style_change( &dst, prevStyles, 0 );
296     
297     // Trim output buffer to the actual amount of data written
298     out->size = dst - out->data;
299     
300     // Copy metadata from the input packet to the output packet
301     out->start = in_start;
302     out->stop = in_stop;
303     out->sequence = in_sequence;
304     
305     return out;
306     
307 fail:
308     hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", in_size, in_data );
309     return NULL;
310 }
311
312 /*
313  * SSA line format:
314  *   Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
315  *             1      2     3   4     5    6       7       8       9      10
316  * 
317  * MKV-SSA packet format:
318  *   ReadOrder,Marked,          Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
319  *   1         2                3     4    5       6       7       8      9
320  */
321 static hb_buffer_t *ssa_decode_line_to_picture( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence )
322 {
323     hb_work_private_t * pv = w->private_data;
324     
325     // Parse values for in->start and in->stop
326     int64_t in_start, in_stop;
327     if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) )
328         goto fail;
329     
330     // Convert the SSA packet to MKV-SSA format, which is what libass expects
331     char *mkvIn;
332     int mkvInSize;
333     {
334         char *layerField = malloc( in_size );
335         int numPartsRead = sscanf( (char *) in_data, "Dialogue: %128[^,],", layerField );
336         if ( numPartsRead != 1 )
337             goto fail;
338         
339         char *styleToTextFields = (char *) find_field( in_data, in_data + in_size, 4 );
340         if ( styleToTextFields == NULL ) {
341             free( layerField );
342             goto fail;
343         }
344         
345         mkvIn = malloc( in_size + 1 );
346         mkvIn[0] = '\0';
347         sprintf(mkvIn, "%d", pv->readOrder++);    // ReadOrder: make this up
348         strcat( mkvIn, "," );
349         strcat( mkvIn, layerField );
350         strcat( mkvIn, "," );
351         strcat( mkvIn, (char *) styleToTextFields );
352         
353         mkvInSize = strlen(mkvIn);
354         
355         free( layerField );
356     }
357     
358     // Parse MKV-SSA packet
359     ass_process_chunk( pv->ssaTrack, mkvIn, mkvInSize, in_start / 90, (in_stop - in_start) / 90 );
360     
361     free( mkvIn );
362     
363     // TODO: To support things like karaoke, it won't be sufficient to only generate
364     //       new subtitle pictures when there are subtitle packets. Rather, pictures will
365     //       need to be generated potentially continuously. 
366     //       
367     //       Until "karaoke support" is implemented, make an educated guess about the
368     //       timepoint within the subtitle that should be rendered. I guess the midpoint.
369     int64_t renderTime = ( in_start + in_stop ) / 2; 
370     
371     int changed;
372     ASS_Image *frameList = ass_render_frame( pv->renderer, pv->ssaTrack, renderTime / 90, &changed );
373     if ( !changed || !frameList )
374         return NULL;
375     
376     int numFrames = 0;
377     ASS_Image *curFrame;
378     for (curFrame = frameList; curFrame; curFrame = curFrame->next)
379         numFrames++;
380     
381     hb_buffer_t *outSubpictureList = NULL;
382     hb_buffer_t **outSubpictureListTailPtr = &outSubpictureList;
383     
384     // Generate a PICTURESUB packet from the frames
385     ASS_Image *frame;
386     for (frame = frameList; frame; frame = frame->next) {
387         // Allocate pixmap where drawing will be done
388         uint8_t *rgba = calloc(frame->w * frame->h * 4, 1);
389         
390         unsigned r = (frame->color >> 24) & 0xff;
391         unsigned g = (frame->color >> 16) & 0xff;
392         unsigned b = (frame->color >>  8) & 0xff;
393         unsigned a = (frame->color      ) & 0xff;
394         
395         int x, y;
396         for (y = 0; y < frame->h; y++) {
397             for (x = 0; x < frame->w; x++) {
398                 unsigned srcAlphaPrenormalized = frame->bitmap[y*frame->stride + x];
399                 unsigned srcAlpha = (255 - a) * srcAlphaPrenormalized / 255;
400                 
401                 uint8_t *dst = &rgba[(y*frame->w + x) * 4];
402                 unsigned oldDstAlpha = dst[3];
403                 
404                 if (oldDstAlpha == 0) {
405                     // Optimized version
406                     dst[0] = r;
407                     dst[1] = g;
408                     dst[2] = b;
409                     dst[3] = srcAlpha;
410                 } else {
411                     dst[3] = 255 - ( 255 - dst[3] ) * ( 255 - srcAlpha ) / 255;
412                     if (dst[3] != 0) {
413                         dst[0] = ( dst[0] * oldDstAlpha * (255-srcAlpha) / 255 + r * srcAlpha ) / dst[3];
414                         dst[1] = ( dst[1] * oldDstAlpha * (255-srcAlpha) / 255 + g * srcAlpha ) / dst[3];
415                         dst[2] = ( dst[2] * oldDstAlpha * (255-srcAlpha) / 255 + b * srcAlpha ) / dst[3];
416                     }
417                 }
418             }
419         }
420         
421         // Generate output subpicture (in PICTURESUB format)
422         hb_buffer_t *out = hb_buffer_init(frame->w * frame->h * 4);
423         out->x = frame->dst_x;
424         out->y = frame->dst_y;
425         out->width = frame->w;
426         out->height = frame->h;
427         
428         int i;
429         int numPixels = frame->w * frame->h;
430         for (i = 0; i < numPixels; i++) {
431             uint8_t *srcRgba = &rgba[i * 4];
432             
433             uint8_t *dstY = &out->data[(numPixels * 0) + i];
434             uint8_t *dstA = &out->data[(numPixels * 1) + i];
435             uint8_t *dstU = &out->data[(numPixels * 2) + i];
436             uint8_t *dstV = &out->data[(numPixels * 3) + i];
437             
438             int srcYuv = hb_rgb2yuv((srcRgba[0] << 16) | (srcRgba[1] << 8) | (srcRgba[2] << 0));
439             int srcA = srcRgba[3];
440             
441             *dstY = (srcYuv >> 16) & 0xff;
442             *dstU = (srcYuv >> 8 ) & 0xff;
443             *dstV = (srcYuv >> 0 ) & 0xff;
444             *dstA = srcA / 16;  // HB's max alpha value is 16
445         }
446         
447         free(rgba);
448         
449         *outSubpictureListTailPtr = out;
450         outSubpictureListTailPtr = &out->next_subpicture;
451     }
452     
453     // NOTE: The subpicture list is actually considered a single packet by most other code
454     hb_buffer_t *out = outSubpictureList;
455     
456     // Copy metadata from the input packet to the output packet
457     out->start = in_start;
458     out->stop = in_stop;
459     out->sequence = in_sequence;
460
461     return out;
462     
463 fail:
464     hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", in_size, in_data );
465     return NULL;
466 }
467
468 static void ssa_log(int level, const char *fmt, va_list args, void *data)
469 {
470     if ( level < 5 )      // same as default verbosity when no callback is set
471     {
472         char *msg;
473         if ( vasprintf( &msg, fmt, args ) < 0 )
474         {
475             hb_log( "decssasub: could not report libass message\n" );
476             return;
477         }
478         hb_log( "[ass] %s", msg );  // no need for extra '\n' because libass sends it
479         
480         free( msg );
481     }
482 }
483
484 static int decssaInit( hb_work_object_t * w, hb_job_t * job )
485 {
486     hb_work_private_t * pv;
487
488     pv              = calloc( 1, sizeof( hb_work_private_t ) );
489     w->private_data = pv;
490     
491     if ( w->subtitle->config.dest == RENDERSUB ) {
492         pv->ssa = ass_library_init();
493         if ( !pv->ssa ) {
494             hb_log( "decssasub: libass initialization failed\n" );
495             return 1;
496         }
497         
498         // Redirect libass output to hb_log
499         ass_set_message_cb( pv->ssa, ssa_log, NULL );
500         
501         // Load embedded fonts
502         hb_list_t * list_attachment = job->title->list_attachment;
503         int i;
504         for ( i = 0; i < hb_list_count(list_attachment); i++ )
505         {
506             hb_attachment_t * attachment = hb_list_item( list_attachment, i );
507             
508             if ( attachment->type == FONT_TTF_ATTACH )
509             {
510                 ass_add_font(
511                     pv->ssa,
512                     attachment->name,
513                     attachment->data,
514                     attachment->size );
515             }
516         }
517         
518         ass_set_extract_fonts( pv->ssa, 1 );
519         ass_set_style_overrides( pv->ssa, NULL );
520         
521         pv->renderer = ass_renderer_init( pv->ssa );
522         if ( !pv->renderer ) {
523             hb_log( "decssasub: renderer initialization failed\n" );
524             return 1;
525         }
526         
527         ass_set_use_margins( pv->renderer, 0 );
528         ass_set_hinting( pv->renderer, ASS_HINTING_LIGHT );     // VLC 1.0.4 uses this
529         ass_set_font_scale( pv->renderer, 1.0 );
530         ass_set_line_spacing( pv->renderer, 1.0 );
531         
532         // Setup default font family
533         // 
534         // SSA v4.00 requires that "Arial" be the default font
535         const char *font = NULL;
536         const char *family = "Arial";
537         // NOTE: This can sometimes block for several *seconds*.
538         //       It seems that process_fontdata() for some embedded fonts is slow.
539         ass_set_fonts( pv->renderer, font, family, /*haveFontConfig=*/1, NULL, 1 );
540         
541         // Setup track state
542         pv->ssaTrack = ass_new_track( pv->ssa );
543         if ( !pv->ssaTrack ) {
544             hb_log( "decssasub: ssa track initialization failed\n" );
545             return 1;
546         }
547         
548         // NOTE: The codec extradata is expected to be in MKV format
549         ass_process_codec_private( pv->ssaTrack,
550             (char *) w->subtitle->extradata, w->subtitle->extradata_size );
551         
552         int originalWidth = job->title->width;
553         int originalHeight = job->title->height;
554         ass_set_frame_size( pv->renderer, originalWidth, originalHeight);
555         ass_set_aspect_ratio( pv->renderer, /*dar=*/1.0, /*sar=*/1.0 );
556     }
557     
558     return 0;
559 }
560
561 static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
562                         hb_buffer_t ** buf_out )
563 {
564     hb_buffer_t * in = *buf_in;
565     hb_buffer_t * out_list = NULL;
566     
567     if ( in->size > 0 ) {
568         out_list = ssa_decode_packet(w, in);
569     } else {
570         out_list = hb_buffer_init( 0 );
571     }
572     
573     // Dispose the input packet, as it is no longer needed
574     hb_buffer_close(&in);
575     
576     *buf_in = NULL;
577     *buf_out = out_list;
578     return HB_WORK_OK;
579 }
580
581 static void decssaClose( hb_work_object_t * w )
582 {
583     hb_work_private_t * pv = w->private_data;
584
585     if ( pv->ssaTrack )
586         ass_free_track( pv->ssaTrack );
587     if ( pv->renderer )
588         ass_renderer_done( pv->renderer );
589     if ( pv->ssa )
590         ass_library_done( pv->ssa );
591     
592     free( w->private_data );
593 }
594
595 hb_work_object_t hb_decssasub =
596 {
597     WORK_DECSSASUB,
598     "SSA Subtitle Decoder",
599     decssaInit,
600     decssaWork,
601     decssaClose
602 };