diff options
Diffstat (limited to 'libhb/decssasub.c')
-rw-r--r-- | libhb/decssasub.c | 341 |
1 files changed, 56 insertions, 285 deletions
diff --git a/libhb/decssasub.c b/libhb/decssasub.c index 9e1c36b47..0a053e9fe 100644 --- a/libhb/decssasub.c +++ b/libhb/decssasub.c @@ -28,9 +28,6 @@ struct hb_work_private_s { // If decoding to PICTURESUB format: - ASS_Library *ssa; - ASS_Renderer *renderer; - ASS_Track *ssaTrack; int readOrder; hb_job_t *job; @@ -113,7 +110,7 @@ static void ssa_append_html_tags_for_style_change( } static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int in_sequence ); -static hb_buffer_t *ssa_decode_line_to_picture( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence ); +static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence ); /* * Decodes a single SSA packet to one or more TEXTSUB or PICTURESUB subtitle packets. @@ -164,7 +161,7 @@ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in ) continue; } } else if ( w->subtitle->config.dest == RENDERSUB ) { - out = ssa_decode_line_to_picture( w, (uint8_t *) curLine, strlen( curLine ), in->sequence ); + out = ssa_decode_line_to_mkv_ssa( w, (uint8_t *) curLine, strlen( curLine ), in->sequence ); if ( out == NULL ) continue; } @@ -188,16 +185,16 @@ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in ) // such that first output packet's display time aligns with the // input packet's display time. This should give the correct time // when point-to-point encoding is in effect. - if (out_list && out_list->start > in->start) + if (out_list && out_list->s.start > in->s.start) { - int64_t slip = out_list->start - in->start; + int64_t slip = out_list->s.start - in->s.start; hb_buffer_t *out; out = out_list; while (out) { - out->start -= slip; - out->stop -= slip; + out->s.start -= slip; + out->s.stop -= slip; out = out->next; } } @@ -262,7 +259,7 @@ static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int uint8_t *pos = in_data; uint8_t *end = in_data + in_size; - // Parse values for in->start and in->stop + // Parse values for in->s.start and in->s.stop int64_t in_start, in_stop; if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) ) goto fail; @@ -335,8 +332,8 @@ static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int out->size = dst - out->data; // Copy metadata from the input packet to the output packet - out->start = in_start; - out->stop = in_stop; + out->s.start = in_start; + out->s.stop = in_stop; out->sequence = in_sequence; return out; @@ -348,7 +345,6 @@ fail: static hb_buffer_t * ssa_to_mkv_ssa( hb_work_object_t * w, hb_buffer_t * in ) { - hb_work_private_t * pv = w->private_data; hb_buffer_t * out_last = NULL; hb_buffer_t * out_first = NULL; @@ -361,62 +357,11 @@ static hb_buffer_t * ssa_to_mkv_ssa( hb_work_object_t * w, hb_buffer_t * in ) curLine; curLine = strtok_r( NULL, EOL, &curLine_parserData ) ) { - // Skip empty lines and spaces between adjacent CR and LF - if (curLine[0] == '\0') - continue; - - int64_t in_start, in_stop; - if ( parse_timing_from_ssa_packet( curLine, &in_start, &in_stop ) ) - continue; - - int len = strlen(curLine); - - // Convert the SSA line to MKV-SSA format - char *layerField = malloc( len ); - // SSA subtitles have an empty layer field (bare ','). The scanf - // format specifier "%*128[^,]" will not match on a bare ','. There - // must be at least one non ',' character in the match. So the format - // specifier is placed directly next to the ':' so that the next - // expected ' ' after the ':' will be the character it matches on - // when there is no layer field. - int numPartsRead = sscanf( curLine, "Dialogue:%128[^,],", layerField ); - if ( numPartsRead != 1 ) - { - free( layerField ); - continue; - } - - char *styleToTextFields = (char *)find_field( (uint8_t*)curLine, (uint8_t*)curLine + len, 4 ); - if ( styleToTextFields == NULL ) - { - free( layerField ); - continue; - } - - // The output should always be shorter than the input - hb_buffer_t * out = hb_buffer_init( len ); - char *mkvOut = (char*)out->data; - out->start = in_start; - out->stop = in_stop; - - // The sscanf conversion above will result in an extra space - // before the layerField. Strip the space. - char *stripLayerField = layerField; - for(; *stripLayerField == ' '; stripLayerField++); - - sprintf( mkvOut, "%d,%s,%s", - pv->readOrder++, stripLayerField, styleToTextFields ); - - free( layerField ); + hb_buffer_t * out; - len = strlen(mkvOut); - if ( len == 0 ) + out = ssa_decode_line_to_mkv_ssa( w, (uint8_t *) curLine, strlen( curLine ), in->sequence ); + if( out ) { - hb_buffer_close(&out); - } - else - { - out->size = len; if ( out_last == NULL ) { out_last = out_first = out; @@ -441,154 +386,64 @@ static hb_buffer_t * ssa_to_mkv_ssa( hb_work_object_t * w, hb_buffer_t * in ) * ReadOrder,Marked, Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0' * 1 2 3 4 5 6 7 8 9 */ -static hb_buffer_t *ssa_decode_line_to_picture( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence ) +static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence ) { hb_work_private_t * pv = w->private_data; + hb_buffer_t * out; - // Parse values for in->start and in->stop + // Parse values for in->s.start and in->s.stop int64_t in_start, in_stop; if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) ) goto fail; // Convert the SSA packet to MKV-SSA format, which is what libass expects char *mkvIn; - int mkvInSize; - { - char *layerField = malloc( in_size ); - // SSA subtitles have an empty layer field (bare ','). The scanf - // format specifier "%*128[^,]" will not match on a bare ','. There - // must be at least one non ',' character in the match. So the format - // specifier is placed directly next to the ':' so that the next - // expected ' ' after the ':' will be the character it matches on - // when there is no layer field. - int numPartsRead = sscanf( (char *) in_data, "Dialogue:%128[^,],", layerField ); - if ( numPartsRead != 1 ) - goto fail; - - char *styleToTextFields = (char *) find_field( in_data, in_data + in_size, 4 ); - if ( styleToTextFields == NULL ) { - free( layerField ); - goto fail; - } - - // The sscanf conversion above will result in an extra space - // before the layerField. Strip the space. - char *stripLayerField = layerField; - for(; *stripLayerField == ' '; stripLayerField++); - - mkvIn = malloc( in_size + 1 ); - mkvIn[0] = '\0'; - sprintf(mkvIn, "%d", pv->readOrder++); // ReadOrder: make this up - strcat( mkvIn, "," ); - strcat( mkvIn, stripLayerField ); - strcat( mkvIn, "," ); - strcat( mkvIn, (char *) styleToTextFields ); - - mkvInSize = strlen(mkvIn); - + int numPartsRead; + char *styleToTextFields; + char *layerField = malloc( in_size ); + + // SSA subtitles have an empty layer field (bare ','). The scanf + // format specifier "%*128[^,]" will not match on a bare ','. There + // must be at least one non ',' character in the match. So the format + // specifier is placed directly next to the ':' so that the next + // expected ' ' after the ':' will be the character it matches on + // when there is no layer field. + numPartsRead = sscanf( (char *)in_data, "Dialogue:%128[^,],", layerField ); + if ( numPartsRead != 1 ) + goto fail; + + styleToTextFields = (char *)find_field( in_data, in_data + in_size, 4 ); + if ( styleToTextFields == NULL ) { free( layerField ); + goto fail; } - // Parse MKV-SSA packet - ass_process_chunk( pv->ssaTrack, mkvIn, mkvInSize, in_start / 90, (in_stop - in_start) / 90 ); - - free( mkvIn ); - - // TODO: To support things like karaoke, it won't be sufficient to only generate - // new subtitle pictures when there are subtitle packets. Rather, pictures will - // need to be generated potentially continuously. - // - // Until "karaoke support" is implemented, make an educated guess about the - // timepoint within the subtitle that should be rendered. I guess the midpoint. - int64_t renderTime = ( in_start + in_stop ) / 2; - - int changed; - ASS_Image *frameList = ass_render_frame( pv->renderer, pv->ssaTrack, renderTime / 90, &changed ); - if ( !changed || !frameList ) - return NULL; - - int numFrames = 0; - ASS_Image *curFrame; - for (curFrame = frameList; curFrame; curFrame = curFrame->next) - numFrames++; - - hb_buffer_t *outSubpictureList = NULL; - hb_buffer_t **outSubpictureListTailPtr = &outSubpictureList; + // The sscanf conversion above will result in an extra space + // before the layerField. Strip the space. + char *stripLayerField = layerField; + for(; *stripLayerField == ' '; stripLayerField++); + + out = hb_buffer_init( in_size + 1 ); + mkvIn = (char*)out->data; + + mkvIn[0] = '\0'; + sprintf(mkvIn, "%d", pv->readOrder++); // ReadOrder: make this up + strcat( mkvIn, "," ); + strcat( mkvIn, stripLayerField ); + strcat( mkvIn, "," ); + strcat( mkvIn, (char *) styleToTextFields ); - // Generate a PICTURESUB packet from the frames - ASS_Image *frame; - for (frame = frameList; frame; frame = frame->next) { - // Allocate pixmap where drawing will be done - uint8_t *rgba = calloc(frame->w * frame->h * 4, 1); - - unsigned r = (frame->color >> 24) & 0xff; - unsigned g = (frame->color >> 16) & 0xff; - unsigned b = (frame->color >> 8) & 0xff; - unsigned a = (frame->color ) & 0xff; - - int x, y; - for (y = 0; y < frame->h; y++) { - for (x = 0; x < frame->w; x++) { - unsigned srcAlphaPrenormalized = frame->bitmap[y*frame->stride + x]; - unsigned srcAlpha = (255 - a) * srcAlphaPrenormalized / 255; - - uint8_t *dst = &rgba[(y*frame->w + x) * 4]; - unsigned oldDstAlpha = dst[3]; - - if (oldDstAlpha == 0) { - // Optimized version - dst[0] = r; - dst[1] = g; - dst[2] = b; - dst[3] = srcAlpha; - } else { - dst[3] = 255 - ( 255 - dst[3] ) * ( 255 - srcAlpha ) / 255; - if (dst[3] != 0) { - dst[0] = ( dst[0] * oldDstAlpha * (255-srcAlpha) / 255 + r * srcAlpha ) / dst[3]; - dst[1] = ( dst[1] * oldDstAlpha * (255-srcAlpha) / 255 + g * srcAlpha ) / dst[3]; - dst[2] = ( dst[2] * oldDstAlpha * (255-srcAlpha) / 255 + b * srcAlpha ) / dst[3]; - } - } - } - } - - // Generate output subpicture (in PICTURESUB format) - hb_buffer_t *out = hb_buffer_init(frame->w * frame->h * 4); - out->x = frame->dst_x; - out->y = frame->dst_y; - out->width = frame->w; - out->height = frame->h; - out->start = in_start; - out->stop = in_stop; - out->sequence = in_sequence; - - int i; - int numPixels = frame->w * frame->h; - for (i = 0; i < numPixels; i++) { - uint8_t *srcRgba = &rgba[i * 4]; - - uint8_t *dstY = &out->data[(numPixels * 0) + i]; - uint8_t *dstA = &out->data[(numPixels * 1) + i]; - uint8_t *dstU = &out->data[(numPixels * 2) + i]; - uint8_t *dstV = &out->data[(numPixels * 3) + i]; - - int srcYuv = hb_rgb2yuv((srcRgba[0] << 16) | (srcRgba[1] << 8) | (srcRgba[2] << 0)); - int srcA = srcRgba[3]; - - *dstY = (srcYuv >> 16) & 0xff; - *dstV = (srcYuv >> 8 ) & 0xff; - *dstU = (srcYuv >> 0 ) & 0xff; - *dstA = srcA / 16; // HB's max alpha value is 16 - } - - free(rgba); - - *outSubpictureListTailPtr = out; - outSubpictureListTailPtr = &out->next; + out->size = strlen(mkvIn); + out->s.start = in_start; + out->s.stop = in_stop; + out->sequence = in_sequence; + + if( out->size == 0 ) + { + hb_buffer_close(&out); } - // NOTE: The subpicture list is actually considered a single packet by most other code - hb_buffer_t *out = outSubpictureList; + free( layerField ); return out; @@ -597,14 +452,6 @@ fail: return NULL; } -static void ssa_log(int level, const char *fmt, va_list args, void *data) -{ - if ( level < 5 ) // same as default verbosity when no callback is set - { - hb_valog( 1, "[ass]", fmt, args ); - } -} - static int decssaInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; @@ -613,73 +460,6 @@ static int decssaInit( hb_work_object_t * w, hb_job_t * job ) w->private_data = pv; pv->job = job; - if ( w->subtitle->config.dest == RENDERSUB ) { - pv->ssa = ass_library_init(); - if ( !pv->ssa ) { - hb_log( "decssasub: libass initialization failed\n" ); - return 1; - } - - // Redirect libass output to hb_log - ass_set_message_cb( pv->ssa, ssa_log, NULL ); - - // Load embedded fonts - hb_list_t * list_attachment = job->title->list_attachment; - int i; - for ( i = 0; i < hb_list_count(list_attachment); i++ ) - { - hb_attachment_t * attachment = hb_list_item( list_attachment, i ); - - if ( attachment->type == FONT_TTF_ATTACH ) - { - ass_add_font( - pv->ssa, - attachment->name, - attachment->data, - attachment->size ); - } - } - - ass_set_extract_fonts( pv->ssa, 1 ); - ass_set_style_overrides( pv->ssa, NULL ); - - pv->renderer = ass_renderer_init( pv->ssa ); - if ( !pv->renderer ) { - hb_log( "decssasub: renderer initialization failed\n" ); - return 1; - } - - ass_set_use_margins( pv->renderer, 0 ); - ass_set_hinting( pv->renderer, ASS_HINTING_LIGHT ); // VLC 1.0.4 uses this - ass_set_font_scale( pv->renderer, 1.0 ); - ass_set_line_spacing( pv->renderer, 1.0 ); - - // Setup default font family - // - // SSA v4.00 requires that "Arial" be the default font - const char *font = NULL; - const char *family = "Arial"; - // NOTE: This can sometimes block for several *seconds*. - // It seems that process_fontdata() for some embedded fonts is slow. - ass_set_fonts( pv->renderer, font, family, /*haveFontConfig=*/1, NULL, 1 ); - - // Setup track state - pv->ssaTrack = ass_new_track( pv->ssa ); - if ( !pv->ssaTrack ) { - hb_log( "decssasub: ssa track initialization failed\n" ); - return 1; - } - - // NOTE: The codec extradata is expected to be in MKV format - ass_process_codec_private( pv->ssaTrack, - (char *) w->subtitle->extradata, w->subtitle->extradata_size ); - - int originalWidth = job->title->width; - int originalHeight = job->title->height; - ass_set_frame_size( pv->renderer, originalWidth, originalHeight); - ass_set_aspect_ratio( pv->renderer, /*dar=*/1.0, /*sar=*/1.0 ); - } - return 0; } @@ -690,7 +470,7 @@ static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t * in = *buf_in; #if SSA_VERBOSE_PACKETS - printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->start/90, in->stop/90, in->size, in->data); + printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data); #endif if ( in->size <= 0 ) @@ -714,15 +494,6 @@ static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, static void decssaClose( hb_work_object_t * w ) { - hb_work_private_t * pv = w->private_data; - - if ( pv->ssaTrack ) - ass_free_track( pv->ssaTrack ); - if ( pv->renderer ) - ass_renderer_done( pv->renderer ); - if ( pv->ssa ) - ass_library_done( pv->ssa ); - free( w->private_data ); } |