diff options
author | jstebbins <[email protected]> | 2012-03-27 20:11:26 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2012-03-27 20:11:26 +0000 |
commit | 45b8f81a2e184e2b7deaf47afc49483766191a27 (patch) | |
tree | 30ed0892995cb4ad3255909f69269c453000800a /libhb/decssasub.c | |
parent | 7eb7737023be00fa0dc9be75a4984b80c0e5ce57 (diff) |
Rework filter pipeline
This patch enhances the filter objects. The 2 key improvements are:
1. A filter can change the image dimensions as frames pass through it.
2. A filter can output more than one frame.
In addition, I have:
Moved cropping & scalling into a filter object
Added 90 degree rotation to the rotate filter
Moved subtitle burn-in rendering to a filter object.
Moved VFR/CFR handling into a framerate shaping filter object.
Removed render.c since all it's responsibilities got moved to filters.
Improves VOBSUB and SSA subtitle handling. Allows subtitle animations.
SSA karaoke support.
My apologies in advance if anything breaks ;)
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4546 b64f7644-9d1e-0410-96f1-a4d463321fa5
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 ); } |