Fixes Ticket #2517.
Clément Bœsch authored on 2013/05/25 08:07:46... | ... |
@@ -4177,6 +4177,79 @@ gradfun=radius=8 |
4177 | 4177 |
|
4178 | 4178 |
@end itemize |
4179 | 4179 |
|
4180 |
+@anchor{haldclut} |
|
4181 |
+@section haldclut |
|
4182 |
+ |
|
4183 |
+Apply a Hald CLUT to a video stream. |
|
4184 |
+ |
|
4185 |
+First input is the video stream to process, and second one is the Hald CLUT. |
|
4186 |
+The Hald CLUT input can be a simple picture or a complete video stream. |
|
4187 |
+ |
|
4188 |
+The filter accepts the following options: |
|
4189 |
+ |
|
4190 |
+@table @option |
|
4191 |
+@item shortest |
|
4192 |
+Force termination when the shortest input terminates. Default is @code{0}. |
|
4193 |
+@item repeatlast |
|
4194 |
+Continue applying the last CLUT after the end of the stream. A value of |
|
4195 |
+@code{0} disable the filter after the last frame of the CLUT is reached. |
|
4196 |
+Default is @code{1}. |
|
4197 |
+@end table |
|
4198 |
+ |
|
4199 |
+@code{haldclut} also has the same interpolation options as @ref{lut3d} (both |
|
4200 |
+filters share the same internals). |
|
4201 |
+ |
|
4202 |
+More information about the Hald CLUT can be found on Eskil Steenberg's website |
|
4203 |
+(Hald CLUT author) at @url{http://www.quelsolaar.com/technology/clut.html}. |
|
4204 |
+ |
|
4205 |
+@subsection Workflow examples |
|
4206 |
+ |
|
4207 |
+@subsubsection Hald CLUT video stream |
|
4208 |
+ |
|
4209 |
+Generate an identity Hald CLUT stream altered with various effects: |
|
4210 |
+@example |
|
4211 |
+ffmpeg -f lavfi -i @ref{haldclutsrc}=8 -vf "hue=H=2*PI*t:s=sin(2*PI*t)+1, curves=cross_process" -t 10 -c:v ffv1 clut.nut |
|
4212 |
+@end example |
|
4213 |
+ |
|
4214 |
+Note: make sure you use a lossless codec. |
|
4215 |
+ |
|
4216 |
+Then use it with @code{haldclut} to apply it on some random stream: |
|
4217 |
+@example |
|
4218 |
+ffmpeg -f lavfi -i mandelbrot -i clut.nut -filter_complex '[0][1] haldclut' -t 20 mandelclut.mkv |
|
4219 |
+@end example |
|
4220 |
+ |
|
4221 |
+The Hald CLUT will be applied to the 10 first seconds (duration of |
|
4222 |
+@file{clut.nut}), then the latest picture of that CLUT stream will be applied |
|
4223 |
+to the remaining frames of the @code{mandelbrot} stream. |
|
4224 |
+ |
|
4225 |
+@subsubsection Hald CLUT with preview |
|
4226 |
+ |
|
4227 |
+A Hald CLUT is supposed to be a squared image of @code{Level*Level*Level} by |
|
4228 |
+@code{Level*Level*Level} pixels. For a given Hald CLUT, FFmpeg will select the |
|
4229 |
+biggest possible square starting at the top left of the picture. The remaining |
|
4230 |
+padding pixels (bottom or right) will be ignored. This area can be used to add |
|
4231 |
+a preview of the Hald CLUT. |
|
4232 |
+ |
|
4233 |
+Typically, the following generated Hald CLUT will be supported by the |
|
4234 |
+@code{haldclut} filter: |
|
4235 |
+ |
|
4236 |
+@example |
|
4237 |
+ffmpeg -f lavfi -i @ref{haldclutsrc}=8 -vf " |
|
4238 |
+ pad=iw+320 [padded_clut]; |
|
4239 |
+ smptebars=s=320x256, split [a][b]; |
|
4240 |
+ [padded_clut][a] overlay=W-320:h, curves=color_negative [main]; |
|
4241 |
+ [main][b] overlay=W-320" -frames:v 1 clut.png |
|
4242 |
+@end example |
|
4243 |
+ |
|
4244 |
+It contains the original and a preview of the effect of the CLUT: SMPTE color |
|
4245 |
+bars are displayed on the right-top, and below the same color bars processed by |
|
4246 |
+the color changes. |
|
4247 |
+ |
|
4248 |
+Then, the effect of this Hald CLUT can be visualized with: |
|
4249 |
+@example |
|
4250 |
+ffplay input.mkv -vf "movie=clut.png, [in] haldclut" |
|
4251 |
+@end example |
|
4252 |
+ |
|
4180 | 4253 |
@section hflip |
4181 | 4254 |
|
4182 | 4255 |
Flip the input video horizontally. |
... | ... |
@@ -4603,6 +4676,7 @@ kerndeint=map=1 |
4603 | 4603 |
@end example |
4604 | 4604 |
@end itemize |
4605 | 4605 |
|
4606 |
+@anchor{lut3d} |
|
4606 | 4607 |
@section lut3d |
4607 | 4608 |
|
4608 | 4609 |
Apply a 3D LUT to an input video. |
... | ... |
@@ -7401,11 +7475,19 @@ ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_c |
7401 | 7401 |
@end example |
7402 | 7402 |
@end itemize |
7403 | 7403 |
|
7404 |
+@anchor{color} |
|
7405 |
+@anchor{haldclutsrc} |
|
7406 |
+@anchor{nullsrc} |
|
7407 |
+@anchor{rgbtestsrc} |
|
7408 |
+@anchor{smptebars} |
|
7409 |
+@anchor{smptehdbars} |
|
7410 |
+@anchor{testsrc} |
|
7404 | 7411 |
@section color, haldclutsrc, nullsrc, rgbtestsrc, smptebars, smptehdbars, testsrc |
7405 | 7412 |
|
7406 | 7413 |
The @code{color} source provides an uniformly colored input. |
7407 | 7414 |
|
7408 |
-The @code{haldclutsrc} source provides an identity Hald CLUT. |
|
7415 |
+The @code{haldclutsrc} source provides an identity Hald CLUT. See also |
|
7416 |
+@ref{haldclut} filter. |
|
7409 | 7417 |
|
7410 | 7418 |
The @code{nullsrc} source returns unprocessed video frames. It is |
7411 | 7419 |
mainly useful to be employed in analysis / debugging tools, or as the |
... | ... |
@@ -135,6 +135,7 @@ OBJS-$(CONFIG_FPS_FILTER) += vf_fps.o |
135 | 135 |
OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o |
136 | 136 |
OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o |
137 | 137 |
OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o |
138 |
+OBJS-$(CONFIG_HALDCLUT_FILTER) += vf_lut3d.o dualinput.o |
|
138 | 139 |
OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o |
139 | 140 |
OBJS-$(CONFIG_HISTEQ_FILTER) += vf_histeq.o |
140 | 141 |
OBJS-$(CONFIG_HISTOGRAM_FILTER) += vf_histogram.o |
... | ... |
@@ -133,6 +133,7 @@ void avfilter_register_all(void) |
133 | 133 |
REGISTER_FILTER(FREI0R, frei0r, vf); |
134 | 134 |
REGISTER_FILTER(GEQ, geq, vf); |
135 | 135 |
REGISTER_FILTER(GRADFUN, gradfun, vf); |
136 |
+ REGISTER_FILTER(HALDCLUT, haldclut, vf); |
|
136 | 137 |
REGISTER_FILTER(HFLIP, hflip, vf); |
137 | 138 |
REGISTER_FILTER(HISTEQ, histeq, vf); |
138 | 139 |
REGISTER_FILTER(HISTOGRAM, histogram, vf); |
... | ... |
@@ -30,7 +30,7 @@ |
30 | 30 |
#include "libavutil/avutil.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFILTER_VERSION_MAJOR 3 |
33 |
-#define LIBAVFILTER_VERSION_MINOR 71 |
|
33 |
+#define LIBAVFILTER_VERSION_MINOR 72 |
|
34 | 34 |
#define LIBAVFILTER_VERSION_MICRO 100 |
35 | 35 |
|
36 | 36 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
... | ... |
@@ -31,6 +31,7 @@ |
31 | 31 |
#include "libavutil/avstring.h" |
32 | 32 |
#include "avfilter.h" |
33 | 33 |
#include "drawutils.h" |
34 |
+#include "dualinput.h" |
|
34 | 35 |
#include "formats.h" |
35 | 36 |
#include "internal.h" |
36 | 37 |
#include "video.h" |
... | ... |
@@ -51,7 +52,9 @@ struct rgbvec { |
51 | 51 |
float r, g, b; |
52 | 52 |
}; |
53 | 53 |
|
54 |
-#define MAX_LEVEL 36 |
|
54 |
+/* 3D LUT don't often go up to level 32, but it is common to have a Hald CLUT |
|
55 |
+ * of 512x512 (64x64x64) */ |
|
56 |
+#define MAX_LEVEL 64 |
|
55 | 57 |
|
56 | 58 |
typedef struct LUT3DContext { |
57 | 59 |
const AVClass *class; |
... | ... |
@@ -64,20 +67,23 @@ typedef struct LUT3DContext { |
64 | 64 |
struct rgbvec (*interp_16)(const struct LUT3DContext*, uint16_t, uint16_t, uint16_t); |
65 | 65 |
struct rgbvec lut[MAX_LEVEL][MAX_LEVEL][MAX_LEVEL]; |
66 | 66 |
int lutsize; |
67 |
+#if CONFIG_HALDCLUT_FILTER |
|
68 |
+ uint8_t clut_rgba_map[4]; |
|
69 |
+ int clut_step; |
|
70 |
+ int clut_is16bit; |
|
71 |
+ int clut_width; |
|
72 |
+ FFDualInputContext dinput; |
|
73 |
+#endif |
|
67 | 74 |
} LUT3DContext; |
68 | 75 |
|
69 | 76 |
#define OFFSET(x) offsetof(LUT3DContext, x) |
70 | 77 |
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
71 |
-static const AVOption lut3d_options[] = { |
|
72 |
- { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, |
|
73 |
- { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, FLAGS, "interp_mode" }, |
|
74 |
- { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, |
|
75 |
- { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, |
|
76 |
- { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, |
|
78 |
+#define COMMON_OPTIONS \ |
|
79 |
+ { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, FLAGS, "interp_mode" }, \ |
|
80 |
+ { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \ |
|
81 |
+ { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \ |
|
82 |
+ { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \ |
|
77 | 83 |
{ NULL } |
78 |
-}; |
|
79 |
- |
|
80 |
-AVFILTER_DEFINE_CLASS(lut3d); |
|
81 | 84 |
|
82 | 85 |
static inline float lerpf(float v0, float v1, float f) |
83 | 86 |
{ |
... | ... |
@@ -404,7 +410,9 @@ static void set_identity_matrix(LUT3DContext *lut3d, int size) |
404 | 404 |
} |
405 | 405 |
} |
406 | 406 |
|
407 |
-static av_cold int init(AVFilterContext *ctx) |
|
407 |
+#if CONFIG_LUT3D_FILTER |
|
408 |
+/* TODO: move to the CONFIG_LUT3D_FILTER definition scope at the bottom */ |
|
409 |
+static av_cold int lut3d_init(AVFilterContext *ctx) |
|
408 | 410 |
{ |
409 | 411 |
int ret; |
410 | 412 |
FILE *f; |
... | ... |
@@ -454,6 +462,7 @@ end: |
454 | 454 |
fclose(f); |
455 | 455 |
return ret; |
456 | 456 |
} |
457 |
+#endif |
|
457 | 458 |
|
458 | 459 |
static int query_formats(AVFilterContext *ctx) |
459 | 460 |
{ |
... | ... |
@@ -523,7 +532,7 @@ static int config_input(AVFilterLink *inlink) |
523 | 523 |
} \ |
524 | 524 |
} while (0) |
525 | 525 |
|
526 |
-static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
526 |
+static AVFrame *apply_lut(AVFilterLink *inlink, AVFrame *in) |
|
527 | 527 |
{ |
528 | 528 |
int x, y, direct = 0; |
529 | 529 |
AVFilterContext *ctx = inlink->dst; |
... | ... |
@@ -543,7 +552,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
543 | 543 |
out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
544 | 544 |
if (!out) { |
545 | 545 |
av_frame_free(&in); |
546 |
- return AVERROR(ENOMEM); |
|
546 |
+ return NULL; |
|
547 | 547 |
} |
548 | 548 |
av_frame_copy_props(out, in); |
549 | 549 |
} |
... | ... |
@@ -554,9 +563,26 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
554 | 554 |
if (!direct) |
555 | 555 |
av_frame_free(&in); |
556 | 556 |
|
557 |
+ return out; |
|
558 |
+} |
|
559 |
+ |
|
560 |
+static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
561 |
+{ |
|
562 |
+ AVFilterLink *outlink = inlink->dst->outputs[0]; |
|
563 |
+ AVFrame *out = apply_lut(inlink, in); |
|
564 |
+ if (!out) |
|
565 |
+ return AVERROR(ENOMEM); |
|
557 | 566 |
return ff_filter_frame(outlink, out); |
558 | 567 |
} |
559 | 568 |
|
569 |
+#if CONFIG_LUT3D_FILTER |
|
570 |
+static const AVOption lut3d_options[] = { |
|
571 |
+ { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, |
|
572 |
+ COMMON_OPTIONS |
|
573 |
+}; |
|
574 |
+ |
|
575 |
+AVFILTER_DEFINE_CLASS(lut3d); |
|
576 |
+ |
|
560 | 577 |
static const AVFilterPad lut3d_inputs[] = { |
561 | 578 |
{ |
562 | 579 |
.name = "default", |
... | ... |
@@ -579,10 +605,192 @@ AVFilter avfilter_vf_lut3d = { |
579 | 579 |
.name = "lut3d", |
580 | 580 |
.description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."), |
581 | 581 |
.priv_size = sizeof(LUT3DContext), |
582 |
- .init = init, |
|
582 |
+ .init = lut3d_init, |
|
583 | 583 |
.query_formats = query_formats, |
584 | 584 |
.inputs = lut3d_inputs, |
585 | 585 |
.outputs = lut3d_outputs, |
586 | 586 |
.priv_class = &lut3d_class, |
587 | 587 |
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, |
588 | 588 |
}; |
589 |
+#endif |
|
590 |
+ |
|
591 |
+#if CONFIG_HALDCLUT_FILTER |
|
592 |
+ |
|
593 |
+static void update_clut(LUT3DContext *lut3d, const AVFrame *frame) |
|
594 |
+{ |
|
595 |
+ const uint8_t *data = frame->data[0]; |
|
596 |
+ const int linesize = frame->linesize[0]; |
|
597 |
+ const int w = lut3d->clut_width; |
|
598 |
+ const int step = lut3d->clut_step; |
|
599 |
+ const uint8_t *rgba_map = lut3d->clut_rgba_map; |
|
600 |
+ const int level = lut3d->lutsize; |
|
601 |
+ |
|
602 |
+#define LOAD_CLUT(nbits) do { \ |
|
603 |
+ int i, j, k, x = 0, y = 0; \ |
|
604 |
+ \ |
|
605 |
+ for (k = 0; k < level; k++) { \ |
|
606 |
+ for (j = 0; j < level; j++) { \ |
|
607 |
+ for (i = 0; i < level; i++) { \ |
|
608 |
+ const uint##nbits##_t *src = (const uint##nbits##_t *) \ |
|
609 |
+ (data + y*linesize + x*step); \ |
|
610 |
+ struct rgbvec *vec = &lut3d->lut[k][j][i]; \ |
|
611 |
+ vec->r = src[rgba_map[0]] / (float)((1<<(nbits)) - 1); \ |
|
612 |
+ vec->g = src[rgba_map[1]] / (float)((1<<(nbits)) - 1); \ |
|
613 |
+ vec->b = src[rgba_map[2]] / (float)((1<<(nbits)) - 1); \ |
|
614 |
+ if (++x == w) { \ |
|
615 |
+ x = 0; \ |
|
616 |
+ y++; \ |
|
617 |
+ } \ |
|
618 |
+ } \ |
|
619 |
+ } \ |
|
620 |
+ } \ |
|
621 |
+} while (0) |
|
622 |
+ |
|
623 |
+ if (!lut3d->clut_is16bit) LOAD_CLUT(8); |
|
624 |
+ else LOAD_CLUT(16); |
|
625 |
+} |
|
626 |
+ |
|
627 |
+ |
|
628 |
+static int config_output(AVFilterLink *outlink) |
|
629 |
+{ |
|
630 |
+ AVFilterContext *ctx = outlink->src; |
|
631 |
+ |
|
632 |
+ outlink->w = ctx->inputs[0]->w; |
|
633 |
+ outlink->h = ctx->inputs[0]->h; |
|
634 |
+ outlink->time_base = ctx->inputs[0]->time_base; |
|
635 |
+ return 0; |
|
636 |
+} |
|
637 |
+ |
|
638 |
+static int filter_frame_main(AVFilterLink *inlink, AVFrame *inpicref) |
|
639 |
+{ |
|
640 |
+ LUT3DContext *s = inlink->dst->priv; |
|
641 |
+ return ff_dualinput_filter_frame_main(&s->dinput, inlink, inpicref); |
|
642 |
+} |
|
643 |
+ |
|
644 |
+static int filter_frame_clut(AVFilterLink *inlink, AVFrame *inpicref) |
|
645 |
+{ |
|
646 |
+ LUT3DContext *s = inlink->dst->priv; |
|
647 |
+ return ff_dualinput_filter_frame_second(&s->dinput, inlink, inpicref); |
|
648 |
+} |
|
649 |
+ |
|
650 |
+static int request_frame(AVFilterLink *outlink) |
|
651 |
+{ |
|
652 |
+ LUT3DContext *s = outlink->src->priv; |
|
653 |
+ return ff_dualinput_request_frame(&s->dinput, outlink); |
|
654 |
+} |
|
655 |
+ |
|
656 |
+static int config_clut(AVFilterLink *inlink) |
|
657 |
+{ |
|
658 |
+ int size, level, w, h; |
|
659 |
+ AVFilterContext *ctx = inlink->dst; |
|
660 |
+ LUT3DContext *lut3d = ctx->priv; |
|
661 |
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
|
662 |
+ |
|
663 |
+ lut3d->clut_is16bit = 0; |
|
664 |
+ switch (inlink->format) { |
|
665 |
+ case AV_PIX_FMT_RGB48: |
|
666 |
+ case AV_PIX_FMT_BGR48: |
|
667 |
+ case AV_PIX_FMT_RGBA64: |
|
668 |
+ case AV_PIX_FMT_BGRA64: |
|
669 |
+ lut3d->clut_is16bit = 1; |
|
670 |
+ } |
|
671 |
+ |
|
672 |
+ lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3; |
|
673 |
+ ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format); |
|
674 |
+ |
|
675 |
+ if (inlink->w > inlink->h) |
|
676 |
+ av_log(ctx, AV_LOG_INFO, "Padding on the right (%dpx) of the " |
|
677 |
+ "Hald CLUT will be ignored\n", inlink->w - inlink->h); |
|
678 |
+ else if (inlink->w < inlink->h) |
|
679 |
+ av_log(ctx, AV_LOG_INFO, "Padding at the bottom (%dpx) of the " |
|
680 |
+ "Hald CLUT will be ignored\n", inlink->h - inlink->w); |
|
681 |
+ lut3d->clut_width = w = h = FFMIN(inlink->w, inlink->h); |
|
682 |
+ |
|
683 |
+ for (level = 1; level*level*level < w; level++); |
|
684 |
+ size = level*level*level; |
|
685 |
+ if (size != w) { |
|
686 |
+ av_log(ctx, AV_LOG_WARNING, "The Hald CLUT width does not match the level\n"); |
|
687 |
+ return AVERROR_INVALIDDATA; |
|
688 |
+ } |
|
689 |
+ av_assert0(w == h && w == size); |
|
690 |
+ level *= level; |
|
691 |
+ if (level > MAX_LEVEL) { |
|
692 |
+ const int max_clut_level = sqrt(MAX_LEVEL); |
|
693 |
+ const int max_clut_size = max_clut_level*max_clut_level*max_clut_level; |
|
694 |
+ av_log(ctx, AV_LOG_ERROR, "Too large Hald CLUT " |
|
695 |
+ "(maximum level is %d, or %dx%d CLUT)\n", |
|
696 |
+ max_clut_level, max_clut_size, max_clut_size); |
|
697 |
+ return AVERROR(EINVAL); |
|
698 |
+ } |
|
699 |
+ lut3d->lutsize = level; |
|
700 |
+ |
|
701 |
+ return 0; |
|
702 |
+} |
|
703 |
+ |
|
704 |
+static AVFrame *update_apply_clut(AVFilterContext *ctx, AVFrame *main, |
|
705 |
+ const AVFrame *second) |
|
706 |
+{ |
|
707 |
+ AVFilterLink *inlink = ctx->inputs[0]; |
|
708 |
+ update_clut(ctx->priv, second); |
|
709 |
+ return apply_lut(inlink, main); |
|
710 |
+} |
|
711 |
+ |
|
712 |
+static av_cold int haldclut_init(AVFilterContext *ctx) |
|
713 |
+{ |
|
714 |
+ LUT3DContext *lut3d = ctx->priv; |
|
715 |
+ lut3d->dinput.process = update_apply_clut; |
|
716 |
+ return 0; |
|
717 |
+} |
|
718 |
+ |
|
719 |
+static av_cold void haldclut_uninit(AVFilterContext *ctx) |
|
720 |
+{ |
|
721 |
+ LUT3DContext *lut3d = ctx->priv; |
|
722 |
+ ff_dualinput_uninit(&lut3d->dinput); |
|
723 |
+} |
|
724 |
+ |
|
725 |
+static const AVOption haldclut_options[] = { |
|
726 |
+ { "shortest", "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, |
|
727 |
+ { "repeatlast", "continue applying the last clut after eos", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS }, |
|
728 |
+ COMMON_OPTIONS |
|
729 |
+}; |
|
730 |
+ |
|
731 |
+AVFILTER_DEFINE_CLASS(haldclut); |
|
732 |
+ |
|
733 |
+static const AVFilterPad haldclut_inputs[] = { |
|
734 |
+ { |
|
735 |
+ .name = "main", |
|
736 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
737 |
+ .filter_frame = filter_frame_main, |
|
738 |
+ .config_props = config_input, |
|
739 |
+ },{ |
|
740 |
+ .name = "clut", |
|
741 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
742 |
+ .filter_frame = filter_frame_clut, |
|
743 |
+ .config_props = config_clut, |
|
744 |
+ }, |
|
745 |
+ { NULL } |
|
746 |
+}; |
|
747 |
+ |
|
748 |
+static const AVFilterPad haldclut_outputs[] = { |
|
749 |
+ { |
|
750 |
+ .name = "default", |
|
751 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
752 |
+ .request_frame = request_frame, |
|
753 |
+ .config_props = config_output, |
|
754 |
+ }, |
|
755 |
+ { NULL } |
|
756 |
+}; |
|
757 |
+ |
|
758 |
+AVFilter avfilter_vf_haldclut = { |
|
759 |
+ .name = "haldclut", |
|
760 |
+ .description = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."), |
|
761 |
+ .priv_size = sizeof(LUT3DContext), |
|
762 |
+ .init = haldclut_init, |
|
763 |
+ .uninit = haldclut_uninit, |
|
764 |
+ .query_formats = query_formats, |
|
765 |
+ .inputs = haldclut_inputs, |
|
766 |
+ .outputs = haldclut_outputs, |
|
767 |
+ .priv_class = &haldclut_class, |
|
768 |
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, |
|
769 |
+}; |
|
770 |
+#endif |