diff options
Diffstat (limited to 'libhb/cropscale.c')
-rw-r--r-- | libhb/cropscale.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/libhb/cropscale.c b/libhb/cropscale.c new file mode 100644 index 000000000..11a22a44e --- /dev/null +++ b/libhb/cropscale.c @@ -0,0 +1,204 @@ + +#include "hb.h" +#include "hbffmpeg.h" + +struct hb_filter_private_s +{ + int width_in; + int height_in; + int pix_fmt; + int pix_fmt_out; + int width_out; + int height_out; + int crop[4]; + struct SwsContext * context; +}; + +static int hb_crop_scale_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); + +static int hb_crop_scale_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +static int hb_crop_scale_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); + +static void hb_crop_scale_close( hb_filter_object_t * filter ); + +hb_filter_object_t hb_filter_crop_scale = +{ + .id = HB_FILTER_CROP_SCALE, + .enforce_order = 1, + .name = "Crop and Scale", + .settings = NULL, + .init = hb_crop_scale_init, + .work = hb_crop_scale_work, + .close = hb_crop_scale_close, + .info = hb_crop_scale_info, +}; + +static int hb_crop_scale_init( hb_filter_object_t * filter, + hb_filter_init_t * init ) +{ + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + + // TODO: add pix format option to settings + pv->pix_fmt_out = init->pix_fmt; + pv->width_in = init->width; + pv->height_in = init->height; + pv->width_out = init->width; + pv->height_out = init->height; + memcpy( pv->crop, init->crop, sizeof( int[4] ) ); + if( filter->settings ) + { + sscanf( filter->settings, "%d:%d:%d:%d:%d:%d", + &pv->width_out, &pv->height_out, + &pv->crop[0], &pv->crop[1], &pv->crop[2], &pv->crop[3] ); + } + // Set init values so the next stage in the pipline + // knows what it will be getting + init->pix_fmt = pv->pix_fmt; + init->width = pv->width_out; + init->height = pv->height_out; + memcpy( init->crop, pv->crop, sizeof( int[4] ) ); + + return 0; +} + +static int hb_crop_scale_info( hb_filter_object_t * filter, + hb_filter_info_t * info ) +{ + hb_filter_private_t * pv = filter->private_data; + + if( !pv ) + return 0; + + // Set init values so the next stage in the pipline + // knows what it will be getting + memset( info, 0, sizeof( hb_filter_info_t ) ); + info->out.pix_fmt = pv->pix_fmt; + info->out.width = pv->width_out; + info->out.height = pv->height_out; + memcpy( info->out.crop, pv->crop, sizeof( int[4] ) ); + + int cropped_width = pv->width_in - ( pv->crop[2] + pv->crop[3] ); + int cropped_height = pv->height_in - ( pv->crop[0] + pv->crop[1] ); + + sprintf( info->human_readable_desc, + "source: %d * %d, crop (%d/%d/%d/%d): %d * %d, scale: %d * %d", + pv->width_in, pv->height_in, + pv->crop[0], pv->crop[1], pv->crop[2], pv->crop[3], + cropped_width, cropped_height, pv->width_out, pv->height_out ); + + return 0; +} + +static void hb_crop_scale_close( hb_filter_object_t * filter ) +{ + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + if ( pv->context ) + { + sws_freeContext( pv->context ); + } + + free( pv ); + filter->private_data = NULL; +} + +static hb_buffer_t* crop_scale( hb_filter_private_t * pv, hb_buffer_t * in ) +{ + AVPicture pic_in; + AVPicture pic_out; + AVPicture pic_crop; + hb_buffer_t * out; + out = hb_video_buffer_init( pv->width_out, pv->height_out ); + + hb_avpicture_fill( &pic_in, in ); + hb_avpicture_fill( &pic_out, out ); + + // Crop; this alters the pointer to the data to point to the + // correct place for cropped frame + av_picture_crop( &pic_crop, &pic_in, in->f.fmt, + pv->crop[0], pv->crop[2] ); + + if ( !pv->context || + pv->width_in != in->f.width || + pv->height_in != in->f.height || + pv->pix_fmt != in->f.fmt ) + { + // Something changed, need a new scaling context. + if( pv->context ) + sws_freeContext( pv->context ); + + pv->context = hb_sws_get_context( + in->f.width - (pv->crop[2] + pv->crop[3]), + in->f.height - (pv->crop[0] + pv->crop[1]), + in->f.fmt, + out->f.width, out->f.height, out->f.fmt, + SWS_LANCZOS | SWS_ACCURATE_RND ); + pv->width_in = in->f.width; + pv->height_in = in->f.height; + pv->pix_fmt = in->f.fmt; + } + + // Scale pic_crop into pic_render according to the + // context set up above + sws_scale(pv->context, + (const uint8_t* const*)pic_crop.data, + pic_crop.linesize, + 0, in->f.height - (pv->crop[0] + pv->crop[1]), + pic_out.data, pic_out.linesize); + + out->s = in->s; + hb_buffer_move_subs( out, in ); + return out; +} + +static int hb_crop_scale_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + + if ( in->size <= 0 ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_DONE; + } + + if ( !pv ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_OK; + } + + // If width or height were not set, set them now based on the + // input width & height + if ( pv->width_out <= 0 || pv->height_out <= 0 ) + { + pv->width_out = in->f.width - (pv->crop[2] + pv->crop[3]); + pv->height_out = in->f.height - (pv->crop[0] + pv->crop[1]); + } + if ( in->f.fmt == pv->pix_fmt_out && + !pv->crop[0] && !pv->crop[1] && !pv->crop[2] && !pv->crop[3] && + in->f.width == pv->width_out && in->f.height == pv->height_out ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_OK; + } + *buf_out = crop_scale( pv, in ); + + return HB_FILTER_OK; +} |