History

avfilter: add vf_yadif_videotoolbox

deinterlaces CVPixelBuffers, i.e. AV_PIX_FMT_VIDEOTOOLBOX frames for example, an interlaced mpeg2 video can be decoded by avcodec, uploaded into a CVPixelBuffer, deinterlaced by Metal, and then encoded to h264 by VideoToolbox as follows: ffmpeg \ -init_hw_device videotoolbox \ -i interlaced.ts \ -vf hwupload,yadif_videotoolbox \ -c:v h264_videotoolbox \ -b:v 2000k \ -c:a copy \ -y progressive.ts (note that uploading AVFrame into CVPixelBuffer via hwupload requires 504c60660d3194758823ddd45ceddb86e35d806f) this work is sponsored by Fancy Bits LLC

Aman Karmani committed 4 years ago

Commit 4ac869c1 parent ecee6af6 files · +682 −0Browse files

Showing the changes to libavfilter (4 of 6 files). Show all

Unified · Split

4 files
  1. libavfilter/Makefile +4 −0
  2. libavfilter/allfilters.c +1 −0
  3. libavfilter/metal/vf_yadif_videotoolbox.metal +269 −0
  4. libavfilter/vf_yadif_videotoolbox.m +406 −0
libavfilter/Makefile+4−0
@@ -519,6 +519,10 @@ OBJS-$(CONFIG_XSTACK_FILTER) += vf_stack.o framesync.o · 518 unmodified lines
519OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o yadif_common.o519OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o yadif_common.o
520OBJS-$(CONFIG_YADIF_CUDA_FILTER) += vf_yadif_cuda.o vf_yadif_cuda.ptx.o \520OBJS-$(CONFIG_YADIF_CUDA_FILTER) += vf_yadif_cuda.o vf_yadif_cuda.ptx.o \
521 yadif_common.o cuda/load_helper.o521 yadif_common.o cuda/load_helper.o
522OBJS-$(CONFIG_YADIF_VIDEOTOOLBOX_FILTER) += vf_yadif_videotoolbox.o \
523 metal/vf_yadif_videotoolbox.metallib.o \
524 metal/utils.o \
525 yadif_common.o
522OBJS-$(CONFIG_YAEPBLUR_FILTER) += vf_yaepblur.o526OBJS-$(CONFIG_YAEPBLUR_FILTER) += vf_yaepblur.o
523OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o527OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o
524OBJS-$(CONFIG_ZOOMPAN_FILTER) += vf_zoompan.o528OBJS-$(CONFIG_ZOOMPAN_FILTER) += vf_zoompan.o
libavfilter/allfilters.c+1−0
@@ -496,6 +496,7 @@ extern const AVFilter ff_vf_xmedian; · 495 unmodified lines
496extern const AVFilter ff_vf_xstack;496extern const AVFilter ff_vf_xstack;
497extern const AVFilter ff_vf_yadif;497extern const AVFilter ff_vf_yadif;
498extern const AVFilter ff_vf_yadif_cuda;498extern const AVFilter ff_vf_yadif_cuda;
499extern const AVFilter ff_vf_yadif_videotoolbox;
499extern const AVFilter ff_vf_yaepblur;500extern const AVFilter ff_vf_yaepblur;
500extern const AVFilter ff_vf_zmq;501extern const AVFilter ff_vf_zmq;
501extern const AVFilter ff_vf_zoompan;502extern const AVFilter ff_vf_zoompan;
libavfilter/metal/vf_yadif_videotoolbox.metal+269−0
@@ -0,0 +1,269 @@
1/*
2 * Copyright (C) 2018 Philip Langdale <philipl@overt.org>
3 * 2020 Aman Karmani <aman@tmm1.net>
4 * 2020 Stefan Dyulgerov <stefan.dyulgerov@gmail.com>
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include <metal_stdlib>
24#include <metal_integer>
25#include <metal_texture>
26
27using namespace metal;
28
29/*
30 * Parameters
31 */
32
33struct deintParams {
34 uint channels;
35 uint parity;
36 uint tff;
37 bool is_second_field;
38 bool skip_spatial_check;
39 int field_mode;
40};
41
42/*
43 * Texture access helpers
44 */
45
46#define accesstype access::sample
47const sampler s(coord::pixel);
48
49template <typename T>
50T tex2D(texture2d<float, access::sample> tex, uint x, uint y)
51{
52 return tex.sample(s, float2(x, y)).x;
53}
54
55template <>
56float2 tex2D<float2>(texture2d<float, access::sample> tex, uint x, uint y)
57{
58 return tex.sample(s, float2(x, y)).xy;
59}
60
61template <typename T>
62T tex2D(texture2d<float, access::read> tex, uint x, uint y)
63{
64 return tex.read(uint2(x, y)).x;
65}
66
67template <>
68float2 tex2D<float2>(texture2d<float, access::read> tex, uint x, uint y)
69{
70 return tex.read(uint2(x, y)).xy;
71}
72
73/*
74 * YADIF helpers
75 */
76
77template<typename T>
78T spatial_predictor(T a, T b, T c, T d, T e, T f, T g,
79 T h, T i, T j, T k, T l, T m, T n)
80{
81 T spatial_pred = (d + k)/2;
82 T spatial_score = abs(c - j) + abs(d - k) + abs(e - l);
83
84 T score = abs(b - k) + abs(c - l) + abs(d - m);
85 if (score < spatial_score) {
86 spatial_pred = (c + l)/2;
87 spatial_score = score;
88 score = abs(a - l) + abs(b - m) + abs(c - n);
89 if (score < spatial_score) {
90 spatial_pred = (b + m)/2;
91 spatial_score = score;
92 }
93 }
94 score = abs(d - i) + abs(e - j) + abs(f - k);
95 if (score < spatial_score) {
96 spatial_pred = (e + j)/2;
97 spatial_score = score;
98 score = abs(e - h) + abs(f - i) + abs(g - j);
99 if (score < spatial_score) {
100 spatial_pred = (f + i)/2;
101 spatial_score = score;
102 }
103 }
104 return spatial_pred;
105}
106
107template<typename T>
108T temporal_predictor(T A, T B, T C, T D, T E, T F,
109 T G, T H, T I, T J, T K, T L,
110 T spatial_pred, bool skip_check)
111{
112 T p0 = (C + H) / 2;
113 T p1 = F;
114 T p2 = (D + I) / 2;
115 T p3 = G;
116 T p4 = (E + J) / 2;
117
118 T tdiff0 = abs(D - I);
119 T tdiff1 = (abs(A - F) + abs(B - G)) / 2;
120 T tdiff2 = (abs(K - F) + abs(G - L)) / 2;
121
122 T diff = max3(tdiff0, tdiff1, tdiff2);
123
124 if (!skip_check) {
125 T maxi = max3(p2 - p3, p2 - p1, min(p0 - p1, p4 - p3));
126 T mini = min3(p2 - p3, p2 - p1, max(p0 - p1, p4 - p3));
127 diff = max3(diff, mini, -maxi);
128 }
129
130 return clamp(spatial_pred, p2 - diff, p2 + diff);
131}
132
133#define T float2
134template <>
135T spatial_predictor<T>(T a, T b, T c, T d, T e, T f, T g,
136 T h, T i, T j, T k, T l, T m, T n)
137{
138 return T(
139 spatial_predictor(a.x, b.x, c.x, d.x, e.x, f.x, g.x,
140 h.x, i.x, j.x, k.x, l.x, m.x, n.x),
141 spatial_predictor(a.y, b.y, c.y, d.y, e.y, f.y, g.y,
142 h.y, i.y, j.y, k.y, l.y, m.y, n.y)
143 );
144}
145
146template <>
147T temporal_predictor<T>(T A, T B, T C, T D, T E, T F,
148 T G, T H, T I, T J, T K, T L,
149 T spatial_pred, bool skip_check)
150{
151 return T(
152 temporal_predictor(A.x, B.x, C.x, D.x, E.x, F.x,
153 G.x, H.x, I.x, J.x, K.x, L.x,
154 spatial_pred.x, skip_check),
155 temporal_predictor(A.y, B.y, C.y, D.y, E.y, F.y,
156 G.y, H.y, I.y, J.y, K.y, L.y,
157 spatial_pred.y, skip_check)
158 );
159}
160#undef T
161
162/*
163 * YADIF compute
164 */
165
166template <typename T>
167T yadif_compute_spatial(
168 texture2d<float, accesstype> cur,
169 uint2 pos)
170{
171 // Calculate spatial prediction
172 T a = tex2D<T>(cur, pos.x - 3, pos.y - 1);
173 T b = tex2D<T>(cur, pos.x - 2, pos.y - 1);
174 T c = tex2D<T>(cur, pos.x - 1, pos.y - 1);
175 T d = tex2D<T>(cur, pos.x - 0, pos.y - 1);
176 T e = tex2D<T>(cur, pos.x + 1, pos.y - 1);
177 T f = tex2D<T>(cur, pos.x + 2, pos.y - 1);
178 T g = tex2D<T>(cur, pos.x + 3, pos.y - 1);
179
180 T h = tex2D<T>(cur, pos.x - 3, pos.y + 1);
181 T i = tex2D<T>(cur, pos.x - 2, pos.y + 1);
182 T j = tex2D<T>(cur, pos.x - 1, pos.y + 1);
183 T k = tex2D<T>(cur, pos.x - 0, pos.y + 1);
184 T l = tex2D<T>(cur, pos.x + 1, pos.y + 1);
185 T m = tex2D<T>(cur, pos.x + 2, pos.y + 1);
186 T n = tex2D<T>(cur, pos.x + 3, pos.y + 1);
187
188 return spatial_predictor(a, b, c, d, e, f, g,
189 h, i, j, k, l, m, n);
190}
191
192template <typename T>
193T yadif_compute_temporal(
194 texture2d<float, accesstype> cur,
195 texture2d<float, accesstype> prev2,
196 texture2d<float, accesstype> prev1,
197 texture2d<float, accesstype> next1,
198 texture2d<float, accesstype> next2,
199 T spatial_pred,
200 bool skip_spatial_check,
201 uint2 pos)
202{
203 // Calculate temporal prediction
204 T A = tex2D<T>(prev2, pos.x, pos.y - 1);
205 T B = tex2D<T>(prev2, pos.x, pos.y + 1);
206 T C = tex2D<T>(prev1, pos.x, pos.y - 2);
207 T D = tex2D<T>(prev1, pos.x, pos.y + 0);
208 T E = tex2D<T>(prev1, pos.x, pos.y + 2);
209 T F = tex2D<T>(cur, pos.x, pos.y - 1);
210 T G = tex2D<T>(cur, pos.x, pos.y + 1);
211 T H = tex2D<T>(next1, pos.x, pos.y - 2);
212 T I = tex2D<T>(next1, pos.x, pos.y + 0);
213 T J = tex2D<T>(next1, pos.x, pos.y + 2);
214 T K = tex2D<T>(next2, pos.x, pos.y - 1);
215 T L = tex2D<T>(next2, pos.x, pos.y + 1);
216
217 return temporal_predictor(A, B, C, D, E, F, G, H, I, J, K, L,
218 spatial_pred, skip_spatial_check);
219}
220
221template <typename T>
222T yadif(
223 texture2d<float, access::write> dst,
224 texture2d<float, accesstype> prev,
225 texture2d<float, accesstype> cur,
226 texture2d<float, accesstype> next,
227 constant deintParams& params,
228 uint2 pos)
229{
230 T spatial_pred = yadif_compute_spatial<T>(cur, pos);
231
232 if (params.is_second_field) {
233 return yadif_compute_temporal(cur, prev, cur, next, next, spatial_pred, params.skip_spatial_check, pos);
234 } else {
235 return yadif_compute_temporal(cur, prev, prev, cur, next, spatial_pred, params.skip_spatial_check, pos);
236 }
237}
238
239/*
240 * Kernel dispatch
241 */
242
243kernel void deint(
244 texture2d<float, access::write> dst [[texture(0)]],
245 texture2d<float, accesstype> prev [[texture(1)]],
246 texture2d<float, accesstype> cur [[texture(2)]],
247 texture2d<float, accesstype> next [[texture(3)]],
248 constant deintParams& params [[buffer(4)]],
249 uint2 pos [[thread_position_in_grid]])
250{
251 if ((pos.x >= dst.get_width()) ||
252 (pos.y >= dst.get_height())) {
253 return;
254 }
255
256 // Don't modify the primary field
257 if (pos.y % 2 == params.parity) {
258 float4 in = cur.read(pos);
259 dst.write(in, pos);
260 return;
261 }
262
263 float2 pred;
264 if (params.channels == 1)
265 pred = float2(yadif<float>(dst, prev, cur, next, params, pos));
266 else
267 pred = yadif<float2>(dst, prev, cur, next, params, pos);
268 dst.write(pred.xyyy, pos);
269}
libavfilter/vf_yadif_videotoolbox.m+406−0
@@ -0,0 +1,406 @@
1/*
2 * Copyright (C) 2018 Philip Langdale <philipl@overt.org>
3 * 2020 Aman Karmani <aman@tmm1.net>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "internal.h"
23#include "yadif.h"
24#include <libavutil/avassert.h>
25#include <libavutil/hwcontext.h>
26#include <libavutil/objc.h>
27#include <libavfilter/metal/utils.h>
28
29extern char ff_vf_yadif_videotoolbox_metallib_data[];
30extern unsigned int ff_vf_yadif_videotoolbox_metallib_len;
31
32typedef struct YADIFVTContext {
33 YADIFContext yadif;
34
35 AVBufferRef *device_ref;
36 AVBufferRef *input_frames_ref;
37 AVHWFramesContext *input_frames;
38
39 id<MTLDevice> mtlDevice;
40 id<MTLLibrary> mtlLibrary;
41 id<MTLCommandQueue> mtlQueue;
42 id<MTLComputePipelineState> mtlPipeline;
43 id<MTLFunction> mtlFunction;
44 id<MTLBuffer> mtlParamsBuffer;
45
46 CVMetalTextureCacheRef textureCache;
47} YADIFVTContext;
48
49struct mtlYadifParams {
50 uint channels;
51 uint parity;
52 uint tff;
53 bool is_second_field;
54 bool skip_spatial_check;
55 int field_mode;
56};
57
58static void call_kernel(AVFilterContext *ctx,
59 id<MTLTexture> dst,
60 id<MTLTexture> prev,
61 id<MTLTexture> cur,
62 id<MTLTexture> next,
63 int channels,
64 int parity,
65 int tff)
66{
67 YADIFVTContext *s = ctx->priv;
68 id<MTLCommandBuffer> buffer = s->mtlQueue.commandBuffer;
69 id<MTLComputeCommandEncoder> encoder = buffer.computeCommandEncoder;
70 struct mtlYadifParams *params = (struct mtlYadifParams *)s->mtlParamsBuffer.contents;
71 *params = (struct mtlYadifParams){
72 .channels = channels,
73 .parity = parity,
74 .tff = tff,
75 .is_second_field = !(parity ^ tff),
76 .skip_spatial_check = s->yadif.mode&2,
77 .field_mode = s->yadif.current_field
78 };
79
80 [encoder setTexture:dst atIndex:0];
81 [encoder setTexture:prev atIndex:1];
82 [encoder setTexture:cur atIndex:2];
83 [encoder setTexture:next atIndex:3];
84 [encoder setBuffer:s->mtlParamsBuffer offset:0 atIndex:4];
85 ff_metal_compute_encoder_dispatch(s->mtlDevice, s->mtlPipeline, encoder, dst.width, dst.height);
86 [encoder endEncoding];
87
88 [buffer commit];
89 [buffer waitUntilCompleted];
90
91 ff_objc_release(&encoder);
92 ff_objc_release(&buffer);
93}
94
95static void filter(AVFilterContext *ctx, AVFrame *dst,
96 int parity, int tff)
97{
98 YADIFVTContext *s = ctx->priv;
99 YADIFContext *y = &s->yadif;
100 int i;
101
102 for (i = 0; i < y->csp->nb_components; i++) {
103 int pixel_size, channels;
104 const AVComponentDescriptor *comp = &y->csp->comp[i];
105 CVMetalTextureRef prev, cur, next, dest;
106 id<MTLTexture> tex_prev, tex_cur, tex_next, tex_dest;
107 MTLPixelFormat format;
108
109 if (comp->plane < i) {
110 // We process planes as a whole, so don't reprocess
111 // them for additional components
112 continue;
113 }
114
115 pixel_size = (comp->depth + comp->shift) / 8;
116 channels = comp->step / pixel_size;
117 if (pixel_size > 2 || channels > 2) {
118 av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", y->csp->name);
119 goto exit;
120 }
121 switch (pixel_size) {
122 case 1:
123 format = channels == 1 ? MTLPixelFormatR8Unorm : MTLPixelFormatRG8Unorm;
124 break;
125 case 2:
126 format = channels == 1 ? MTLPixelFormatR16Unorm : MTLPixelFormatRG16Unorm;
127 break;
128 default:
129 av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", y->csp->name);
130 goto exit;
131 }
132 av_log(ctx, AV_LOG_TRACE,
133 "Deinterlacing plane %d: pixel_size: %d channels: %d\n",
134 comp->plane, pixel_size, channels);
135
136 prev = ff_metal_texture_from_pixbuf(ctx, s->textureCache, (CVPixelBufferRef)y->prev->data[3], i, format);
137 cur = ff_metal_texture_from_pixbuf(ctx, s->textureCache, (CVPixelBufferRef)y->cur->data[3], i, format);
138 next = ff_metal_texture_from_pixbuf(ctx, s->textureCache, (CVPixelBufferRef)y->next->data[3], i, format);
139 dest = ff_metal_texture_from_pixbuf(ctx, s->textureCache, (CVPixelBufferRef)dst->data[3], i, format);
140
141 tex_prev = CVMetalTextureGetTexture(prev);
142 tex_cur = CVMetalTextureGetTexture(cur);
143 tex_next = CVMetalTextureGetTexture(next);
144 tex_dest = CVMetalTextureGetTexture(dest);
145
146 call_kernel(ctx, tex_dest, tex_prev, tex_cur, tex_next,
147 channels, parity, tff);
148
149 CFRelease(prev);
150 CFRelease(cur);
151 CFRelease(next);
152 CFRelease(dest);
153 }
154
155 CVBufferPropagateAttachments((CVPixelBufferRef)y->cur->data[3], (CVPixelBufferRef)dst->data[3]);
156
157 if (y->current_field == YADIF_FIELD_END) {
158 y->current_field = YADIF_FIELD_NORMAL;
159 }
160
161exit:
162 return;
163}
164
165static av_cold void yadif_videotoolbox_uninit(AVFilterContext *ctx)
166{
167 YADIFVTContext *s = ctx->priv;
168 YADIFContext *y = &s->yadif;
169
170 av_frame_free(&y->prev);
171 av_frame_free(&y->cur);
172 av_frame_free(&y->next);
173
174 av_buffer_unref(&s->device_ref);
175 av_buffer_unref(&s->input_frames_ref);
176 s->input_frames = NULL;
177
178 ff_objc_release(&s->mtlParamsBuffer);
179 ff_objc_release(&s->mtlFunction);
180 ff_objc_release(&s->mtlPipeline);
181 ff_objc_release(&s->mtlQueue);
182 ff_objc_release(&s->mtlLibrary);
183 ff_objc_release(&s->mtlDevice);
184
185 if (s->textureCache) {
186 CFRelease(s->textureCache);
187 s->textureCache = NULL;
188 }
189}
190
191static av_cold int yadif_videotoolbox_init(AVFilterContext *ctx)
192{
193 YADIFVTContext *s = ctx->priv;
194 NSError *err = nil;
195 CVReturn ret;
196
197 s->mtlDevice = MTLCreateSystemDefaultDevice();
198 if (!s->mtlDevice) {
199 av_log(ctx, AV_LOG_ERROR, "Unable to find Metal device\n");
200 goto fail;
201 }
202
203 av_log(ctx, AV_LOG_INFO, "Using Metal device: %s\n", s->mtlDevice.name.UTF8String);
204
205 dispatch_data_t libData = dispatch_data_create(
206 ff_vf_yadif_videotoolbox_metallib_data,
207 ff_vf_yadif_videotoolbox_metallib_len,
208 nil,
209 nil);
210 s->mtlLibrary = [s->mtlDevice newLibraryWithData:libData error:&err];
211 dispatch_release(libData);
212 libData = nil;
213 if (err) {
214 av_log(ctx, AV_LOG_ERROR, "Failed to load Metal library: %s\n", err.description.UTF8String);
215 goto fail;
216 }
217
218 s->mtlFunction = [s->mtlLibrary newFunctionWithName:@"deint"];
219 if (!s->mtlFunction) {
220 av_log(ctx, AV_LOG_ERROR, "Failed to create Metal function!\n");
221 goto fail;
222 }
223
224 s->mtlQueue = s->mtlDevice.newCommandQueue;
225 if (!s->mtlQueue) {
226 av_log(ctx, AV_LOG_ERROR, "Failed to create Metal command queue!\n");
227 goto fail;
228 }
229
230 s->mtlPipeline = [s->mtlDevice
231 newComputePipelineStateWithFunction:s->mtlFunction
232 error:&err];
233 if (err) {
234 av_log(ctx, AV_LOG_ERROR, "Failed to create Metal compute pipeline: %s\n", err.description.UTF8String);
235 goto fail;
236 }
237
238 s->mtlParamsBuffer = [s->mtlDevice
239 newBufferWithLength:sizeof(struct mtlYadifParams)
240 options:MTLResourceStorageModeShared];
241 if (!s->mtlParamsBuffer) {
242 av_log(ctx, AV_LOG_ERROR, "Failed to create Metal buffer for parameters\n");
243 goto fail;
244 }
245
246 ret = CVMetalTextureCacheCreate(
247 NULL,
248 NULL,
249 s->mtlDevice,
250 NULL,
251 &s->textureCache
252 );
253 if (ret != kCVReturnSuccess) {
254 av_log(ctx, AV_LOG_ERROR, "Failed to create CVMetalTextureCache: %d\n", ret);
255 goto fail;
256 }
257
258 return 0;
259fail:
260 yadif_videotoolbox_uninit(ctx);
261 return AVERROR_EXTERNAL;
262}
263
264static int config_input(AVFilterLink *inlink)
265{
266 AVFilterContext *ctx = inlink->dst;
267 YADIFVTContext *s = ctx->priv;
268
269 if (!inlink->hw_frames_ctx) {
270 av_log(ctx, AV_LOG_ERROR, "A hardware frames reference is "
271 "required to associate the processing device.\n");
272 return AVERROR(EINVAL);
273 }
274
275 s->input_frames_ref = av_buffer_ref(inlink->hw_frames_ctx);
276 if (!s->input_frames_ref) {
277 av_log(ctx, AV_LOG_ERROR, "A input frames reference create "
278 "failed.\n");
279 return AVERROR(ENOMEM);
280 }
281 s->input_frames = (AVHWFramesContext*)s->input_frames_ref->data;
282
283 return 0;
284}
285
286static int config_output(AVFilterLink *link)
287{
288 AVHWFramesContext *output_frames;
289 AVFilterContext *ctx = link->src;
290 YADIFVTContext *s = ctx->priv;
291 YADIFContext *y = &s->yadif;
292 int ret = 0;
293
294 av_assert0(s->input_frames);
295 s->device_ref = av_buffer_ref(s->input_frames->device_ref);
296 if (!s->device_ref) {
297 av_log(ctx, AV_LOG_ERROR, "A device reference create "
298 "failed.\n");
299 return AVERROR(ENOMEM);
300 }
301
302 link->hw_frames_ctx = av_hwframe_ctx_alloc(s->device_ref);
303 if (!link->hw_frames_ctx) {
304 av_log(ctx, AV_LOG_ERROR, "Failed to create HW frame context "
305 "for output.\n");
306 ret = AVERROR(ENOMEM);
307 goto exit;
308 }
309
310 output_frames = (AVHWFramesContext*)link->hw_frames_ctx->data;
311
312 output_frames->format = AV_PIX_FMT_VIDEOTOOLBOX;
313 output_frames->sw_format = s->input_frames->sw_format;
314 output_frames->width = ctx->inputs[0]->w;
315 output_frames->height = ctx->inputs[0]->h;
316
317 ret = ff_filter_init_hw_frames(ctx, link, 10);
318 if (ret < 0)
319 goto exit;
320
321 ret = av_hwframe_ctx_init(link->hw_frames_ctx);
322 if (ret < 0) {
323 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VideoToolbox frame "
324 "context for output: %d\n", ret);
325 goto exit;
326 }
327
328 link->time_base.num = ctx->inputs[0]->time_base.num;
329 link->time_base.den = ctx->inputs[0]->time_base.den * 2;
330 link->w = ctx->inputs[0]->w;
331 link->h = ctx->inputs[0]->h;
332
333 if(y->mode & 1)
334 link->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate,
335 (AVRational){2, 1});
336
337 if (link->w < 3 || link->h < 3) {
338 av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n");
339 ret = AVERROR(EINVAL);
340 goto exit;
341 }
342
343 y->csp = av_pix_fmt_desc_get(output_frames->sw_format);
344 y->filter = filter;
345
346exit:
347 return ret;
348}
349
350#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
351#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit }
352
353static const AVOption yadif_videotoolbox_options[] = {
354 #define OFFSET(x) offsetof(YADIFContext, x)
355 { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FRAME}, 0, 3, FLAGS, "mode"},
356 CONST("send_frame", "send one frame for each frame", YADIF_MODE_SEND_FRAME, "mode"),
357 CONST("send_field", "send one frame for each field", YADIF_MODE_SEND_FIELD, "mode"),
358 CONST("send_frame_nospatial", "send one frame for each frame, but skip spatial interlacing check", YADIF_MODE_SEND_FRAME_NOSPATIAL, "mode"),
359 CONST("send_field_nospatial", "send one frame for each field, but skip spatial interlacing check", YADIF_MODE_SEND_FIELD_NOSPATIAL, "mode"),
360
361 { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, "parity" },
362 CONST("tff", "assume top field first", YADIF_PARITY_TFF, "parity"),
363 CONST("bff", "assume bottom field first", YADIF_PARITY_BFF, "parity"),
364 CONST("auto", "auto detect parity", YADIF_PARITY_AUTO, "parity"),
365
366 { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, "deint" },
367 CONST("all", "deinterlace all frames", YADIF_DEINT_ALL, "deint"),
368 CONST("interlaced", "only deinterlace frames marked as interlaced", YADIF_DEINT_INTERLACED, "deint"),
369 #undef OFFSET
370
371 { NULL }
372};
373
374AVFILTER_DEFINE_CLASS(yadif_videotoolbox);
375
376static const AVFilterPad yadif_videotoolbox_inputs[] = {
377 {
378 .name = "default",
379 .type = AVMEDIA_TYPE_VIDEO,
380 .filter_frame = ff_yadif_filter_frame,
381 .config_props = config_input,
382 },
383};
384
385static const AVFilterPad yadif_videotoolbox_outputs[] = {
386 {
387 .name = "default",
388 .type = AVMEDIA_TYPE_VIDEO,
389 .request_frame = ff_yadif_request_frame,
390 .config_props = config_output,
391 },
392};
393
394AVFilter ff_vf_yadif_videotoolbox = {
395 .name = "yadif_videotoolbox",
396 .description = NULL_IF_CONFIG_SMALL("YADIF for VideoToolbox frames using Metal compute"),
397 .priv_size = sizeof(YADIFVTContext),
398 .priv_class = &yadif_videotoolbox_class,
399 .init = yadif_videotoolbox_init,
400 .uninit = yadif_videotoolbox_uninit,
401 FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VIDEOTOOLBOX),
402 FILTER_INPUTS(yadif_videotoolbox_inputs),
403 FILTER_OUTPUTS(yadif_videotoolbox_outputs),
404 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
405 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
406};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t