summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stebbins <[email protected]>2017-05-31 10:21:53 -0700
committerBradley Sepos <[email protected]>2017-06-06 11:42:09 -0400
commit021b3caa6efbfadb212bb1e686c2de08da6df2c6 (patch)
tree5b972504671f6bb1c19f99ad36a477f50dbe7cba
parentc365e557920c8911e638534b5fa5cc59487834a7 (diff)
filter: add frame parallelizing filter wrapper
This wrapper can be used to frame parallelize simple video filters. By simple, I mean there can be no temporal context that is shared from one frame to the next. Wrap unsharp and lapsharp filters. unsharp required a small rework to separate out temporary storage that is required when processing each frame. We now need to duplicate this storage for each thread. Closes #759.
-rw-r--r--libhb/common.c28
-rw-r--r--libhb/common.h21
-rw-r--r--libhb/hb.c4
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/mt_frame_filter.c236
-rw-r--r--libhb/unsharp.c169
6 files changed, 403 insertions, 56 deletions
diff --git a/libhb/common.c b/libhb/common.c
index 71a8d0e26..ebe51c6e5 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -3861,6 +3861,7 @@ hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter )
memcpy( filter_copy, filter, sizeof( hb_filter_object_t ) );
if( filter->settings )
filter_copy->settings = hb_value_dup(filter->settings);
+ filter_copy->sub_filter = hb_filter_copy(filter->sub_filter);
return filter_copy;
}
@@ -3997,6 +3998,10 @@ hb_filter_object_t * hb_filter_get( int filter_id )
break;
#endif
+ case HB_FILTER_MT_FRAME:
+ filter = &hb_filter_mt_frame;
+ break;
+
default:
filter = NULL;
break;
@@ -4006,7 +4011,23 @@ hb_filter_object_t * hb_filter_get( int filter_id )
hb_filter_object_t * hb_filter_init( int filter_id )
{
- return hb_filter_copy(hb_filter_get(filter_id));
+ switch (filter_id)
+ {
+ case HB_FILTER_UNSHARP:
+ case HB_FILTER_LAPSHARP:
+ {
+ hb_filter_object_t * wrapper;
+
+ wrapper = hb_filter_copy(hb_filter_get(HB_FILTER_MT_FRAME));
+ wrapper->sub_filter = hb_filter_copy(hb_filter_get(filter_id));
+ wrapper->id = filter_id;
+ wrapper->name = wrapper->sub_filter->name;
+ return wrapper;
+ } break;
+
+ default:
+ return hb_filter_copy(hb_filter_get(filter_id));
+ }
}
/**********************************************************************
@@ -4018,6 +4039,11 @@ void hb_filter_close( hb_filter_object_t ** _f )
{
hb_filter_object_t * f = *_f;
+ if (f == NULL)
+ {
+ return;
+ }
+ hb_filter_close(&f->sub_filter);
hb_value_free(&f->settings);
free( f );
diff --git a/libhb/common.h b/libhb/common.h
index a251365ac..2edbdc697 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -1222,12 +1222,15 @@ struct hb_filter_object_s
hb_dict_t * settings;
#ifdef __LIBHB__
- int (* init) ( hb_filter_object_t *, hb_filter_init_t * );
- int (* post_init)( hb_filter_object_t *, hb_job_t * );
- int (* work) ( hb_filter_object_t *,
- hb_buffer_t **, hb_buffer_t ** );
- void (* close) ( hb_filter_object_t * );
- hb_filter_info_t * (* info) ( hb_filter_object_t * );
+ int (* init) ( hb_filter_object_t *, hb_filter_init_t * );
+ int (* init_thread)( hb_filter_object_t *, int );
+ int (* post_init) ( hb_filter_object_t *, hb_job_t * );
+ int (* work) ( hb_filter_object_t *,
+ hb_buffer_t **, hb_buffer_t ** );
+ int (* work_thread)( hb_filter_object_t *,
+ hb_buffer_t **, hb_buffer_t **, int );
+ void (* close) ( hb_filter_object_t * );
+ hb_filter_info_t * (* info) ( hb_filter_object_t * );
const char * settings_template;
@@ -1246,6 +1249,8 @@ struct hb_filter_object_s
// These are used to bridge the chapter to the next buffer
int chapter_val;
int64_t chapter_time;
+
+ hb_filter_object_t * sub_filter;
#endif
};
@@ -1284,7 +1289,9 @@ enum
HB_FILTER_QSV_POST,
// default MSDK VPP filter
HB_FILTER_QSV,
- HB_FILTER_LAST = HB_FILTER_QSV
+ HB_FILTER_LAST = HB_FILTER_QSV,
+ // wrapper filter for frame based multi-threading of simple filters
+ HB_FILTER_MT_FRAME
};
hb_filter_object_t * hb_filter_get( int filter_id );
diff --git a/libhb/hb.c b/libhb/hb.c
index f8bba2a66..a680df91f 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -1441,6 +1441,10 @@ void hb_add_filter_dict( hb_job_t * job, hb_filter_object_t * filter,
settings = hb_value_dup(settings_in);
}
filter->settings = settings;
+ if (filter->sub_filter)
+ {
+ filter->sub_filter->settings = hb_value_dup(settings);
+ }
if( filter->enforce_order )
{
// Find the position in the filter chain this filter belongs in
diff --git a/libhb/internal.h b/libhb/internal.h
index 4ffd6422a..5d462f37f 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -468,6 +468,7 @@ extern hb_filter_object_t hb_filter_pad;
extern hb_filter_object_t hb_filter_lapsharp;
extern hb_filter_object_t hb_filter_unsharp;
extern hb_filter_object_t hb_filter_avfilter;
+extern hb_filter_object_t hb_filter_mt_frame;
#ifdef USE_QSV
extern hb_filter_object_t hb_filter_qsv;
diff --git a/libhb/mt_frame_filter.c b/libhb/mt_frame_filter.c
new file mode 100644
index 000000000..7ae009c0e
--- /dev/null
+++ b/libhb/mt_frame_filter.c
@@ -0,0 +1,236 @@
+/* mt_frame_filter.c
+
+ Copyright (c) 2003-2017 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
+ */
+
+/* This is a psuedo-filter that wraps other filters to provide frame
+ * based multi-threading of the wrapped filter. The sub-filter must
+ * operate on each frame independently with no context carried over
+ * from one frame to the next. */
+
+#include "hb.h"
+#include "taskset.h"
+
+typedef struct
+{
+ hb_filter_private_t *pv;
+ int segment;
+ hb_buffer_t *out;
+} mt_frame_thread_arg_t;
+
+struct hb_filter_private_s
+{
+ hb_filter_object_t * sub_filter;
+ hb_buffer_t ** buf;
+ int frame_count;
+ taskset_t taskset;
+ int thread_count;
+ mt_frame_thread_arg_t ** thread_data;
+};
+
+static int mt_frame_init(hb_filter_object_t *filter, hb_filter_init_t *init);
+static int mt_frame_work(hb_filter_object_t *filter,
+ hb_buffer_t **buf_in,
+ hb_buffer_t **buf_out);
+static void mt_frame_close(hb_filter_object_t *filter);
+
+static void mt_frame_filter_thread(void *thread_args_v);
+
+static const char mt_frame_template[] = "";
+
+hb_filter_object_t hb_filter_mt_frame =
+{
+ .id = HB_FILTER_MT_FRAME,
+ .enforce_order = 0,
+ .name = "MTFrame (mtframe)",
+ .settings = NULL,
+ .init = mt_frame_init,
+ .work = mt_frame_work,
+ .close = mt_frame_close,
+ .settings_template = mt_frame_template,
+};
+
+static int mt_frame_init(hb_filter_object_t * filter,
+ hb_filter_init_t * init)
+{
+ filter->private_data = calloc(sizeof(struct hb_filter_private_s), 1);
+ hb_filter_private_t *pv = filter->private_data;
+
+ pv->sub_filter = filter->sub_filter;
+ pv->sub_filter->init(pv->sub_filter, init);
+
+ pv->thread_count = hb_get_cpu_count();
+ pv->buf = calloc(pv->thread_count, sizeof(hb_buffer_t*));
+
+ pv->thread_data = malloc(pv->thread_count * sizeof(mt_frame_thread_arg_t*));
+ if (taskset_init(&pv->taskset, pv->thread_count,
+ sizeof(mt_frame_thread_arg_t)) == 0)
+ {
+ hb_error("MTFrame could not initialize taskset");
+ goto fail;
+ }
+
+ for (int ii = 0; ii < pv->thread_count; ii++)
+ {
+ pv->thread_data[ii] = taskset_thread_args(&pv->taskset, ii);
+ if (pv->thread_data[ii] == NULL)
+ {
+ hb_error("MTFrame could not create thread args");
+ goto fail;
+ }
+ pv->thread_data[ii]->pv = pv;
+ pv->thread_data[ii]->segment = ii;
+ if (taskset_thread_spawn(&pv->taskset, ii, "mt_frame_filter",
+ mt_frame_filter_thread, HB_NORMAL_PRIORITY) == 0)
+ {
+ hb_error("MTFrame could not spawn thread");
+ goto fail;
+ }
+ }
+
+ if (pv->sub_filter->init_thread != NULL)
+ {
+ if (pv->sub_filter->init_thread(pv->sub_filter, pv->thread_count) < 0)
+ {
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ taskset_fini(&pv->taskset);
+ free(pv->thread_data);
+ free(pv);
+ return -1;
+}
+
+static void mt_frame_close(hb_filter_object_t *filter)
+{
+ hb_filter_private_t *pv = filter->private_data;
+
+ if (pv == NULL)
+ {
+ return;
+ }
+
+ pv->sub_filter->close(pv->sub_filter);
+ taskset_fini(&pv->taskset);
+ free(pv->thread_data);
+ free(pv->buf);
+ free(pv);
+ filter->private_data = NULL;
+}
+
+static void mt_frame_filter_thread(void *thread_args_v)
+{
+ mt_frame_thread_arg_t *thread_data = thread_args_v;
+ hb_filter_private_t *pv = thread_data->pv;
+ int segment = thread_data->segment;
+
+ hb_log("MTFrame thread started for segment %d", segment);
+
+ while (1)
+ {
+ // Wait until there is work to do.
+ taskset_thread_wait4start(&pv->taskset, segment);
+
+ if (taskset_thread_stop(&pv->taskset, segment))
+ {
+ break;
+ }
+
+ if (pv->sub_filter->work_thread != NULL)
+ {
+ pv->sub_filter->work_thread(pv->sub_filter,
+ &pv->buf[segment], &thread_data->out, segment);
+ }
+ else
+ {
+ pv->sub_filter->work(pv->sub_filter,
+ &pv->buf[segment], &thread_data->out);
+ }
+ if (pv->buf[segment] != NULL)
+ {
+ hb_buffer_close(&pv->buf[segment]);
+ }
+
+ // Finished this segment, notify.
+ taskset_thread_complete(&pv->taskset, segment);
+ }
+ taskset_thread_complete(&pv->taskset, segment);
+}
+
+static hb_buffer_t * mt_frame_filter(hb_filter_private_t *pv)
+{
+ if (pv->frame_count < pv->thread_count)
+ {
+ return NULL;
+ }
+
+ taskset_cycle(&pv->taskset);
+ pv->frame_count = 0;
+
+ // Collect results from taskset
+ hb_buffer_list_t list;
+ hb_buffer_list_clear(&list);
+ for (int t = 0; t < pv->thread_count; t++)
+ {
+ hb_buffer_list_append(&list, pv->thread_data[t]->out);
+ }
+ return hb_buffer_list_clear(&list);
+}
+
+static hb_buffer_t * mt_frame_filter_flush(hb_filter_private_t *pv)
+{
+ hb_buffer_list_t list;
+
+ hb_buffer_list_clear(&list);
+ for (int f = 0; f < pv->frame_count; f++)
+ {
+ hb_buffer_t * out;
+ pv->sub_filter->work(pv->sub_filter, &pv->buf[f], &out);
+
+ if (pv->buf[f] != NULL)
+ {
+ hb_buffer_close(&pv->buf[f]);
+ }
+ hb_buffer_list_append(&list, out);
+ }
+ pv->frame_count = 0;
+ return hb_buffer_list_clear(&list);
+}
+
+static int mt_frame_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)
+ {
+ hb_buffer_list_t list;
+ hb_buffer_t *buf;
+
+ // Flush buffered frames
+ buf = mt_frame_filter_flush(pv);
+ hb_buffer_list_set(&list, buf);
+
+ // And terminate the buffer list with a EOF buffer
+ hb_buffer_list_append(&list, in);
+ *buf_out = hb_buffer_list_clear(&list);
+
+ return HB_FILTER_DONE;
+ }
+
+ pv->buf[pv->frame_count++] = in;
+ *buf_out = mt_frame_filter(pv);
+
+ return HB_FILTER_OK;
+}
diff --git a/libhb/unsharp.c b/libhb/unsharp.c
index 2d5a1d577..8f1c99e6b 100644
--- a/libhb/unsharp.c
+++ b/libhb/unsharp.c
@@ -19,6 +19,8 @@
typedef struct
{
+ int pix_fmt; // source pixel format
+ int width; // source video width
double strength; // strength
int size; // pixel context region width (must be odd)
@@ -26,24 +28,37 @@ typedef struct
int amount;
int scalebits;
int32_t halfscale;
- uint32_t * SC[UNSHARP_SIZE_MAX - 1];
} unsharp_plane_context_t;
+typedef struct
+{
+ uint32_t * SC[UNSHARP_SIZE_MAX - 1];
+} unsharp_thread_context_t;
+
+typedef unsharp_thread_context_t unsharp_thread_context3_t[3];
+
struct hb_filter_private_s
{
- unsharp_plane_context_t plane_ctx[3];
+ unsharp_plane_context_t plane_ctx[3];
+ unsharp_thread_context3_t * thread_ctx;
+ int threads;
};
-static int hb_unsharp_init(hb_filter_object_t *filter,
- hb_filter_init_t *init);
+static int unsharp_init(hb_filter_object_t *filter,
+ hb_filter_init_t *init);
+
+static int unsharp_init_thread(hb_filter_object_t *filter, int threads);
-static int hb_unsharp_work(hb_filter_object_t *filter,
- hb_buffer_t ** buf_in,
- hb_buffer_t ** buf_out);
+static int unsharp_work(hb_filter_object_t *filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out);
+static int unsharp_work_thread(hb_filter_object_t *filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out, int thread);
-static void hb_unsharp_close(hb_filter_object_t *filter);
+static void unsharp_close(hb_filter_object_t *filter);
-static const char hb_unsharp_template[] =
+static const char unsharp_template[] =
"y-strength=^"HB_FLOAT_REG"$:y-size=^"HB_INT_REG"$:"
"cb-strength=^"HB_FLOAT_REG"$:cb-size=^"HB_INT_REG"$:"
"cr-strength=^"HB_FLOAT_REG"$:cr-size=^"HB_INT_REG"$";
@@ -54,20 +69,23 @@ hb_filter_object_t hb_filter_unsharp =
.enforce_order = 1,
.name = "Sharpen (unsharp)",
.settings = NULL,
- .init = hb_unsharp_init,
- .work = hb_unsharp_work,
- .close = hb_unsharp_close,
- .settings_template = hb_unsharp_template,
+ .init = unsharp_init,
+ .init_thread = unsharp_init_thread,
+ .work = unsharp_work,
+ .work_thread = unsharp_work_thread,
+ .close = unsharp_close,
+ .settings_template = unsharp_template,
};
-static void hb_unsharp(const uint8_t *src,
- uint8_t *dst,
- const int width,
- const int height,
- const int stride,
- unsharp_plane_context_t * ctx)
+static void unsharp(const uint8_t *src,
+ uint8_t *dst,
+ const int width,
+ const int height,
+ const int stride,
+ unsharp_plane_context_t * ctx,
+ unsharp_thread_context_t * tctx)
{
- uint32_t **SC = ctx->SC;
+ uint32_t **SC = tctx->SC;
uint32_t SR[UNSHARP_SIZE_MAX - 1],
Tmp1,
Tmp2;
@@ -138,10 +156,15 @@ static void hb_unsharp(const uint8_t *src,
}
}
-static int hb_unsharp_init(hb_filter_object_t *filter,
- hb_filter_init_t *init)
+static int unsharp_init(hb_filter_object_t *filter,
+ hb_filter_init_t *init)
{
filter->private_data = calloc(sizeof(struct hb_filter_private_s), 1);
+ if (filter->private_data == NULL)
+ {
+ hb_error("Unsharp calloc failed");
+ return -1;
+ }
hb_filter_private_t * pv = filter->private_data;
// Mark parameters unset
@@ -179,7 +202,8 @@ static int hb_unsharp_init(hb_filter_object_t *filter,
for (int c = 0; c < 3; c++)
{
unsharp_plane_context_t * ctx = &pv->plane_ctx[c];
- int w = hb_image_width(init->pix_fmt, init->geometry.width, c);
+
+ ctx->width = init->geometry.width;
// Replace unset values with defaults
if (ctx->strength == -1)
@@ -204,44 +228,85 @@ static int hb_unsharp_init(hb_filter_object_t *filter,
ctx->steps = ctx->size / 2;
ctx->scalebits = ctx->steps * 4;
ctx->halfscale = 1 << (ctx->scalebits - 1);
+ }
- int z;
- for (z = 0; z < 2 * ctx->steps; z++)
- {
- ctx->SC[z] = malloc(sizeof(*(ctx->SC[z])) * (w + 2 * ctx->steps));
- }
+ if (unsharp_init_thread(filter, 1) < 0)
+ {
+ unsharp_close(filter);
+ return -1;
}
return 0;
}
-static void hb_unsharp_close(hb_filter_object_t * filter)
+static void unsharp_thread_close(hb_filter_private_t *pv)
{
- hb_filter_private_t *pv = filter->private_data;
-
- if (pv == NULL)
+ int c, z;
+ for (c = 0; c < 3; c++)
{
- return;
+ unsharp_plane_context_t * ctx = &pv->plane_ctx[c];
+ for (int t = 0; t < pv->threads; t++)
+ {
+ unsharp_thread_context_t * tctx = &pv->thread_ctx[t][c];
+ for (z = 0; z < 2 * ctx->steps; z++)
+ {
+ free(tctx->SC[z]);
+ tctx->SC[z] = NULL;
+ }
+ }
}
+ free(pv->thread_ctx);
+}
- int c, z;
- for (c = 0; c < 3; c++)
+static int unsharp_init_thread(hb_filter_object_t *filter, int threads)
+{
+ hb_filter_private_t * pv = filter->private_data;
+
+ unsharp_thread_close(pv);
+ pv->thread_ctx = calloc(threads, sizeof(unsharp_thread_context3_t));
+ pv->threads = threads;
+ for (int c = 0; c < 3; c++)
{
unsharp_plane_context_t * ctx = &pv->plane_ctx[c];
- for (z = 0; z < ctx->steps; z++)
+ int w = hb_image_width(ctx->pix_fmt, ctx->width, c);
+
+ for (int t = 0; t < threads; t++)
{
- free(ctx->SC[z]);
- ctx->SC[z] = NULL;
+ unsharp_thread_context_t * tctx = &pv->thread_ctx[t][c];
+ int z;
+ for (z = 0; z < 2 * ctx->steps; z++)
+ {
+ tctx->SC[z] = malloc(sizeof(*(tctx->SC[z])) *
+ (w + 2 * ctx->steps));
+ if (tctx->SC[z] == NULL)
+ {
+ hb_error("Unsharp calloc failed");
+ unsharp_close(filter);
+ return -1;
+ }
+ }
}
}
+ return 0;
+}
+
+static void unsharp_close(hb_filter_object_t * filter)
+{
+ hb_filter_private_t *pv = filter->private_data;
+
+ if (pv == NULL)
+ {
+ return;
+ }
+ unsharp_thread_close(pv);
free(pv);
filter->private_data = NULL;
}
-static int hb_unsharp_work(hb_filter_object_t *filter,
- hb_buffer_t ** buf_in,
- hb_buffer_t ** buf_out)
+static int unsharp_work_thread(hb_filter_object_t *filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out, int thread)
{
hb_filter_private_t *pv = filter->private_data;
hb_buffer_t *in = *buf_in, *out;
@@ -258,13 +323,14 @@ static int hb_unsharp_work(hb_filter_object_t *filter,
int c;
for (c = 0; c < 3; c++)
{
- unsharp_plane_context_t * ctx = &pv->plane_ctx[c];
- hb_unsharp(in->plane[c].data,
- out->plane[c].data,
- in->plane[c].width,
- in->plane[c].height,
- in->plane[c].stride,
- ctx);
+ unsharp_plane_context_t * ctx = &pv->plane_ctx[c];
+ unsharp_thread_context_t * tctx = &pv->thread_ctx[thread][c];
+ unsharp(in->plane[c].data,
+ out->plane[c].data,
+ in->plane[c].width,
+ in->plane[c].height,
+ in->plane[c].stride,
+ ctx, tctx);
}
out->s = in->s;
@@ -272,3 +338,10 @@ static int hb_unsharp_work(hb_filter_object_t *filter,
return HB_FILTER_OK;
}
+
+static int unsharp_work(hb_filter_object_t *filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out)
+{
+ return unsharp_work_thread(filter, buf_in, buf_out, 0);
+}