diff options
author | John Stebbins <[email protected]> | 2015-10-24 14:06:56 -0700 |
---|---|---|
committer | John Stebbins <[email protected]> | 2016-01-21 12:38:42 -0700 |
commit | 10ea76c71197b302b10088d93680a4bed4bc6b8e (patch) | |
tree | 459b46b16256c39ed34fe1f0a4b9476ec3439871 | |
parent | ef956e695879c716dc22c96f7f8fa24e3fa5d08c (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.patch | 34 | ||||
-rw-r--r-- | contrib/ffmpeg/A11-avfilter-framerate.patch | 352 | ||||
-rw-r--r-- | contrib/ffmpeg/module.defs | 1 | ||||
-rw-r--r-- | gtk/configure.ac | 2 | ||||
-rw-r--r-- | libhb/avfilter.c | 444 | ||||
-rw-r--r-- | libhb/common.c | 145 | ||||
-rw-r--r-- | libhb/common.h | 72 | ||||
-rw-r--r-- | libhb/cropscale.c | 20 | ||||
-rw-r--r-- | libhb/grayscale.c | 11 | ||||
-rw-r--r-- | libhb/hb.c | 2 | ||||
-rw-r--r-- | libhb/hb.h | 1 | ||||
-rw-r--r-- | libhb/internal.h | 12 | ||||
-rw-r--r-- | libhb/module.defs | 2 | ||||
-rw-r--r-- | libhb/param.c | 81 | ||||
-rw-r--r-- | libhb/preset.c | 22 | ||||
-rw-r--r-- | libhb/qsv_filter.c | 25 | ||||
-rw-r--r-- | libhb/qsv_filter_pp.c | 38 | ||||
-rw-r--r-- | libhb/rotate.c | 28 | ||||
-rw-r--r-- | libhb/vfr.c | 33 | ||||
-rw-r--r-- | libhb/work.c | 26 | ||||
-rw-r--r-- | test/module.defs | 4 | ||||
-rw-r--r-- | test/test.c | 315 |
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) |