summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stebbins <[email protected]>2015-10-24 14:06:56 -0700
committerJohn Stebbins <[email protected]>2016-01-21 12:38:42 -0700
commit10ea76c71197b302b10088d93680a4bed4bc6b8e (patch)
tree459b46b16256c39ed34fe1f0a4b9476ec3439871
parentef956e695879c716dc22c96f7f8fa24e3fa5d08c (diff)
libhb: Add libavfilter support and pad filter
New filter types HB_FILTER_AVFILTER and HB_FILTER_PAD. Settings for HB_FILTER_AVFILTER are the same as you would pass to avconv from the command line -vf option, except that we do not support multi-input or multi-output filters. Settings for HB_FILTER_PAD are "width:height:color:x_offset:y_offset". width x height is the size of the output frame after padding. color may be a w3c color name or RGB value (default black). x_offset, y_offset is the position of the video within the padded area (default centered). Any of the values may be omitted or "auto".
-rw-r--r--contrib/ffmpeg/A10-vf-pad.patch34
-rw-r--r--contrib/ffmpeg/A11-avfilter-framerate.patch352
-rw-r--r--contrib/ffmpeg/module.defs1
-rw-r--r--gtk/configure.ac2
-rw-r--r--libhb/avfilter.c444
-rw-r--r--libhb/common.c145
-rw-r--r--libhb/common.h72
-rw-r--r--libhb/cropscale.c20
-rw-r--r--libhb/grayscale.c11
-rw-r--r--libhb/hb.c2
-rw-r--r--libhb/hb.h1
-rw-r--r--libhb/internal.h12
-rw-r--r--libhb/module.defs2
-rw-r--r--libhb/param.c81
-rw-r--r--libhb/preset.c22
-rw-r--r--libhb/qsv_filter.c25
-rw-r--r--libhb/qsv_filter_pp.c38
-rw-r--r--libhb/rotate.c28
-rw-r--r--libhb/vfr.c33
-rw-r--r--libhb/work.c26
-rw-r--r--test/module.defs4
-rw-r--r--test/test.c315
22 files changed, 1355 insertions, 315 deletions
diff --git a/contrib/ffmpeg/A10-vf-pad.patch b/contrib/ffmpeg/A10-vf-pad.patch
new file mode 100644
index 000000000..85115838f
--- /dev/null
+++ b/contrib/ffmpeg/A10-vf-pad.patch
@@ -0,0 +1,34 @@
+diff --git a/libavfilter/vf_pad.c b/libavfilter/vf_pad.c
+index 634af4c..cddd2a6 100644
+--- a/libavfilter/vf_pad.c
++++ b/libavfilter/vf_pad.c
+@@ -167,12 +167,17 @@ static int config_input(AVFilterLink *inlink)
+ NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
+ goto eval_fail;
+ s->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res;
++ if (!s->h)
++ var_values[VAR_OUT_H] = var_values[VAR_OH] = s->h = inlink->h;
++
+ /* evaluate the width again, as it may depend on the evaluated output height */
+ if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
+ var_names, var_values,
+ NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
+ goto eval_fail;
+ s->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res;
++ if (!s->w)
++ var_values[VAR_OUT_W] = var_values[VAR_OW] = s->w = inlink->w;
+
+ /* evaluate x and y */
+ av_expr_parse_and_eval(&res, (expr = s->x_expr),
+@@ -197,11 +202,6 @@ static int config_input(AVFilterLink *inlink)
+ return AVERROR(EINVAL);
+ }
+
+- if (!s->w)
+- s->w = inlink->w;
+- if (!s->h)
+- s->h = inlink->h;
+-
+ s->w &= ~((1 << s->hsub) - 1);
+ s->h &= ~((1 << s->vsub) - 1);
+ s->x &= ~((1 << s->hsub) - 1);
diff --git a/contrib/ffmpeg/A11-avfilter-framerate.patch b/contrib/ffmpeg/A11-avfilter-framerate.patch
new file mode 100644
index 000000000..146e4fc3c
--- /dev/null
+++ b/contrib/ffmpeg/A11-avfilter-framerate.patch
@@ -0,0 +1,352 @@
+diff --git a/doc/filters.texi b/doc/filters.texi
+index d9874b6..9804c0e 100644
+--- a/doc/filters.texi
++++ b/doc/filters.texi
+@@ -209,6 +209,9 @@ The expression is evaluated through the eval API and can contain the following
+ constants:
+
+ @table @option
++@item FRAME_RATE
++frame rate, only defined for constant frame-rate video
++
+ @item PTS
+ the presentation timestamp in input
+
+diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
+index 64b2645..cd98d16 100644
+--- a/libavfilter/avfilter.c
++++ b/libavfilter/avfilter.c
+@@ -195,6 +195,8 @@ int avfilter_config_links(AVFilterContext *filter)
+ link->src->inputs[0]->sample_aspect_ratio : (AVRational){1,1};
+
+ if (link->src->nb_inputs) {
++ if (!link->frame_rate.num && !link->frame_rate.den)
++ link->frame_rate = link->src->inputs[0]->frame_rate;
+ if (!link->w)
+ link->w = link->src->inputs[0]->w;
+ if (!link->h)
+diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
+index 9dbfeea..0b670e0 100644
+--- a/libavfilter/avfilter.h
++++ b/libavfilter/avfilter.h
+@@ -375,6 +375,18 @@ struct AVFilterLink {
+ AVLINK_STARTINIT, ///< started, but incomplete
+ AVLINK_INIT ///< complete
+ } init_state;
++
++ /**
++ * Frame rate of the stream on the link, or 1/0 if unknown;
++ * if left to 0/0, will be automatically be copied from the first input
++ * of the source filter if it exists.
++ *
++ * Sources should set it to the best estimation of the real frame rate.
++ * Filters should update it if necessary depending on their function.
++ * Sinks can use it to set a default output frame rate.
++ * It is similar to the r_frae_rate field in AVStream.
++ */
++ AVRational frame_rate;
+ };
+
+ /**
+diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
+index a9b893c..ac14d54 100644
+--- a/libavfilter/buffersrc.c
++++ b/libavfilter/buffersrc.c
+@@ -44,6 +44,7 @@ typedef struct BufferSourceContext {
+ const AVClass *class;
+ AVFifoBuffer *fifo;
+ AVRational time_base; ///< time_base to set in the output link
++ AVRational frame_rate; ///< frame_rate to set in the output link
+
+ /* video only */
+ int h, w;
+@@ -191,6 +192,7 @@ static const AVOption video_options[] = {
+ #endif
+ { "sar", "sample aspect ratio", OFFSET(pixel_aspect), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, DBL_MAX, V },
+ { "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V },
++ { "frame_rate", NULL, OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, V },
+ { NULL },
+ };
+
+@@ -308,6 +310,7 @@ static int config_props(AVFilterLink *link)
+ }
+
+ link->time_base = c->time_base;
++ link->frame_rate = c->frame_rate;
+ return 0;
+ }
+
+diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
+index 98bafc2..ab3674a 100644
+--- a/libavfilter/setpts.c
++++ b/libavfilter/setpts.c
+@@ -41,6 +41,7 @@
+
+ static const char *const var_names[] = {
+ "E", ///< Euler number
++ "FRAME_RATE", ///< defined only for constant frame-rate video
+ "INTERLACED", ///< tell if the current frame is interlaced
+ "N", ///< frame / sample number (starting at zero)
+ "PHI", ///< golden ratio
+@@ -59,6 +60,7 @@ static const char *const var_names[] = {
+
+ enum var_name {
+ VAR_E,
++ VAR_FRAME_RATE,
+ VAR_INTERLACED,
+ VAR_N,
+ VAR_PHI,
+@@ -115,6 +117,10 @@ static int config_input(AVFilterLink *inlink)
+ setpts->var_values[VAR_SR] = inlink->sample_rate;
+ }
+
++ setpts->var_values[VAR_FRAME_RATE] = inlink->frame_rate.num &&
++ inlink->frame_rate.den ?
++ av_q2d(inlink->frame_rate) : NAN;
++
+ av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f\n", setpts->var_values[VAR_TB]);
+ return 0;
+ }
+diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c
+index ea22d37..e20f5a0 100644
+--- a/libavfilter/vf_fps.c
++++ b/libavfilter/vf_fps.c
+@@ -117,6 +117,7 @@ static int config_props(AVFilterLink* link)
+ FPSContext *s = link->src->priv;
+
+ link->time_base = (AVRational){ s->framerate.den, s->framerate.num };
++ link->frame_rate= s->framerate;
+ link->w = link->src->inputs[0]->w;
+ link->h = link->src->inputs[0]->h;
+
+diff --git a/libavfilter/vf_framepack.c b/libavfilter/vf_framepack.c
+index e9806ba..10c4add 100644
+--- a/libavfilter/vf_framepack.c
++++ b/libavfilter/vf_framepack.c
+@@ -82,6 +82,7 @@ static int config_output(AVFilterLink *outlink)
+ int width = ctx->inputs[LEFT]->w;
+ int height = ctx->inputs[LEFT]->h;
+ AVRational time_base = ctx->inputs[LEFT]->time_base;
++ AVRational frame_rate = ctx->inputs[LEFT]->frame_rate;
+
+ // check size and fps match on the other input
+ if (width != ctx->inputs[RIGHT]->w ||
+@@ -93,11 +94,18 @@ static int config_output(AVFilterLink *outlink)
+ return AVERROR_INVALIDDATA;
+ } else if (av_cmp_q(time_base, ctx->inputs[RIGHT]->time_base) != 0) {
+ av_log(ctx, AV_LOG_ERROR,
+- "Left and right framerates differ (%d/%d vs %d/%d).\n",
++ "Left and right time bases differ (%d/%d vs %d/%d).\n",
+ time_base.num, time_base.den,
+ ctx->inputs[RIGHT]->time_base.num,
+ ctx->inputs[RIGHT]->time_base.den);
+ return AVERROR_INVALIDDATA;
++ } else if (av_cmp_q(frame_rate, ctx->inputs[RIGHT]->frame_rate) != 0) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Left and right framerates differ (%d/%d vs %d/%d).\n",
++ frame_rate.num, frame_rate.den,
++ ctx->inputs[RIGHT]->frame_rate.num,
++ ctx->inputs[RIGHT]->frame_rate.den);
++ return AVERROR_INVALIDDATA;
+ }
+
+ s->pix_desc = av_pix_fmt_desc_get(outlink->format);
+@@ -108,6 +116,8 @@ static int config_output(AVFilterLink *outlink)
+ switch (s->format) {
+ case AV_STEREO3D_FRAMESEQUENCE:
+ time_base.den *= 2;
++ frame_rate.num *= 2;
++
+ s->double_pts = AV_NOPTS_VALUE;
+ break;
+ case AV_STEREO3D_COLUMNS:
+@@ -126,6 +136,7 @@ static int config_output(AVFilterLink *outlink)
+ outlink->w = width;
+ outlink->h = height;
+ outlink->time_base = time_base;
++ outlink->frame_rate= frame_rate;
+
+ return 0;
+ }
+diff --git a/libavfilter/vf_frei0r.c b/libavfilter/vf_frei0r.c
+index 0122b8d..04f74bc 100644
+--- a/libavfilter/vf_frei0r.c
++++ b/libavfilter/vf_frei0r.c
+@@ -459,6 +459,7 @@ static int source_config_props(AVFilterLink *outlink)
+ outlink->w = s->w;
+ outlink->h = s->h;
+ outlink->time_base = s->time_base;
++ outlink->frame_rate = av_inv_q(s->time_base);
+
+ if (s->destruct && s->instance)
+ s->destruct(s->instance);
+diff --git a/libavfilter/vf_interlace.c b/libavfilter/vf_interlace.c
+index 8ef58e4..939fabc 100644
+--- a/libavfilter/vf_interlace.c
++++ b/libavfilter/vf_interlace.c
+@@ -109,8 +109,10 @@ static int config_out_props(AVFilterLink *outlink)
+ outlink->w = inlink->w;
+ outlink->h = inlink->h;
+ outlink->time_base = inlink->time_base;
++ outlink->frame_rate = inlink->frame_rate;
+ // half framerate
+ outlink->time_base.num *= 2;
++ outlink->frame_rate.den *= 2;
+
+
+ if (s->lowpass) {
+diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
+index ede1765..9b60408 100644
+--- a/libavfilter/vf_showinfo.c
++++ b/libavfilter/vf_showinfo.c
+@@ -140,12 +140,37 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+ return ff_filter_frame(inlink->dst->outputs[0], frame);
+ }
+
++static int config_props(AVFilterContext *ctx, AVFilterLink *link, int is_out)
++{
++
++ av_log(ctx, AV_LOG_INFO, "config %s time_base: %d/%d, frame_rate: %d/%d\n",
++ is_out ? "out" :"in",
++ link->time_base.num, link->time_base.den,
++ link->frame_rate.num, link->frame_rate.den
++ );
++
++ return 0;
++}
++
++static int config_props_in(AVFilterLink *link)
++{
++ AVFilterContext *ctx = link->dst;
++ return config_props(ctx, link, 0);
++}
++
++static int config_props_out(AVFilterLink *link)
++{
++ AVFilterContext *ctx = link->src;
++ return config_props(ctx, link, 1);
++}
++
+ static const AVFilterPad avfilter_vf_showinfo_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .get_video_buffer = ff_null_get_video_buffer,
+ .filter_frame = filter_frame,
++ .config_props = config_props_in,
+ },
+ { NULL }
+ };
+@@ -153,7 +178,8 @@ static const AVFilterPad avfilter_vf_showinfo_inputs[] = {
+ static const AVFilterPad avfilter_vf_showinfo_outputs[] = {
+ {
+ .name = "default",
+- .type = AVMEDIA_TYPE_VIDEO
++ .type = AVMEDIA_TYPE_VIDEO,
++ .config_props = config_props_out,
+ },
+ { NULL }
+ };
+diff --git a/libavfilter/vf_yadif.c b/libavfilter/vf_yadif.c
+index 574eac4..62576f7 100644
+--- a/libavfilter/vf_yadif.c
++++ b/libavfilter/vf_yadif.c
+@@ -462,6 +462,9 @@ static int config_props(AVFilterLink *link)
+ link->w = link->src->inputs[0]->w;
+ link->h = link->src->inputs[0]->h;
+
++ if(s->mode&1)
++ link->frame_rate = av_mul_q(link->src->inputs[0]->frame_rate, (AVRational){2,1});
++
+ s->csp = av_pix_fmt_desc_get(link->format);
+ if (s->csp->comp[0].depth > 8) {
+ s->filter_line = filter_line_c_16bit;
+diff --git a/libavfilter/vsrc_color.c b/libavfilter/vsrc_color.c
+index 3b506ec..644fd8b 100644
+--- a/libavfilter/vsrc_color.c
++++ b/libavfilter/vsrc_color.c
+@@ -138,6 +138,7 @@ static int color_config_props(AVFilterLink *inlink)
+ inlink->w = color->w;
+ inlink->h = color->h;
+ inlink->time_base = color->time_base;
++ inlink->frame_rate = av_inv_q(color->time_base);
+
+ return 0;
+ }
+diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c
+index e41625e..b6be2ea 100644
+--- a/libavfilter/vsrc_testsrc.c
++++ b/libavfilter/vsrc_testsrc.c
+@@ -46,10 +46,10 @@ typedef struct TestSourceContext {
+ const AVClass *class;
+ int h, w;
+ unsigned int nb_frame;
+- AVRational time_base;
++ AVRational time_base, frame_rate;
+ int64_t pts, max_pts;
+ char *size; ///< video frame size
+- char *rate; ///< video frame rate
++ char *frame_rate_str; ///< video frame rate
+ char *duration; ///< total duration of the generated video
+ AVRational sar; ///< sample aspect ratio
+
+@@ -65,8 +65,8 @@ typedef struct TestSourceContext {
+ static const AVOption testsrc_options[] = {
+ { "size", "set video size", OFFSET(size), AV_OPT_TYPE_STRING, {.str = "320x240"}, .flags = FLAGS },
+ { "s", "set video size", OFFSET(size), AV_OPT_TYPE_STRING, {.str = "320x240"}, .flags = FLAGS },
+- { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, .flags = FLAGS },
+- { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, .flags = FLAGS },
++ { "rate", "set video rate", OFFSET(frame_rate_str), AV_OPT_TYPE_STRING, {.str = "25"}, .flags = FLAGS },
++ { "r", "set video rate", OFFSET(frame_rate_str), AV_OPT_TYPE_STRING, {.str = "25"}, .flags = FLAGS },
+ { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
+ { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, {.dbl = 1}, 0, INT_MAX, FLAGS },
+ { NULL },
+@@ -75,7 +75,6 @@ static const AVOption testsrc_options[] = {
+ static av_cold int init_common(AVFilterContext *ctx)
+ {
+ TestSourceContext *test = ctx->priv;
+- AVRational frame_rate_q;
+ int64_t duration = -1;
+ int ret = 0;
+
+@@ -84,9 +83,9 @@ static av_cold int init_common(AVFilterContext *ctx)
+ return ret;
+ }
+
+- if ((ret = av_parse_video_rate(&frame_rate_q, test->rate)) < 0 ||
+- frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
+- av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", test->rate);
++ if ((ret = av_parse_video_rate(&test->frame_rate, test->frame_rate_str)) < 0 ||
++ test->frame_rate.den <= 0 || test->frame_rate.num <= 0) {
++ av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", test->frame_rate_str);
+ return ret;
+ }
+
+@@ -95,15 +94,14 @@ static av_cold int init_common(AVFilterContext *ctx)
+ return ret;
+ }
+
+- test->time_base.num = frame_rate_q.den;
+- test->time_base.den = frame_rate_q.num;
++ test->time_base = av_inv_q(test->frame_rate);
+ test->max_pts = duration >= 0 ?
+ av_rescale_q(duration, AV_TIME_BASE_Q, test->time_base) : -1;
+ test->nb_frame = 0;
+ test->pts = 0;
+
+ av_log(ctx, AV_LOG_DEBUG, "size:%dx%d rate:%d/%d duration:%f sar:%d/%d\n",
+- test->w, test->h, frame_rate_q.num, frame_rate_q.den,
++ test->w, test->h, test->frame_rate.num, test->frame_rate.den,
+ duration < 0 ? -1 : test->max_pts * av_q2d(test->time_base),
+ test->sar.num, test->sar.den);
+ return 0;
+@@ -116,7 +114,8 @@ static int config_props(AVFilterLink *outlink)
+ outlink->w = test->w;
+ outlink->h = test->h;
+ outlink->sample_aspect_ratio = test->sar;
+- outlink->time_base = test->time_base;
++ outlink->frame_rate = test->frame_rate;
++ outlink->time_base = test->time_base;
+
+ return 0;
+ }
diff --git a/contrib/ffmpeg/module.defs b/contrib/ffmpeg/module.defs
index 9c9c8d4ac..682ecb575 100644
--- a/contrib/ffmpeg/module.defs
+++ b/contrib/ffmpeg/module.defs
@@ -22,7 +22,6 @@ FFMPEG.CONFIGURE.extra = \
--disable-avplay \
--disable-avprobe \
--disable-avdevice \
- --disable-avfilter \
--disable-muxers \
--disable-network \
--disable-hwaccels \
diff --git a/gtk/configure.ac b/gtk/configure.ac
index 0cf85a8b1..42a4ed581 100644
--- a/gtk/configure.ac
+++ b/gtk/configure.ac
@@ -157,7 +157,7 @@ PKG_CHECK_MODULES(GHB, [$GHB_PACKAGES])
GHB_CFLAGS="$HBINC $GHB_CFLAGS"
-HB_LIBS="-lhandbrake -lavresample -lavformat -lavcodec -lavutil -ldvdnav -ldvdread -lmp3lame -lvorbis -lvorbisenc -logg -lsamplerate -lx264 -lswscale -ltheoraenc -ltheoradec -lvpx -lz -lbz2 -lbluray -lass -lfontconfig -lfreetype -lxml2 -ljansson"
+HB_LIBS="-lhandbrake -lavresample -lavformat -lavcodec -lavfilter -lavutil -ldvdnav -ldvdread -lmp3lame -lvorbis -lvorbisenc -logg -lsamplerate -lx264 -lswscale -ltheoraenc -ltheoradec -lvpx -lz -lbz2 -lbluray -lass -lfontconfig -lfreetype -lxml2 -ljansson"
case $host in
*-*-mingw*)
diff --git a/libhb/avfilter.c b/libhb/avfilter.c
new file mode 100644
index 000000000..a58f6009e
--- /dev/null
+++ b/libhb/avfilter.c
@@ -0,0 +1,444 @@
+/* avfilter.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 "common.h"
+#include "libavfilter/avfilter.h"
+#include "libavfilter/buffersrc.h"
+#include "libavfilter/buffersink.h"
+
+
+/*****
+ * This is a meta-filter. By itself it makes no modifications to the video.
+ * Other filters use this filter to perform their function (see pad.c)
+ */
+struct hb_filter_private_s
+{
+ hb_buffer_list_t list;
+ AVFilterGraph * graph;
+ AVFilterContext * last;
+ AVFilterContext * input;
+ AVFilterContext * output;
+ AVFrame * frame;
+ AVRational out_time_base;
+ char * settings;
+};
+
+static int avfilter_init( hb_filter_object_t * filter,
+ hb_filter_init_t * init );
+static void avfilter_close( hb_filter_object_t * filter );
+static int avfilter_work( hb_filter_object_t * filter,
+ hb_buffer_t ** buf_in, hb_buffer_t ** buf_out );
+static hb_filter_info_t * avfilter_info( hb_filter_object_t * filter );
+
+hb_filter_object_t hb_filter_avfilter =
+{
+ .id = HB_FILTER_AVFILTER,
+ .enforce_order = 0,
+ .name = "avfilter",
+ .settings = NULL,
+ .init = avfilter_init,
+ .work = avfilter_work,
+ .close = avfilter_close,
+ .info = avfilter_info,
+};
+
+hb_filter_object_t hb_filter_pad =
+{
+ .id = HB_FILTER_PAD,
+ .enforce_order = 1,
+ .name = "avfilter",
+ .settings = NULL,
+ .init = avfilter_init,
+ .work = avfilter_work,
+ .close = avfilter_close,
+ .info = avfilter_info,
+};
+
+static AVFilterContext * append_filter( hb_filter_private_t * pv,
+ const char * name, const char * args)
+{
+ AVFilterContext * filter;
+ int result;
+
+ result = avfilter_graph_create_filter(&filter, avfilter_get_by_name(name),
+ name, args, NULL, pv->graph);
+ if (result < 0)
+ {
+ return NULL;
+ }
+ if (pv->last != NULL)
+ {
+ result = avfilter_link(pv->last, 0, filter, 0);
+ if (result < 0)
+ {
+ avfilter_free(filter);
+ return NULL;
+ }
+ }
+ pv->last = filter;
+
+ return filter;
+}
+
+static int avfilter_init( hb_filter_object_t * filter, hb_filter_init_t * init )
+{
+ hb_filter_private_t * pv = filter->private_data;
+ char * sws_flags;
+ AVFilterContext * avfilter;
+ char * filter_args;
+ int result;
+ AVFilterInOut * in = NULL, * out = NULL;
+
+ if (filter->settings == NULL || filter->settings[0] == 0)
+ {
+ hb_error("avfilter_init: no filter settings specified");
+ return 1;
+ }
+
+ pv = calloc(1, sizeof(struct hb_filter_private_s));
+ filter->private_data = pv;
+
+ pv->settings = strdup(filter->settings);
+ pv->graph = avfilter_graph_alloc();
+ if (pv->graph == NULL)
+ {
+ hb_error("avfilter_init: avfilter_graph_alloc failed");
+ goto fail;
+ }
+
+ sws_flags = hb_strdup_printf("flags=%d", SWS_LANCZOS|SWS_ACCURATE_RND);
+ pv->graph->scale_sws_opts = sws_flags;
+
+ result = avfilter_graph_parse2(pv->graph, filter->settings, &in, &out);
+ if (result < 0 || in == NULL || out == NULL)
+ {
+ hb_error("avfilter_init: avfilter_graph_parse2 failed (%s)",
+ filter->settings);
+ goto fail;
+ }
+
+ // Build filter input
+ filter_args = hb_strdup_printf(
+ "width=%d:height=%d:pix_fmt=%d:sar=%d/%d:"
+ "time_base=%d/%d:frame_rate=%d/%d",
+ init->geometry.width, init->geometry.height, init->pix_fmt,
+ init->geometry.par.num, init->geometry.par.den,
+ 1, 90000, init->vrate.num, init->vrate.den);
+
+ avfilter = append_filter(pv, "buffer", filter_args);
+ free(filter_args);
+ if (avfilter == NULL)
+ {
+ hb_error("avfilter_init: failed to create buffer source filter");
+ goto fail;
+ }
+ pv->input = avfilter;
+
+ avfilter = append_filter(pv, "format", "yuv420p");
+ if (avfilter == NULL)
+ {
+ hb_error("avfilter_init: failed to create pix format filter");
+ goto fail;
+ }
+
+ // Link input to filter chain created by avfilter_graph_parse2
+ result = avfilter_link(pv->last, 0, in->filter_ctx, 0);
+ if (result < 0)
+ {
+ goto fail;
+ }
+ pv->last = out->filter_ctx;
+
+ // Build filter output
+ avfilter = append_filter(pv, "buffersink", NULL);
+ if (avfilter == NULL)
+ {
+ hb_error("avfilter_init: failed to create buffer output filter");
+ goto fail;
+ }
+ pv->output = avfilter;
+
+ result = avfilter_graph_config(pv->graph, NULL);
+ if (result < 0)
+ {
+ hb_error("avfilter_init: failed to configure filter graph");
+ goto fail;
+ }
+
+ pv->frame = av_frame_alloc();
+ if (pv->frame == NULL)
+ {
+ hb_error("avfilter_init: failed to allocate frame filter");
+ goto fail;
+ }
+
+ avfilter_inout_free(&in);
+ avfilter_inout_free(&out);
+
+ // Retrieve the parameters of the output filter
+ AVFilterLink *link = pv->output->inputs[0];
+ init->geometry.width = link->w;
+ init->geometry.height = link->h;
+ init->geometry.par.num = link->sample_aspect_ratio.num;
+ init->geometry.par.den = link->sample_aspect_ratio.den;
+ init->pix_fmt = link->format;
+ // avfilter can generate "unknown" framerates. If this happens
+ // just pass along the source framerate.
+ if (link->frame_rate.num > 0 && link->frame_rate.den > 0)
+ {
+ init->vrate.num = link->frame_rate.num;
+ init->vrate.den = link->frame_rate.den;
+ }
+ pv->out_time_base = link->time_base;
+
+ hb_buffer_list_clear(&pv->list);
+
+ return 0;
+
+fail:
+ if (pv->input != NULL)
+ avfilter_free(pv->input);
+ if (pv->output != NULL)
+ avfilter_free(pv->output);
+ avfilter_inout_free(&in);
+ avfilter_inout_free(&out);
+ avfilter_graph_free(&pv->graph);
+ free(pv);
+
+ return 1;
+}
+
+static hb_filter_info_t * avfilter_info( hb_filter_object_t * filter )
+{
+ hb_filter_private_t * pv = filter->private_data;
+ hb_filter_info_t * info;
+
+ if( !pv )
+ return NULL;
+
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(1024);
+ info->human_readable_desc[0] = 0;
+
+ char * dst = info->human_readable_desc;
+ char * start = pv->settings;
+ while (start != NULL && *start != 0)
+ {
+ // Find end of a filter
+ char * comma = strchr(start, ',');
+ char * quote = strchr(start, '\'');
+ if (comma != NULL && quote != NULL && quote < comma)
+ {
+ // Find end of quote
+ quote = strchr(quote+1, '\'');
+ comma = strchr(start, ',');
+ }
+ // pretty print line
+ int name = 1;
+ while (*start != 0 && (comma == NULL || start < comma))
+ {
+ switch (*start)
+ {
+ case '=':
+ if (name)
+ {
+ *dst++ = ':';
+ *dst++ = ' ';
+ name = 0;
+ }
+ else
+ {
+ *dst++ = '=';
+ }
+ break;
+
+ case ':':
+ *dst++ = ',';
+ *dst++ = ' ';
+ break;
+
+ case '\'':
+ case ' ':
+ break;
+
+ default:
+ *dst++ = *start;
+
+ }
+ start++;
+ }
+ if (*start != 0)
+ {
+ *dst++ = '\n';
+ start++;
+ }
+ }
+ *dst = 0;
+ return info;
+}
+
+static void avfilter_close( hb_filter_object_t * filter )
+{
+ hb_filter_private_t * pv = filter->private_data;
+ if (pv == NULL)
+ {
+ // Already closed
+ return;
+ }
+
+ av_frame_free(&pv->frame);
+ avfilter_graph_free(&pv->graph);
+ free(pv->settings);
+ free(pv);
+ filter->private_data = NULL;
+}
+
+static void fill_frame(hb_filter_private_t * pv,
+ AVFrame * frame, hb_buffer_t * buf)
+{
+ frame->data[0] = buf->plane[0].data;
+ frame->data[1] = buf->plane[1].data;
+ frame->data[2] = buf->plane[2].data;
+ frame->linesize[0] = buf->plane[0].stride;
+ frame->linesize[1] = buf->plane[1].stride;
+ frame->linesize[2] = buf->plane[2].stride;
+
+ frame->pts = buf->s.start;
+ frame->reordered_opaque = buf->s.start;
+ frame->width = buf->f.width;
+ frame->height = buf->f.height;
+ frame->format = buf->f.fmt;
+}
+
+static hb_buffer_t* avframe_to_buffer(hb_filter_private_t * pv, AVFrame *frame)
+{
+ hb_buffer_t * buf;
+
+ buf = hb_frame_buffer_init(frame->format, frame->width, frame->height);
+ if (buf == NULL)
+ {
+ return NULL;
+ }
+
+ int pp;
+ for (pp = 0; pp < 3; pp++)
+ {
+ int yy;
+ int width = buf->plane[pp].width;
+ int stride = buf->plane[pp].stride;
+ int height = buf->plane[pp].height;
+ int linesize = frame->linesize[pp];
+ uint8_t * dst = buf->plane[pp].data;
+ uint8_t * src = frame->data[pp];
+
+ for (yy = 0; yy < height; yy++)
+ {
+ memcpy(dst, src, width);
+ dst += stride;
+ src += linesize;
+ }
+ }
+ buf->s.start = av_rescale_q(frame->pts, pv->out_time_base,
+ (AVRational){1, 90000});
+
+ return buf;
+}
+
+static hb_buffer_t* filterFrame( hb_filter_private_t * pv, hb_buffer_t * in )
+{
+ int result;
+ hb_buffer_list_t list;
+
+ fill_frame(pv, pv->frame, in);
+ result = av_buffersrc_add_frame(pv->input, pv->frame);
+ if (result < 0)
+ {
+ return NULL;
+ }
+
+ hb_buffer_list_clear(&list);
+ result = av_buffersink_get_frame(pv->output, pv->frame);
+ while (result >= 0)
+ {
+ hb_buffer_t * buf = avframe_to_buffer(pv, pv->frame);
+ hb_buffer_list_append(&pv->list, buf);
+ av_frame_unref(pv->frame);
+
+ result = av_buffersink_get_frame(pv->output, pv->frame);
+ }
+ while (hb_buffer_list_count(&pv->list) > 1)
+ {
+ hb_buffer_t * buf = hb_buffer_list_rem_head(&pv->list);
+ hb_buffer_t * next = hb_buffer_list_head(&pv->list);
+
+ buf->s.stop = next->s.start;
+ hb_buffer_list_append(&list, buf);
+ }
+
+ return hb_buffer_list_head(&list);
+}
+
+static int avfilter_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->s.flags & HB_BUF_FLAG_EOF)
+ {
+ hb_buffer_list_append(&pv->list, in);
+ *buf_out = hb_buffer_list_clear(&pv->list);
+ *buf_in = NULL;
+ return HB_FILTER_DONE;
+ }
+
+ *buf_out = filterFrame(pv, in);
+
+ return HB_FILTER_OK;
+}
+
+void hb_avfilter_combine( hb_list_t * list )
+{
+ hb_filter_object_t * avfilter = NULL;
+ int ii;
+
+ for (ii = 0; ii < hb_list_count(list);)
+ {
+ hb_filter_object_t * filter = hb_list_item(list, ii);
+ switch (filter->id)
+ {
+ case HB_FILTER_AVFILTER:
+ case HB_FILTER_PAD:
+ if (avfilter != NULL)
+ {
+ // Chain filter together
+ char * settings;
+ settings = hb_strdup_printf("%s, %s", avfilter->settings,
+ filter->settings);
+ free(avfilter->settings);
+ avfilter->settings = settings;
+ hb_list_rem(list, filter);
+ hb_filter_close(&filter);
+ continue;
+ }
+ else
+ {
+ avfilter = filter;
+ avfilter->id = HB_FILTER_AVFILTER;
+ }
+ break;
+ default:
+ avfilter = NULL;
+ }
+ ii++;
+ }
+}
+
diff --git a/libhb/common.c b/libhb/common.c
index f33a4abca..62826a654 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -3765,6 +3765,14 @@ hb_filter_object_t * hb_filter_init( int filter_id )
filter = &hb_filter_crop_scale;
break;
+ case HB_FILTER_AVFILTER:
+ filter = &hb_filter_avfilter;
+ break;
+
+ case HB_FILTER_PAD:
+ filter = &hb_filter_pad;
+ break;
+
case HB_FILTER_ROTATE:
filter = &hb_filter_rotate;
break;
@@ -3803,14 +3811,28 @@ void hb_filter_close( hb_filter_object_t ** _f )
{
hb_filter_object_t * f = *_f;
- if( f->settings )
- free( f->settings );
+ free(f->settings);
free( f );
*_f = NULL;
}
/**********************************************************************
+ * hb_filter_info_close
+ **********************************************************************
+ *
+ *********************************************************************/
+void hb_filter_info_close( hb_filter_info_t ** _fi )
+{
+ hb_filter_info_t * fi = *_fi;
+
+ free(fi->human_readable_desc);
+
+ free( fi );
+ *_fi = NULL;
+}
+
+/**********************************************************************
* hb_chapter_copy
**********************************************************************
*
@@ -4843,3 +4865,122 @@ void hb_hexdump( hb_debug_level_t level, const char * label, const uint8_t * dat
hb_deep_log( level, " %-50s%20s", line, ascii );
}
}
+
+int hb_str_vlen(char **strv)
+{
+ int i;
+ if (strv == NULL)
+ return 0;
+ for (i = 0; strv[i]; i++);
+ return i;
+}
+
+static const char* strchr_quote(const char *pos, char c, char q)
+{
+ if (pos == NULL)
+ return NULL;
+
+ while (*pos != 0 && *pos != c)
+ {
+ if (*pos == q)
+ {
+ pos = strchr_quote(pos+1, q, 0);
+ if (pos == NULL)
+ return NULL;
+ pos++;
+ }
+ else if (*pos == '\\' && *(pos+1) != 0)
+ pos += 2;
+ else
+ pos++;
+ }
+ if (*pos != c)
+ return NULL;
+ return pos;
+}
+
+static char *strndup_quote(const char *str, char q, int len)
+{
+ if (str == NULL)
+ return NULL;
+
+ char * res;
+ int str_len = strlen( str );
+ int src = 0, dst = 0;
+ res = malloc( len > str_len ? str_len + 1 : len + 1 );
+ if ( res == NULL ) return res;
+
+ while (str[src] != 0 && src < len)
+ {
+ if (str[src] == q)
+ src++;
+ else if (str[src] == '\\' && str[src+1] != 0)
+ {
+ res[dst++] = str[src+1];
+ src += 2;
+ }
+ else
+ res[dst++] = str[src++];
+ }
+ res[dst] = '\0';
+ return res;
+}
+
+char** hb_str_vsplit( const char *str, char delem )
+{
+ const char * pos;
+ const char * end;
+ char ** ret;
+ int count, i;
+ char quote = '"';
+
+ if (delem == '"')
+ {
+ quote = '\'';
+ }
+ if ( str == NULL || str[0] == 0 )
+ {
+ ret = malloc( sizeof(char*) );
+ if ( ret == NULL ) return ret;
+ *ret = NULL;
+ return ret;
+ }
+
+ // Find number of elements in the string
+ count = 1;
+ pos = str;
+ while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL )
+ {
+ count++;
+ pos++;
+ }
+
+ ret = calloc( ( count + 1 ), sizeof(char*) );
+ if ( ret == NULL ) return ret;
+
+ pos = str;
+ for ( i = 0; i < count - 1; i++ )
+ {
+ end = strchr_quote( pos, delem, quote );
+ ret[i] = strndup_quote(pos, quote, end - pos);
+ pos = end + 1;
+ }
+ ret[i] = strndup_quote(pos, quote, strlen(pos));
+
+ return ret;
+}
+
+void hb_str_vfree( char **strv )
+{
+ int i;
+
+ if (strv == NULL)
+ return;
+
+ for ( i = 0; strv[i]; i++ )
+ {
+ free( strv[i] );
+ }
+ free( strv );
+}
+
diff --git a/libhb/common.h b/libhb/common.h
index 15f847095..daeeb7920 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -1203,53 +1203,51 @@ extern hb_work_object_t hb_reader;
typedef struct hb_filter_init_s
{
- hb_job_t * job;
- int pix_fmt;
- hb_geometry_t geometry;
- int crop[4];
- hb_rational_t vrate;
- int cfr;
- int grayscale;
+ hb_job_t * job;
+ int pix_fmt;
+ hb_geometry_t geometry;
+ int crop[4];
+ hb_rational_t vrate;
+ int cfr;
+ int grayscale;
} hb_filter_init_t;
typedef struct hb_filter_info_s
{
- char human_readable_desc[128];
+ char * human_readable_desc;
hb_filter_init_t out;
} hb_filter_info_t;
struct hb_filter_object_s
{
- int id;
- int enforce_order;
- char * name;
- char * settings;
+ int id;
+ int enforce_order;
+ char * name;
+ char * 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 ** );
+ 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 * );
- void (* close) ( hb_filter_object_t * );
- int (* info) ( hb_filter_object_t *, hb_filter_info_t * );
+ hb_fifo_t * fifo_in;
+ hb_fifo_t * fifo_out;
- hb_fifo_t * fifo_in;
- hb_fifo_t * fifo_out;
-
- hb_subtitle_t * subtitle;
+ hb_subtitle_t * subtitle;
hb_filter_private_t * private_data;
- hb_thread_t * thread;
- volatile int * done;
- int status;
+ hb_thread_t * thread;
+ volatile int * done;
+ int status;
// Filters can drop frames and thus chapter marks
// These are used to bridge the chapter to the next buffer
- int chapter_val;
- int64_t chapter_time;
+ int chapter_val;
+ int64_t chapter_time;
#endif
};
@@ -1273,11 +1271,13 @@ enum
HB_FILTER_NLMEANS,
HB_FILTER_RENDER_SUB,
HB_FILTER_CROP_SCALE,
+ HB_FILTER_ROTATE,
+ HB_FILTER_GRAYSCALE,
+ HB_FILTER_PAD,
// 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,
+ HB_FILTER_AVFILTER,
// for QSV - important to have as a last one
HB_FILTER_QSV_POST,
@@ -1288,8 +1288,9 @@ enum
hb_filter_object_t * hb_filter_init( int filter_id );
hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter );
-hb_list_t *hb_filter_list_copy(const hb_list_t *src);
-void hb_filter_close( hb_filter_object_t ** );
+hb_list_t * hb_filter_list_copy(const hb_list_t *src);
+void hb_filter_close( hb_filter_object_t ** );
+void hb_filter_info_close( hb_filter_info_t ** );
typedef void hb_error_handler_t( const char *errmsg );
@@ -1299,6 +1300,13 @@ char * hb_strdup_vaprintf( const char * fmt, va_list args );
char * hb_strdup_printf(const char *fmt, ...) HB_WPRINTF(1, 2);
char * hb_strncat_dup( const char * s1, const char * s2, size_t n );
+// free array of strings
+void hb_str_vfree( char ** strv );
+// count number of strings in array of strings
+int hb_str_vlen(char ** strv);
+// split string into array of strings
+char ** hb_str_vsplit( const char * str, char delem );
+
int hb_yuv2rgb(int yuv);
int hb_rgb2yuv(int rgb);
diff --git a/libhb/cropscale.c b/libhb/cropscale.c
index 5499156ec..4c505ff20 100644
--- a/libhb/cropscale.c
+++ b/libhb/cropscale.c
@@ -39,8 +39,7 @@ 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 hb_filter_info_t * hb_crop_scale_info( hb_filter_object_t * filter );
static void hb_crop_scale_close( hb_filter_object_t * filter );
@@ -98,17 +97,18 @@ static int hb_crop_scale_init( hb_filter_object_t * filter,
return 0;
}
-static int hb_crop_scale_info( hb_filter_object_t * filter,
- hb_filter_info_t * info )
+static hb_filter_info_t * hb_crop_scale_info( hb_filter_object_t * filter )
{
hb_filter_private_t * pv = filter->private_data;
+ hb_filter_info_t * info;
if( !pv )
- return 0;
+ return NULL;
+
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(128);
+ info->human_readable_desc[0] = 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.geometry.width = pv->width_out;
info->out.geometry.height = pv->height_out;
@@ -117,13 +117,13 @@ static int hb_crop_scale_info( hb_filter_object_t * filter,
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,
+ snprintf( info->human_readable_desc, 128,
"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;
+ return info;
}
static void hb_crop_scale_close( hb_filter_object_t * filter )
diff --git a/libhb/grayscale.c b/libhb/grayscale.c
index 5942d43c5..195c5889b 100644
--- a/libhb/grayscale.c
+++ b/libhb/grayscale.c
@@ -37,13 +37,12 @@ static int hb_grayscale_work( hb_filter_object_t * filter,
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 );
+static hb_filter_info_t * hb_grayscale_info( hb_filter_object_t * filter );
hb_filter_object_t hb_filter_grayscale =
{
.id = HB_FILTER_GRAYSCALE,
- .enforce_order = 0,
+ .enforce_order = 1,
.name = "Grayscale",
.settings = NULL,
.init = hb_grayscale_init,
@@ -207,11 +206,9 @@ static int hb_grayscale_init( hb_filter_object_t * filter,
return 0;
}
-static int hb_grayscale_info( hb_filter_object_t * filter,
- hb_filter_info_t * info )
+static hb_filter_info_t * hb_grayscale_info( hb_filter_object_t * filter )
{
- info->human_readable_desc[0] = 0;
- return 0;
+ return NULL;
}
static void hb_grayscale_close( hb_filter_object_t * filter )
diff --git a/libhb/hb.c b/libhb/hb.c
index 41347f613..88d90e187 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -11,6 +11,7 @@
#include "opencl.h"
#include "hbffmpeg.h"
#include "encx264.h"
+#include "libavfilter/avfilter.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
@@ -128,6 +129,7 @@ void hb_avcodec_init()
{
av_lockmgr_register(ff_lockmgr_cb);
av_register_all();
+ avfilter_register_all();
#ifdef _WIN64
// avresample's assembly optimizations can cause crashes under Win x86_64
// (see http://bugzilla.libav.org/show_bug.cgi?id=496)
diff --git a/libhb/hb.h b/libhb/hb.h
index d000a0e06..3521e4d3e 100644
--- a/libhb/hb.h
+++ b/libhb/hb.h
@@ -22,6 +22,7 @@ extern "C" {
#include "preset.h"
#include "plist.h"
#include "param.h"
+#include "colormap.h"
/* hb_init()
Initializes a libhb session (launches his own thread, detects CPUs,
diff --git a/libhb/internal.h b/libhb/internal.h
index 8fe461dac..a4c0eb5b0 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -460,16 +460,18 @@ enum
};
extern hb_filter_object_t hb_filter_detelecine;
+extern hb_filter_object_t hb_filter_decomb;
extern hb_filter_object_t hb_filter_deinterlace;
+extern hb_filter_object_t hb_filter_vfr;
extern hb_filter_object_t hb_filter_deblock;
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_render_sub;
+extern hb_filter_object_t hb_filter_crop_scale;
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;
+extern hb_filter_object_t hb_filter_pad;
+extern hb_filter_object_t hb_filter_avfilter;
#ifdef USE_QSV
extern hb_filter_object_t hb_filter_qsv;
@@ -508,3 +510,5 @@ void hb_muxmp4_process_subtitle_style( uint8_t *input,
uint8_t *style, uint16_t *stylesize );
void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src);
+void hb_avfilter_combine( hb_list_t * list );
+
diff --git a/libhb/module.defs b/libhb/module.defs
index f98930dd5..612177e0d 100644
--- a/libhb/module.defs
+++ b/libhb/module.defs
@@ -121,7 +121,7 @@ LIBHB.dll = $(LIBHB.build/)hb.dll
LIBHB.lib = $(LIBHB.build/)hb.lib
LIBHB.dll.libs = $(foreach n, \
- ass avcodec avformat avutil avresample dvdnav dvdread fontconfig \
+ ass avcodec avformat avfilter avutil avresample dvdnav dvdread fontconfig \
freetype mp3lame ogg samplerate swscale vpx theora vorbis vorbisenc \
x264 xml2 bluray jansson, \
$(CONTRIB.build/)lib/lib$(n).a )
diff --git a/libhb/param.c b/libhb/param.c
index 998adb0e9..f56f3957b 100644
--- a/libhb/param.c
+++ b/libhb/param.c
@@ -10,6 +10,7 @@
#include "param.h"
#include "common.h"
+#include "colormap.h"
#include <regex.h>
const char hb_filter_off[] = "off";
@@ -103,6 +104,76 @@ static filter_param_map_t param_map[] =
{ HB_FILTER_INVALID, NULL, NULL, 0 }
};
+/* Pad presets and tunes
+ *
+ * There are currently no presets and tunes for pad
+ * The custom pad string is converted to an avformat filter graph string
+ */
+char *
+generate_pad_settings(const char * preset, const char * tune)
+{
+ int width = 0, height = 0, rgb = 0;
+ int x = -1, y = -1, ii;
+ char ** args;
+ char * result;
+
+ args = hb_str_vsplit(preset, ':');
+ for (ii = 0; ii < 5 && args[ii]; ii++)
+ {
+ if (args[ii][0] == 0 || !strcasecmp("auto", args[ii]))
+ continue;
+ switch (ii)
+ {
+ case 0:
+ width = strtol(args[ii], &result, 0);
+ break;
+ case 1:
+ height = strtol(args[ii], &result, 0);
+ break;
+ case 2:
+ rgb = strtol(args[ii], &result, 0);
+ if (result == args[ii])
+ {
+ // Not a numeric value, lookup by name
+ rgb = hb_rgb_lookup_by_name(args[2]);
+ }
+ break;
+ case 3:
+ x = strtol(args[ii], &result, 0);
+ break;
+ case 4:
+ y = strtol(args[ii], &result, 0);
+ break;
+ default:
+ break;
+ }
+ }
+ hb_str_vfree(args);
+
+ char x_str[20];
+ char y_str[20];
+ if (x < 0)
+ {
+ snprintf(x_str, 20, "(out_w-in_w)/2");
+ }
+ else
+ {
+ snprintf(x_str, 20, "%d", x);
+ }
+ if (y < 0)
+ {
+ snprintf(y_str, 20, "(out_h-in_h)/2");
+ }
+ else
+ {
+ snprintf(y_str, 20, "%d", y);
+ }
+ result = hb_strdup_printf(
+ "pad='width=%d:height=%d:x=%s:y=%s:color=0x%06x'",
+ width, height, x_str, y_str, rgb);
+ return result;
+}
+
/* NL-means presets and tunes
*
* Presets adjust strength:
@@ -311,11 +382,15 @@ int hb_validate_filter_settings(int filter_id, const char *filter_param)
// Regex matches "number" followed by one or more ":number", where number is int or float
const char *hb_colon_separated_params_regex = "^(((([\\-])?[0-9]+([.,][0-9]+)?)|(([\\-])?[.,][0-9]+))((:((([\\-])?[0-9]+([,.][0-9]+)?)|(([\\-])?[,.][0-9]+)))+)?)$";
+ const char *hb_pad_regex = "^([0-9]*|auto)(:([0-9]*|auto)(:([a-zA-Z0-9]*|auto)(:([0-9]*|auto)(:([0-9]*|auto))?)?)?)?$";
const char *regex_pattern = NULL;
switch (filter_id)
{
+ case HB_FILTER_PAD:
+ regex_pattern = hb_pad_regex;
+ break;
case HB_FILTER_ROTATE:
case HB_FILTER_DEBLOCK:
case HB_FILTER_DETELECINE:
@@ -534,6 +609,12 @@ hb_generate_filter_settings(int filter_id, const char *preset, const char *tune)
switch (filter_id)
{
+ case HB_FILTER_PAD:
+ if (preset == NULL) return NULL;
+ if (!strcasecmp(preset, "off")) return (char*)hb_filter_off;
+ if (hb_validate_filter_settings(filter_id, preset)) return NULL;
+
+ return generate_pad_settings(preset, tune);
case HB_FILTER_NLMEANS:
filter_param = generate_nlmeans_settings(preset, tune);
break;
diff --git a/libhb/preset.c b/libhb/preset.c
index 760dfd582..bc84965e1 100644
--- a/libhb/preset.c
+++ b/libhb/preset.c
@@ -1269,6 +1269,28 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict)
return -1;
}
+ // Pad filter
+ char *pad = hb_value_get_string_xform(
+ hb_dict_get(preset, "PicturePad"));
+ if (pad != NULL)
+ {
+ filter_str = hb_generate_filter_settings(HB_FILTER_PAD, pad, NULL);
+ if (filter_str == NULL)
+ {
+ hb_error("Invalid pad filter settings (%s)", pad);
+ return -1;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_PAD));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+ free(pad);
+
int fr_mode;
hb_value_t *fr_mode_value = hb_dict_get(preset, "VideoFramerateMode");
fr_mode = hb_value_type(fr_mode_value) == HB_VALUE_TYPE_STRING ? (
diff --git a/libhb/qsv_filter.c b/libhb/qsv_filter.c
index 33a324cf8..f168bb197 100644
--- a/libhb/qsv_filter.c
+++ b/libhb/qsv_filter.c
@@ -67,8 +67,7 @@ static int hb_qsv_filter_work( hb_filter_object_t * filter,
hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out );
-static int hb_qsv_filter_info( hb_filter_object_t * filter,
- hb_filter_info_t * info );
+static hb_filter_info_t * hb_qsv_filter_info( hb_filter_object_t * filter );
static void hb_qsv_filter_close( hb_filter_object_t * filter );
@@ -371,15 +370,19 @@ static int hb_qsv_filter_init( hb_filter_object_t * filter,
return 0;
}
-static int hb_qsv_filter_info( hb_filter_object_t * filter,
- hb_filter_info_t * info )
+static hb_filter_info_t * hb_qsv_filter_info( hb_filter_object_t * filter )
{
-
hb_filter_private_t *pv = filter->private_data;
- if (pv == NULL)
- return -1;
+ hb_filter_info_t * info;
+
+ if( !pv )
+ return NULL;
- sprintf(info->human_readable_desc,
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(128);
+ info->human_readable_desc[0] = 0;
+
+ snprintf(info->human_readable_desc, 128,
"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],
@@ -389,11 +392,11 @@ static int hb_qsv_filter_info( hb_filter_object_t * filter,
if (pv->deinterlace)
{
- sprintf(info->human_readable_desc + strlen(info->human_readable_desc),
- ", deinterlace");
+ int len = strlen(info->human_readable_desc);
+ snprintf(info->human_readable_desc + len, 128 - len, ", deinterlace");
}
- return 0;
+ return info;
}
void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ){
diff --git a/libhb/qsv_filter_pp.c b/libhb/qsv_filter_pp.c
index 250e2b52c..9920dbaf0 100644
--- a/libhb/qsv_filter_pp.c
+++ b/libhb/qsv_filter_pp.c
@@ -41,8 +41,7 @@ static int hb_qsv_filter_pre_init( hb_filter_object_t * filter,
static int hb_qsv_filter_pre_work( hb_filter_object_t * filter,
hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out );
-static int hb_qsv_filter_pre_info( hb_filter_object_t * filter,
- hb_filter_info_t * info );
+static hb_filter_info_t * hb_qsv_filter_pre_info( hb_filter_object_t * filter )
static void hb_qsv_filter_pre_close( hb_filter_object_t * filter );
static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
@@ -50,8 +49,7 @@ static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
static int hb_qsv_filter_post_work( hb_filter_object_t * filter,
hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out );
-static int hb_qsv_filter_post_info( hb_filter_object_t * filter,
- hb_filter_info_t * info );
+static hb_filter_info_t * hb_qsv_filter_post_info(hb_filter_object_t * filter);
static void hb_qsv_filter_post_close( hb_filter_object_t * filter );
@@ -242,15 +240,21 @@ static int filter_pre_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
return 0;
}
-static int hb_qsv_filter_pre_info( hb_filter_object_t * filter,
- hb_filter_info_t * info ){
+static hb_filter_info_t * hb_qsv_filter_pre_info( hb_filter_object_t * filter )
+{
hb_filter_private_t * pv = filter->private_data;
+ hb_filter_info_t * info;
+
if( !pv )
- return 0;
+ return NULL;
- sprintf(info->human_readable_desc, "copy data to system memory");
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(128);
+ info->human_readable_desc[0] = 0;
- return 0;
+ snprintf(info->human_readable_desc, 128, "copy data to system memory");
+
+ return info;
}
static int hb_qsv_filter_pre_init( hb_filter_object_t * filter,
hb_filter_init_t * init ){
@@ -535,15 +539,21 @@ static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ){
}
-static int hb_qsv_filter_post_info( hb_filter_object_t * filter,
- hb_filter_info_t * info ){
+static hb_filter_info_t * hb_qsv_filter_post_info( hb_filter_object_t * filter )
+{
hb_filter_private_t * pv = filter->private_data;
+ hb_filter_info_t * info;
+
if( !pv )
- return 0;
+ return NULL;
- sprintf(info->human_readable_desc, "copy data to opaque memory");
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(128);
+ info->human_readable_desc[0] = 0;
- return 0;
+ snprintf(info->human_readable_desc, 128, "copy data to opaque memory");
+
+ return info;
}
static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
hb_filter_init_t * init ){
diff --git a/libhb/rotate.c b/libhb/rotate.c
index ad05d9ead..f407eabbf 100644
--- a/libhb/rotate.c
+++ b/libhb/rotate.c
@@ -59,13 +59,12 @@ static int hb_rotate_work( hb_filter_object_t * filter,
static void hb_rotate_close( hb_filter_object_t * filter );
-static int hb_rotate_info( hb_filter_object_t * filter,
- hb_filter_info_t * info );
+static hb_filter_info_t * hb_rotate_info( hb_filter_object_t * filter );
hb_filter_object_t hb_filter_rotate =
{
.id = HB_FILTER_ROTATE,
- .enforce_order = 0,
+ .enforce_order = 1,
.name = "Rotate (rotate & flip image axes)",
.settings = NULL,
.init = hb_rotate_init,
@@ -322,18 +321,21 @@ static int hb_rotate_init( hb_filter_object_t * filter,
return 0;
}
-static int hb_rotate_info( hb_filter_object_t * filter,
- hb_filter_info_t * info )
+static hb_filter_info_t * hb_rotate_info( hb_filter_object_t * filter )
{
hb_filter_private_t * pv = filter->private_data;
+ hb_filter_info_t * info;
+
if( !pv )
- return 1;
+ return NULL;
+
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(128);
+ info->human_readable_desc[0] = 0;
- memset( info, 0, sizeof( hb_filter_info_t ) );
info->out.geometry.width = pv->width;
info->out.geometry.height = pv->height;
info->out.geometry.par = pv->par;
- int pos = 0;
int mirror_x = !!(pv->mode & 2);
int mirror_y = !!(pv->mode & 1);
@@ -348,18 +350,18 @@ static int hb_rotate_info( hb_filter_object_t * filter,
if (degrees > 0)
{
- sprintf(&info->human_readable_desc[pos], "Rotate %d%s", degrees,
- mirror_x ? " / mirror image" : "");
+ snprintf(info->human_readable_desc, 128, "Rotate %d%s", degrees,
+ mirror_x ? " / mirror image" : "");
}
else if (mirror_x)
{
- sprintf(&info->human_readable_desc[pos], "Mirror image");
+ snprintf(info->human_readable_desc, 128, "Mirror image");
}
else
{
- sprintf(&info->human_readable_desc[pos], "No rotation or mirror!");
+ snprintf(info->human_readable_desc, 128, "No rotation or mirror!");
}
- return 0;
+ return info;
}
static void hb_rotate_close( hb_filter_object_t * filter )
diff --git a/libhb/vfr.c b/libhb/vfr.c
index b1ba3196a..cc995ff59 100644
--- a/libhb/vfr.c
+++ b/libhb/vfr.c
@@ -46,7 +46,7 @@ static int hb_vfr_work( hb_filter_object_t * filter,
hb_buffer_t ** buf_out );
static void hb_vfr_close( hb_filter_object_t * filter );
-static int hb_vfr_info( hb_filter_object_t * filter, hb_filter_info_t * info );
+static hb_filter_info_t * hb_vfr_info( hb_filter_object_t * filter );
hb_filter_object_t hb_filter_vfr =
{
@@ -349,15 +349,18 @@ static int hb_vfr_init(hb_filter_object_t *filter, hb_filter_init_t *init)
return 0;
}
-static int hb_vfr_info( hb_filter_object_t * filter,
- hb_filter_info_t * info )
+static hb_filter_info_t * hb_vfr_info( hb_filter_object_t * filter )
{
hb_filter_private_t * pv = filter->private_data;
+ hb_filter_info_t * info;
if( !pv )
- return 1;
+ return NULL;
+
+ info = calloc(1, sizeof(hb_filter_info_t));
+ info->human_readable_desc = malloc(128);
+ info->human_readable_desc[0] = 0;
- memset( info, 0, sizeof( hb_filter_info_t ) );
info->out.vrate = pv->input_vrate;
if (pv->cfr == 2)
{
@@ -380,9 +383,9 @@ static int hb_vfr_info( hb_filter_object_t * filter,
if ( pv->cfr == 0 )
{
/* Ensure we're using "Same as source" FPS */
- sprintf( info->human_readable_desc,
- "frame rate: same as source (around %.3f fps)",
- (float)pv->vrate.num / pv->vrate.den );
+ snprintf( info->human_readable_desc, 128,
+ "frame rate: same as source (around %.3f fps)",
+ (float)pv->vrate.num / pv->vrate.den );
}
else if ( pv->cfr == 2 )
{
@@ -390,21 +393,21 @@ static int hb_vfr_info( hb_filter_object_t * filter,
// framerate, unless it's higher than the specified peak framerate.
double source_fps = (double)pv->input_vrate.num / pv->input_vrate.den;
double peak_fps = (double)pv->vrate.num / pv->vrate.den;
- sprintf( info->human_readable_desc,
- "frame rate: %.3f fps -> peak rate limited to %.3f fps",
- source_fps , peak_fps );
+ snprintf( info->human_readable_desc, 128,
+ "frame rate: %.3f fps -> peak rate limited to %.3f fps",
+ source_fps , peak_fps );
}
else
{
// Constant framerate. Signal the framerate we are using.
double source_fps = (double)pv->input_vrate.num / pv->input_vrate.den;
double constant_fps = (double)pv->vrate.num / pv->vrate.den;
- sprintf( info->human_readable_desc,
- "frame rate: %.3f fps -> constant %.3f fps",
- source_fps , constant_fps );
+ snprintf( info->human_readable_desc, 128,
+ "frame rate: %.3f fps -> constant %.3f fps",
+ source_fps , constant_fps );
}
- return 0;
+ return info;
}
static void hb_vfr_close( hb_filter_object_t * filter )
diff --git a/libhb/work.c b/libhb/work.c
index 1ed356dd1..6e3195e0b 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -387,12 +387,23 @@ void hb_display_job_info(hb_job_t *job)
hb_log(" + %s (default settings)", filter->name);
if( filter->info )
{
- hb_filter_info_t info;
- filter->info( filter, &info );
- if( info.human_readable_desc[0] )
+ hb_filter_info_t * info;
+
+ info = filter->info(filter);
+ if (info != NULL &&
+ info->human_readable_desc != NULL &&
+ info->human_readable_desc[0] != 0)
{
- hb_log(" + %s", info.human_readable_desc);
+ char * line, * pos = NULL;
+ char * tmp = strdup(info->human_readable_desc);
+ for (line = strtok_r(tmp, "\n", &pos); line != NULL;
+ line = strtok_r(NULL, "\n", &pos))
+ {
+ hb_log(" + %s", line);
+ }
+ free(tmp);
}
+ hb_filter_info_close(&info);
}
}
}
@@ -1146,6 +1157,7 @@ static int sanitize_qsv( hb_job_t * job )
// validated, CPU-based filters
case HB_FILTER_ROTATE:
case HB_FILTER_RENDER_SUB:
+ case HB_FILTER_AVFILTER:
encode_only = 1;
break;
@@ -1244,6 +1256,7 @@ static int sanitize_qsv( hb_job_t * job )
// then, validated filters
case HB_FILTER_ROTATE: // TODO: use Media SDK for this
case HB_FILTER_RENDER_SUB:
+ case HB_FILTER_AVFILTER:
num_cpu_filters++;
break;
@@ -1399,6 +1412,10 @@ static void do_job(hb_job_t *job)
{
hb_filter_init_t init;
+ // Combine HB_FILTER_AVFILTERs that are sequential
+ hb_avfilter_combine(job->list_filter);
+
+ memset(&init, 0, sizeof(init));
init.job = job;
init.pix_fmt = AV_PIX_FMT_YUV420P;
init.geometry.width = title->geometry.width;
@@ -1615,7 +1632,6 @@ static void do_job(hb_job_t *job)
for (i = 0; i < hb_list_count(job->list_filter); i++)
{
hb_filter_object_t * filter = hb_list_item(job->list_filter, i);
-
filter->fifo_in = fifo_in;
filter->fifo_out = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
fifo_in = filter->fifo_out;
diff --git a/test/module.defs b/test/module.defs
index 29f28a354..cec9bd668 100644
--- a/test/module.defs
+++ b/test/module.defs
@@ -14,8 +14,8 @@ TEST.GCC.L = $(CONTRIB.build/)lib
TEST.libs = $(LIBHB.a)
TEST.GCC.l = \
- ass avresample avformat avcodec avutil mp3lame dvdnav dvdread \
- fontconfig fribidi ogg \
+ ass avresample avformat avcodec avfilter avutil mp3lame dvdnav \
+ dvdread fontconfig fribidi ogg \
samplerate swscale vpx theoraenc theoradec vorbis vorbisenc x264 \
bluray freetype xml2 bz2 z jansson
diff --git a/test/test.c b/test/test.c
index 795ea1f2c..a5179c702 100644
--- a/test/test.c
+++ b/test/test.c
@@ -54,6 +54,8 @@ static int main_feature = 0;
static char * native_language = NULL;
static int native_dub = 0;
static int twoPass = 0;
+static int pad_disable = 0;
+static char * pad = NULL;
static int deinterlace_disable = 0;
static int deinterlace_custom = 0;
static char * deinterlace = NULL;
@@ -104,14 +106,14 @@ static char ** srtoffset = NULL;
static char ** srtlang = NULL;
static int srtdefault = -1;
static int srtburn = -1;
-static int width = 0;
-static int height = 0;
-static int crop[4] = { -1,-1,-1,-1 };
-static int loose_crop = -1;
-static char * vrate = NULL;
-static float vquality = -1.0;
-static int vbitrate = 0;
-static int mux = 0;
+static int width = 0;
+static int height = 0;
+static int crop[4] = { -1,-1,-1,-1 };
+static int loose_crop = -1;
+static char * vrate = NULL;
+static float vquality = -1.0;
+static int vbitrate = 0;
+static int mux = 0;
static int anamorphic_mode = -1;
static int modulus = 0;
static int par_height = -1;
@@ -178,10 +180,6 @@ static hb_dict_t * PreparePreset( const char *preset_name );
static hb_dict_t * PrepareJob( hb_handle_t *h, hb_title_t *title,
hb_dict_t *preset_dict );
-static int str_vlen(char **strv);
-static void str_vfree( char **strv );
-static char** str_split( char *str, char delem );
-
static void print_string_list(FILE *out, const char* const *list, const char *prefix);
#ifdef __APPLE_CC__
@@ -461,20 +459,20 @@ int main( int argc, char ** argv )
hb_value_free(&preset_dict);
hb_close(&h);
hb_global_close();
- str_vfree(audio_copy_list);
- str_vfree(abitrates);
- str_vfree(acompressions);
- str_vfree(aqualities);
- str_vfree(audio_dither);
- str_vfree(acodecs);
- str_vfree(arates);
- str_vfree(atracks);
- str_vfree(audio_lang_list);
- str_vfree(audio_gain);
- str_vfree(dynamic_range_compression);
- str_vfree(mixdowns);
- str_vfree(subtitle_lang_list);
- str_vfree(subtracks);
+ hb_str_vfree(audio_copy_list);
+ hb_str_vfree(abitrates);
+ hb_str_vfree(acompressions);
+ hb_str_vfree(aqualities);
+ hb_str_vfree(audio_dither);
+ hb_str_vfree(acodecs);
+ hb_str_vfree(arates);
+ hb_str_vfree(atracks);
+ hb_str_vfree(audio_lang_list);
+ hb_str_vfree(audio_gain);
+ hb_str_vfree(dynamic_range_compression);
+ hb_str_vfree(mixdowns);
+ hb_str_vfree(subtitle_lang_list);
+ hb_str_vfree(subtracks);
free(acodec_fallback);
free(native_language);
free(format);
@@ -1228,6 +1226,12 @@ static void ShowHelp()
" --crop <T:B:L:R> Set cropping values (default: autocrop)\n"
" --loose-crop Always crop to a multiple of the modulus\n"
" --no-loose-crop Disable preset 'loose-crop'\n"
+" --pad <W:H:C:X,Y> Add borders to pad image to WxH (e.g. letterbox)\n"
+" Optionally set color of pad to C (default black)\n"
+" Color may be HTML color name or RGB value\n"
+" Optionally set position of image in pad area\n"
+" Any value may be 'auto' in which case the\n"
+" default value for that field is used\n"
" -Y, --maxHeight <#> Set maximum height\n"
" -X, --maxWidth <#> Set maximum width\n"
" --non-anamorphic Set pixel aspect ratio to 1:1\n"
@@ -1546,131 +1550,13 @@ static void ShowPresets(hb_value_array_t *presets, int indent, int descriptions)
Indent(stderr, " ", indent+1);
fprintf(stderr, "%s\n", split[ii]);
}
- str_vfree(split);
+ hb_str_vfree(split);
}
}
}
}
}
-static char* strchr_quote(char *pos, char c, char q)
-{
- if (pos == NULL)
- return NULL;
-
- while (*pos != 0 && *pos != c)
- {
- if (*pos == q)
- {
- pos = strchr_quote(pos+1, q, 0);
- if (pos == NULL)
- return NULL;
- pos++;
- }
- else if (*pos == '\\' && *(pos+1) != 0)
- pos += 2;
- else
- pos++;
- }
- if (*pos != c)
- return NULL;
- return pos;
-}
-
-static char *strndup_quote(char *str, char q, int len)
-{
- if (str == NULL)
- return NULL;
-
- char * res;
- int str_len = strlen( str );
- int src = 0, dst = 0;
- res = malloc( len > str_len ? str_len + 1 : len + 1 );
- if ( res == NULL ) return res;
-
- while (str[src] != 0 && src < len)
- {
- if (str[src] == q)
- src++;
- else if (str[src] == '\\' && str[src+1] != 0)
- {
- res[dst++] = str[src+1];
- src += 2;
- }
- else
- res[dst++] = str[src++];
- }
- res[dst] = '\0';
- return res;
-}
-
-static int str_vlen(char **strv)
-{
- int i;
- if (strv == NULL)
- return 0;
- for (i = 0; strv[i]; i++);
- return i;
-}
-
-static char** str_split( char *str, char delem )
-{
- char * pos;
- char * end;
- char ** ret;
- int count, i;
- char quote = '"';
-
- if (delem == '"')
- {
- quote = '\'';
- }
- if ( str == NULL || str[0] == 0 )
- {
- ret = malloc( sizeof(char*) );
- if ( ret == NULL ) return ret;
- *ret = NULL;
- return ret;
- }
-
- // Find number of elements in the string
- count = 1;
- pos = str;
- while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL )
- {
- count++;
- pos++;
- }
-
- ret = calloc( ( count + 1 ), sizeof(char*) );
- if ( ret == NULL ) return ret;
-
- pos = str;
- for ( i = 0; i < count - 1; i++ )
- {
- end = strchr_quote( pos, delem, quote );
- ret[i] = strndup_quote(pos, quote, end - pos);
- pos = end + 1;
- }
- ret[i] = strndup_quote(pos, quote, strlen(pos));
-
- return ret;
-}
-
-static void str_vfree( char **strv )
-{
- int i;
-
- if (strv == NULL)
- return;
-
- for ( i = 0; strv[i]; i++ )
- {
- free( strv[i] );
- }
- free( strv );
-}
-
static double parse_hhmmss_strtok()
{
/* Assumes strtok has already been called on a string. Intends to parse
@@ -1743,6 +1629,7 @@ static int ParseOptions( int argc, char ** argv )
#define PRESET_IMPORT_GUI 306
#define VERSION 307
#define DESCRIBE 308
+ #define PAD 309
for( ;; )
{
@@ -1843,6 +1730,8 @@ static int ParseOptions( int argc, char ** argv )
{ "crop", required_argument, NULL, 'n' },
{ "loose-crop", optional_argument, NULL, LOOSE_CROP },
{ "no-loose-crop", no_argument, &loose_crop, 0 },
+ { "pad", required_argument, NULL, PAD },
+ { "no-pad", no_argument, &pad_disable, 1 },
// mapping of legacy option names for backwards compatibility
{ "qsv-preset", required_argument, NULL, ENCODER_PRESET, },
@@ -2056,53 +1945,53 @@ static int ParseOptions( int argc, char ** argv )
chapter_markers = 1;
break;
case AUDIO_LANG_LIST:
- audio_lang_list = str_split(optarg, ',');
+ audio_lang_list = hb_str_vsplit(optarg, ',');
break;
case SUBTITLE_LANG_LIST:
- subtitle_lang_list = str_split(optarg, ',');
+ subtitle_lang_list = hb_str_vsplit(optarg, ',');
break;
case 'a':
if( optarg != NULL )
{
- atracks = str_split(optarg, ',');
+ atracks = hb_str_vsplit(optarg, ',');
}
else
{
- atracks = str_split("1", ',');
+ atracks = hb_str_vsplit("1", ',');
}
break;
case '6':
if( optarg != NULL )
{
- mixdowns = str_split(optarg, ',');
+ mixdowns = hb_str_vsplit(optarg, ',');
}
break;
case 'D':
if( optarg != NULL )
{
- dynamic_range_compression = str_split(optarg, ',');
+ dynamic_range_compression = hb_str_vsplit(optarg, ',');
}
break;
case AUDIO_GAIN:
if( optarg != NULL )
{
- audio_gain = str_split(optarg, ',');
+ audio_gain = hb_str_vsplit(optarg, ',');
}
break;
case AUDIO_DITHER:
if (optarg != NULL)
{
- audio_dither = str_split(optarg, ',');
+ audio_dither = hb_str_vsplit(optarg, ',');
}
break;
case NORMALIZE_MIX:
- normalize_mix_level = str_split(optarg, ',');
+ normalize_mix_level = hb_str_vsplit(optarg, ',');
break;
case 's':
- subtracks = str_split( optarg, ',' );
+ subtracks = hb_str_vsplit( optarg, ',' );
break;
case 'F':
- subforce = str_split( optarg, ',' );
+ subforce = hb_str_vsplit( optarg, ',' );
break;
case SUB_BURNED:
if (optarg != NULL)
@@ -2121,7 +2010,7 @@ static int ParseOptions( int argc, char ** argv )
}
if (subburn > 0)
{
- if (subtracks != NULL && str_vlen(subtracks) >= subburn &&
+ if (subtracks != NULL && hb_str_vlen(subtracks) >= subburn &&
!strcasecmp("scan", subtracks[subburn-1]))
{
subburn_native = 1;
@@ -2155,16 +2044,16 @@ static int ParseOptions( int argc, char ** argv )
native_dub = 1;
break;
case SRT_FILE:
- srtfile = str_split( optarg, ',' );
+ srtfile = hb_str_vsplit( optarg, ',' );
break;
case SRT_CODESET:
- srtcodeset = str_split( optarg, ',' );
+ srtcodeset = hb_str_vsplit( optarg, ',' );
break;
case SRT_OFFSET:
- srtoffset = str_split( optarg, ',' );
+ srtoffset = hb_str_vsplit( optarg, ',' );
break;
case SRT_LANG:
- srtlang = str_split( optarg, ',' );
+ srtlang = hb_str_vsplit( optarg, ',' );
break;
case SRT_DEFAULT:
if( optarg != NULL )
@@ -2309,7 +2198,7 @@ static int ParseOptions( int argc, char ** argv )
case 'E':
if( optarg != NULL )
{
- acodecs = str_split(optarg, ',');
+ acodecs = hb_str_vsplit(optarg, ',');
}
break;
case 'w':
@@ -2337,6 +2226,15 @@ static int ParseOptions( int argc, char ** argv )
else
loose_crop = 1;
break;
+ case PAD:
+ {
+ free(pad);
+ if (optarg != NULL)
+ {
+ pad = strdup(optarg);
+ }
+ break;
+ }
case 'r':
{
vrate = strdup(optarg);
@@ -2347,7 +2245,7 @@ static int ParseOptions( int argc, char ** argv )
break;
}
case 'R':
- arates = str_split( optarg, ',' );
+ arates = hb_str_vsplit( optarg, ',' );
break;
case 'b':
vbitrate = atoi( optarg );
@@ -2356,13 +2254,13 @@ static int ParseOptions( int argc, char ** argv )
vquality = atof( optarg );
break;
case 'B':
- abitrates = str_split( optarg, ',' );
+ abitrates = hb_str_vsplit( optarg, ',' );
break;
case 'Q':
- aqualities = str_split( optarg, ',' );
+ aqualities = hb_str_vsplit( optarg, ',' );
break;
case 'C':
- acompressions = str_split( optarg, ',' );
+ acompressions = hb_str_vsplit( optarg, ',' );
break;
case ENCODER_PRESET:
encoder_preset = strdup( optarg );
@@ -2419,7 +2317,7 @@ static int ParseOptions( int argc, char ** argv )
case 'A':
if( optarg != NULL )
{
- anames = str_split( optarg, ',' );
+ anames = hb_str_vsplit( optarg, ',' );
}
break;
case PREVIEWS:
@@ -2478,7 +2376,7 @@ static int ParseOptions( int argc, char ** argv )
}
case ALLOWED_AUDIO_COPY:
{
- audio_copy_list = str_split(optarg, ',');
+ audio_copy_list = hb_str_vsplit(optarg, ',');
break;
}
case AUDIO_FALLBACK:
@@ -2557,6 +2455,21 @@ static int ParseOptions( int argc, char ** argv )
}
}
+ if (pad != NULL)
+ {
+ if (pad_disable)
+ {
+ fprintf(stderr,
+ "Incompatible options --pad and --no-pad\n");
+ return -1;
+ }
+ else if (hb_validate_filter_settings(HB_FILTER_PAD, pad))
+ {
+ fprintf(stderr, "Invalid pad option %s\n", pad);
+ return -1;
+ }
+ }
+
if (deinterlace != NULL)
{
if (deinterlace_disable)
@@ -2665,7 +2578,7 @@ static int foreign_audio_scan(char **subtracks)
{
if (subtracks != NULL)
{
- int count = str_vlen(subtracks);
+ int count = hb_str_vlen(subtracks);
int ii;
for (ii = 0; ii < count; ii++)
{
@@ -2683,7 +2596,7 @@ static int count_subtitles(char **subtracks)
int subtitle_track_count = 0;
if (subtracks != NULL)
{
- int count = str_vlen(subtracks);
+ int count = hb_str_vlen(subtracks);
int ii;
for (ii = 0; ii < count; ii++)
{
@@ -2844,7 +2757,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
if (subtitle_lang_list != NULL)
{
hb_value_array_clear(subtitle_lang_array);
- int count = str_vlen(subtitle_lang_list);
+ int count = hb_str_vlen(subtitle_lang_list);
for (ii = 0; ii < count; ii++)
{
const iso639_lang_t *lang = lang_lookup(subtitle_lang_list[ii]);
@@ -2943,7 +2856,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
if (audio_lang_list != NULL)
{
hb_value_array_clear(audio_lang_array);
- int count = str_vlen(audio_lang_list);
+ int count = hb_str_vlen(audio_lang_list);
for (ii = 0; ii < count; ii++)
{
const iso639_lang_t *lang = lang_lookup(audio_lang_list[ii]);
@@ -2998,17 +2911,17 @@ static hb_dict_t * PreparePreset(const char *preset_name)
list = hb_value_array_init();
hb_dict_set(preset, "AudioList", list);
}
- int count = MAX(str_vlen(mixdowns),
- MAX(str_vlen(dynamic_range_compression),
- MAX(str_vlen(audio_gain),
- MAX(str_vlen(audio_dither),
- MAX(str_vlen(normalize_mix_level),
- MAX(str_vlen(arates),
- MAX(str_vlen(abitrates),
- MAX(str_vlen(aqualities),
- MAX(str_vlen(acompressions),
- MAX(str_vlen(acodecs),
- str_vlen(anames)))))))))));
+ int count = MAX(hb_str_vlen(mixdowns),
+ MAX(hb_str_vlen(dynamic_range_compression),
+ MAX(hb_str_vlen(audio_gain),
+ MAX(hb_str_vlen(audio_dither),
+ MAX(hb_str_vlen(normalize_mix_level),
+ MAX(hb_str_vlen(arates),
+ MAX(hb_str_vlen(abitrates),
+ MAX(hb_str_vlen(aqualities),
+ MAX(hb_str_vlen(acompressions),
+ MAX(hb_str_vlen(acodecs),
+ hb_str_vlen(anames)))))))))));
hb_dict_t *audio_dict;
// Add audio dict entries to list if needed
@@ -3023,7 +2936,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update codecs
- if (str_vlen(acodecs) > 0)
+ if (hb_str_vlen(acodecs) > 0)
{
for (ii = 0; acodecs[ii] != NULL; ii++)
{
@@ -3041,7 +2954,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update qualities
- if (str_vlen(aqualities) > 0)
+ if (hb_str_vlen(aqualities) > 0)
{
for (ii = 0; aqualities[ii] != NULL &&
aqualities[ii][0] != 0; ii++)
@@ -3055,7 +2968,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update bitrates
- if (str_vlen(abitrates) > 0)
+ if (hb_str_vlen(abitrates) > 0)
{
for (ii = 0; abitrates[ii] != NULL &&
abitrates[ii][0] != 0; ii++)
@@ -3085,7 +2998,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update samplerates
- if (str_vlen(arates) > 0)
+ if (hb_str_vlen(arates) > 0)
{
for (ii = 0; arates[ii] != NULL &&
arates[ii][0] != 0; ii++)
@@ -3105,7 +3018,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update mixdowns
- if (str_vlen(mixdowns) > 0)
+ if (hb_str_vlen(mixdowns) > 0)
{
for (ii = 0; mixdowns[ii] != NULL &&
mixdowns[ii][0] != 0; ii++)
@@ -3125,7 +3038,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update mixdowns normalization
- if (str_vlen(normalize_mix_level) > 0)
+ if (hb_str_vlen(normalize_mix_level) > 0)
{
for (ii = 0; normalize_mix_level[ii] != NULL &&
normalize_mix_level[ii][0] != 0; ii++)
@@ -3147,7 +3060,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update DRC
- if (str_vlen(dynamic_range_compression) > 0)
+ if (hb_str_vlen(dynamic_range_compression) > 0)
{
for (ii = 0;dynamic_range_compression[ii] != NULL &&
dynamic_range_compression[ii][0] != 0; ii++)
@@ -3170,7 +3083,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update Gain
- if (str_vlen(audio_gain) > 0)
+ if (hb_str_vlen(audio_gain) > 0)
{
for (ii = 0; audio_gain[ii] != NULL &&
audio_gain[ii][0] != 0; ii++)
@@ -3192,7 +3105,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update dither method
- if (str_vlen(audio_dither) > 0)
+ if (hb_str_vlen(audio_dither) > 0)
{
for (ii = 0; audio_dither[ii] != NULL &&
audio_dither[ii][0] != 0; ii++)
@@ -3212,7 +3125,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update compression
- if (str_vlen(acompressions) > 0)
+ if (hb_str_vlen(acompressions) > 0)
{
for (ii = 0; acompressions[ii] != NULL &&
acompressions[ii][0] != 0; ii++)
@@ -3234,7 +3147,7 @@ static hb_dict_t * PreparePreset(const char *preset_name)
}
// Update track names
- if (str_vlen(anames) > 0)
+ if (hb_str_vlen(anames) > 0)
{
for (ii = 0; anames[ii] != NULL &&
anames[ii][0] != 0; ii++)
@@ -3544,6 +3457,14 @@ static hb_dict_t * PreparePreset(const char *preset_name)
{
hb_dict_set(preset, "PictureRotate", hb_value_string(rotate));
}
+ if (pad_disable)
+ {
+ hb_dict_set(preset, "PicturePad", hb_value_string("off"));
+ }
+ if (pad != NULL)
+ {
+ hb_dict_set(preset, "PicturePad", hb_value_string(pad));
+ }
return preset;
}
@@ -3598,11 +3519,11 @@ static int add_srt(hb_value_array_t *list, int track, int *one_burned)
*one_burned |= burn;
int def = srtdefault == track + 1;
- if (srtcodeset && track < str_vlen(srtcodeset) && srtcodeset[track])
+ if (srtcodeset && track < hb_str_vlen(srtcodeset) && srtcodeset[track])
codeset = srtcodeset[track];
- if (srtoffset && track < str_vlen(srtoffset) && srtoffset[track])
+ if (srtoffset && track < hb_str_vlen(srtoffset) && srtoffset[track])
offset = strtoll(srtoffset[track], NULL, 0);
- if (srtlang && track < str_vlen(srtlang) && srtlang[track])
+ if (srtlang && track < hb_str_vlen(srtlang) && srtlang[track])
{
const iso639_lang_t *lang = lang_lookup(srtlang[track]);
if (lang != NULL)