diff options
author | John Stebbins <[email protected]> | 2015-10-14 15:26:44 -0700 |
---|---|---|
committer | John Stebbins <[email protected]> | 2015-10-14 15:30:16 -0700 |
commit | 10e3deb6cf13ebbe2020a7736a231db68e6ab27a (patch) | |
tree | ac7339f7ad63238dc779e3b6899ceaf9c018d130 | |
parent | e918e48bfb3f0512dc3d246c0f233ca4bbe28918 (diff) |
grayscale: make it a real filter
It only worked properly with the x264 encoder. Now it works with all
encoders.
-rw-r--r-- | libhb/common.c | 4 | ||||
-rw-r--r-- | libhb/common.h | 2 | ||||
-rw-r--r-- | libhb/encx264.c | 18 | ||||
-rw-r--r-- | libhb/grayscale.c | 257 | ||||
-rw-r--r-- | libhb/hb_json.c | 8 | ||||
-rw-r--r-- | libhb/internal.h | 1 | ||||
-rw-r--r-- | libhb/preset.c | 11 | ||||
-rw-r--r-- | libhb/work.c | 5 |
8 files changed, 279 insertions, 27 deletions
diff --git a/libhb/common.c b/libhb/common.c index c03fb2807..47f1b26f9 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -3662,6 +3662,10 @@ hb_filter_object_t * hb_filter_init( int filter_id ) filter = &hb_filter_rotate; break; + case HB_FILTER_GRAYSCALE: + filter = &hb_filter_grayscale; + break; + #ifdef USE_QSV case HB_FILTER_QSV: filter = &hb_filter_qsv; diff --git a/libhb/common.h b/libhb/common.h index 4f39c8944..bac165a4f 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -1185,6 +1185,7 @@ typedef struct hb_filter_init_s int crop[4]; hb_rational_t vrate; int cfr; + int grayscale; } hb_filter_init_t; typedef struct hb_filter_info_s @@ -1252,6 +1253,7 @@ enum // Finally filters that don't care what order they are in, // except that they must be after the above filters HB_FILTER_ROTATE, + HB_FILTER_GRAYSCALE, // for QSV - important to have as a last one HB_FILTER_QSV_POST, diff --git a/libhb/encx264.c b/libhb/encx264.c index 2580f2ee9..552e713ad 100644 --- a/libhb/encx264.c +++ b/libhb/encx264.c @@ -51,7 +51,6 @@ struct hb_work_private_s hb_job_t * job; x264_t * x264; x264_picture_t pic_in; - uint8_t * grey_data; int64_t last_stop; // Debugging - stop time of previous input frame @@ -374,15 +373,6 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job ) pv->pic_in.img.i_csp = X264_CSP_I420; pv->pic_in.img.i_plane = 3; - if( job->grayscale ) - { - int uvsize = hb_image_stride(AV_PIX_FMT_YUV420P, job->width, 1) * - hb_image_height(AV_PIX_FMT_YUV420P, job->height, 1); - pv->grey_data = malloc(uvsize); - memset(pv->grey_data, 0x80, uvsize); - pv->pic_in.img.plane[1] = pv->pic_in.img.plane[2] = pv->grey_data; - } - return 0; } @@ -401,7 +391,6 @@ void encx264Close( hb_work_object_t * w ) hb_list_close(&pv->delayed_chapters); } - free( pv->grey_data ); x264_encoder_close( pv->x264 ); free( pv ); w->private_data = NULL; @@ -580,11 +569,8 @@ static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in ) pv->pic_in.img.i_stride[1] = in->plane[1].stride; pv->pic_in.img.i_stride[2] = in->plane[2].stride; pv->pic_in.img.plane[0] = in->plane[0].data; - if( !job->grayscale ) - { - pv->pic_in.img.plane[1] = in->plane[1].data; - pv->pic_in.img.plane[2] = in->plane[2].data; - } + pv->pic_in.img.plane[1] = in->plane[1].data; + pv->pic_in.img.plane[2] = in->plane[2].data; if( in->s.new_chap && job->chapter_markers ) { diff --git a/libhb/grayscale.c b/libhb/grayscale.c new file mode 100644 index 000000000..bdbf3b722 --- /dev/null +++ b/libhb/grayscale.c @@ -0,0 +1,257 @@ +/* grayscale.c + + Copyright (c) 2003-2015 HandBrake Team + This file is part of the HandBrake source code + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License v2. + For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "taskset.h" + +// Settings: +// This filter has no settings. +// But at some point it might be interesting to add effects other than +// just gray. + +typedef struct grayscale_arguments_s { + hb_buffer_t *src; +} grayscale_arguments_t; + +struct hb_filter_private_s +{ + int cpu_count; + + taskset_t grayscale_taskset; // Threads - one per CPU + grayscale_arguments_t *grayscale_arguments; // Arguments to thread for work +}; + +static int hb_grayscale_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); + +static int hb_grayscale_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +static void hb_grayscale_close( hb_filter_object_t * filter ); + +static int hb_grayscale_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); + +hb_filter_object_t hb_filter_grayscale = +{ + .id = HB_FILTER_GRAYSCALE, + .enforce_order = 0, + .name = "Grayscale", + .settings = NULL, + .init = hb_grayscale_init, + .work = hb_grayscale_work, + .close = hb_grayscale_close, + .info = hb_grayscale_info +}; + + +typedef struct grayscale_thread_arg_s { + hb_filter_private_t *pv; + int segment; +} grayscale_thread_arg_t; + +/* + * gray this segment of all three planes in a single thread. + */ +void grayscale_filter_thread( void *thread_args_v ) +{ + grayscale_arguments_t *grayscale_work = NULL; + hb_filter_private_t * pv; + int run = 1; + int plane; + int segment, segment_start, segment_stop; + grayscale_thread_arg_t *thread_args = thread_args_v; + hb_buffer_t *src_buf; + + pv = thread_args->pv; + segment = thread_args->segment; + + hb_log("Grayscale thread started for segment %d", segment); + + while( run ) + { + /* + * Wait here until there is work to do. + */ + taskset_thread_wait4start( &pv->grayscale_taskset, segment ); + + if( taskset_thread_stop( &pv->grayscale_taskset, segment ) ) + { + /* + * No more work to do, exit this thread. + */ + run = 0; + goto report_completion; + } + + grayscale_work = &pv->grayscale_arguments[segment]; + if (grayscale_work->src == NULL) + { + hb_error( "Thread started when no work available" ); + hb_snooze(500); + goto report_completion; + } + + /* + * Process all three planes, but only this segment of it. + */ + src_buf = grayscale_work->src; + for (plane = 1; plane < 3; plane++) + { + int src_stride = src_buf->plane[plane].stride; + int height = src_buf->plane[plane].height; + segment_start = (height / pv->cpu_count) * segment; + if (segment == pv->cpu_count - 1) + { + /* + * Final segment + */ + segment_stop = height; + } else { + segment_stop = (height / pv->cpu_count) * (segment + 1); + } + + memset(&src_buf->plane[plane].data[segment_start * src_stride], + 0x80, (segment_stop - segment_start) * src_stride); + } + +report_completion: + /* + * Finished this segment, let everyone know. + */ + taskset_thread_complete( &pv->grayscale_taskset, segment ); + } +} + + +/* + * threaded gray - each thread grays a single segment of all + * three planes. Where a segment is defined as the frame divided by + * the number of CPUs. + * + * This function blocks until the frame is grayed. + */ +static void grayscale_filter( hb_filter_private_t * pv, + hb_buffer_t * in ) +{ + + int segment; + + for( segment = 0; segment < pv->cpu_count; segment++ ) + { + /* + * Setup the work for this plane. + */ + pv->grayscale_arguments[segment].src = in; + } + + /* + * Allow the taskset threads to make one pass over the data. + */ + taskset_cycle( &pv->grayscale_taskset ); + + /* + * Entire frame is now grayed. + */ +} + + +static int hb_grayscale_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; + + pv->cpu_count = hb_get_cpu_count(); + + /* + * Create gray taskset. + */ + pv->grayscale_arguments = malloc(sizeof(grayscale_arguments_t) * + pv->cpu_count); + if (pv->grayscale_arguments == NULL || + taskset_init( &pv->grayscale_taskset, pv->cpu_count, + sizeof( grayscale_thread_arg_t ) ) == 0) + { + hb_error( "grayscale could not initialize taskset" ); + } + + int ii; + for (ii = 0; ii < pv->cpu_count; ii++) + { + grayscale_thread_arg_t *thread_args; + + thread_args = taskset_thread_args(&pv->grayscale_taskset, ii); + + thread_args->pv = pv; + thread_args->segment = ii; + pv->grayscale_arguments[ii].src = NULL; + + if (taskset_thread_spawn(&pv->grayscale_taskset, ii, + "grayscale_filter_segment", + grayscale_filter_thread, + HB_NORMAL_PRIORITY ) == 0) + { + hb_error( "grayscale could not spawn thread" ); + } + } + + return 0; +} + +static int hb_grayscale_info( hb_filter_object_t * filter, + hb_filter_info_t * info ) +{ + info->human_readable_desc[0] = 0; + return 0; +} + +static void hb_grayscale_close( hb_filter_object_t * filter ) +{ + hb_filter_private_t * pv = filter->private_data; + + if( !pv ) + { + return; + } + + taskset_fini( &pv->grayscale_taskset ); + + /* + * free memory for grayscale structs + */ + free( pv->grayscale_arguments ); + + free( pv ); + filter->private_data = NULL; +} + +static int hb_grayscale_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; + + *buf_in = NULL; + if (in->s.flags & HB_BUF_FLAG_EOF) + { + *buf_out = in; + return HB_FILTER_DONE; + } + + // Grayscale! + grayscale_filter(pv, in); + + *buf_out = in; + + return HB_FILTER_OK; +} diff --git a/libhb/hb_json.c b/libhb/hb_json.c index e5c64bb9f..ec2a8cae1 100644 --- a/libhb/hb_json.c +++ b/libhb/hb_json.c @@ -403,8 +403,8 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) "s:{s:{s:o, s:o, s:o, s:o}, s:[]}," // Metadata "s:{}," - // Filters {Grayscale, FilterList []} - "s:{s:o, s:[]}" + // Filters {FilterList []} + "s:{s:[]}" "}", "SequenceID", hb_value_int(job->sequence_id), "Destination", @@ -438,7 +438,6 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) "SubtitleList", "Metadata", "Filters", - "Grayscale", hb_value_bool(job->grayscale), "FilterList" ); if (dict == NULL) @@ -853,7 +852,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) // Comment, Genre, Description, LongDescription} "s?{s?s, s?s, s?s, s?s, s?s, s?s, s?s, s?s, s?s}," // Filters {FilterList} - "s?{s?b, s?o}" + "s?{s?o}" "}", "SequenceID", unpack_i(&job->sequence_id), "Destination", @@ -913,7 +912,6 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) "Description", unpack_s(&meta_desc), "LongDescription", unpack_s(&meta_long_desc), "Filters", - "Grayscale", unpack_b(&job->grayscale), "FilterList", unpack_o(&filter_list) ); if (result < 0) diff --git a/libhb/internal.h b/libhb/internal.h index ab444ace8..e66783bb4 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -452,6 +452,7 @@ extern hb_filter_object_t hb_filter_denoise; extern hb_filter_object_t hb_filter_nlmeans; extern hb_filter_object_t hb_filter_decomb; extern hb_filter_object_t hb_filter_rotate; +extern hb_filter_object_t hb_filter_grayscale; extern hb_filter_object_t hb_filter_crop_scale; extern hb_filter_object_t hb_filter_render_sub; extern hb_filter_object_t hb_filter_vfr; diff --git a/libhb/preset.c b/libhb/preset.c index 83535d3bc..2b072f188 100644 --- a/libhb/preset.c +++ b/libhb/preset.c @@ -1076,9 +1076,6 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict) filter_list = hb_value_array_init(); hb_dict_set(filters_dict, "FilterList", filter_list); - hb_dict_set(filters_dict, "Grayscale", hb_value_xform( - hb_dict_get(preset, "VideoGrayScale"), HB_VALUE_TYPE_BOOL)); - // Detelecine filter hb_value_t *detel_val = hb_dict_get(preset, "PictureDetelecine"); if (detel_val != NULL) @@ -1246,6 +1243,14 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict) } free(rotate); + // Grayscale filter + if (hb_value_get_bool(hb_dict_get(preset, "VideoGrayScale"))) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_GRAYSCALE)); + hb_value_array_append(filter_list, filter_dict); + } + hb_value_t *fr_value = hb_dict_get(preset, "VideoFramerate"); int vrate_den = get_video_framerate(fr_value); if (vrate_den < 0) diff --git a/libhb/work.c b/libhb/work.c index 8ea9db918..825654524 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -340,9 +340,6 @@ void hb_display_job_info(hb_job_t *job) } } - if ( job->grayscale ) - hb_log( " + grayscale mode" ); - hb_log( " + Output geometry" ); hb_log( " + storage dimensions: %d x %d", job->width, job->height ); hb_log( " + pixel aspect ratio: %d : %d", job->par.num, job->par.den ); @@ -971,6 +968,7 @@ static void do_job(hb_job_t *job) memcpy(init.crop, title->crop, sizeof(int[4])); init.vrate = title->vrate; init.cfr = 0; + init.grayscale = 0; for( i = 0; i < hb_list_count( job->list_filter ); ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); @@ -990,6 +988,7 @@ static void do_job(hb_job_t *job) memcpy(job->crop, init.crop, sizeof(int[4])); job->vrate = init.vrate; job->cfr = init.cfr; + job->grayscale = init.grayscale; // Perform filter post_init which informs filters of final // job configuration. e.g. rendersub filter needs to know the |