summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stebbins <[email protected]>2015-10-14 15:26:44 -0700
committerJohn Stebbins <[email protected]>2015-10-14 15:30:16 -0700
commit10e3deb6cf13ebbe2020a7736a231db68e6ab27a (patch)
treeac7339f7ad63238dc779e3b6899ceaf9c018d130
parente918e48bfb3f0512dc3d246c0f233ca4bbe28918 (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.c4
-rw-r--r--libhb/common.h2
-rw-r--r--libhb/encx264.c18
-rw-r--r--libhb/grayscale.c257
-rw-r--r--libhb/hb_json.c8
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/preset.c11
-rw-r--r--libhb/work.c5
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