FFmpeg
Loading...
Searching...
No Matches
vf_transpose_vt.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Zhao Zhili <zhilizhao@tencent.com>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <VideoToolbox/VideoToolbox.h>
22
23#include "libavutil/hwcontext.h"
25#include "libavutil/opt.h"
26#include "libavutil/pixdesc.h"
27
28#include "filters.h"
29#include "transpose.h"
30#include "video.h"
31
32typedef struct TransposeVtContext {
33 AVClass *class;
34
35 VTPixelRotationSessionRef session;
36 int dir;
39
41{
42 TransposeVtContext *s = avctx->priv;
43 int ret;
44
45 ret = VTPixelRotationSessionCreate(kCFAllocatorDefault, &s->session);
46 if (ret != noErr) {
47 av_log(avctx, AV_LOG_ERROR, "Rotation session create failed, %d\n", ret);
48 return AVERROR_EXTERNAL;
49 }
50
51 return 0;
52}
53
55{
56 TransposeVtContext *s = avctx->priv;
57
58 if (s->session) {
59 VTPixelRotationSessionInvalidate(s->session);
60 CFRelease(s->session);
61 s->session = NULL;
62 }
63}
64
66{
67 int ret;
68 AVFilterContext *ctx = link->dst;
69 TransposeVtContext *s = ctx->priv;
70 AVFilterLink *outlink = ctx->outputs[0];
71 CVPixelBufferRef src;
72 CVPixelBufferRef dst;
73 AVFrame *out;
74
75 if (s->passthrough)
76 return ff_filter_frame(outlink, in);
77
78 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
79 if (!out) {
80 ret = AVERROR(ENOMEM);
81 goto fail;
82 }
83
84 ret = av_frame_copy_props(out, in);
85 if (ret < 0)
86 goto fail;
87
88 src = (CVPixelBufferRef)in->data[3];
89 dst = (CVPixelBufferRef)out->data[3];
90 ret = VTPixelRotationSessionRotateImage(s->session, src, dst);
91 if (ret != noErr) {
92 av_log(ctx, AV_LOG_ERROR, "transfer image failed, %d\n", ret);
93 ret = AVERROR_EXTERNAL;
94 goto fail;
95 }
96
97 av_frame_free(&in);
98
99 return ff_filter_frame(outlink, out);
100
101fail:
102 av_frame_free(&in);
104 return ret;
105}
106
108{
109 FilterLink *outl = ff_filter_link(outlink);
110 AVFilterContext *avctx = outlink->src;
111 AVFilterLink *inlink = outlink->src->inputs[0];
112 FilterLink *inl = ff_filter_link(inlink);
113 AVHWFramesContext *hw_frame_ctx_in;
114 AVHWFramesContext *hw_frame_ctx_out;
115 int err;
116
118
119 hw_frame_ctx_in = (AVHWFramesContext *)inl->hw_frames_ctx->data;
120 outl->hw_frames_ctx = av_hwframe_ctx_alloc(hw_frame_ctx_in->device_ref);
121 hw_frame_ctx_out = (AVHWFramesContext *)outl->hw_frames_ctx->data;
122 hw_frame_ctx_out->format = AV_PIX_FMT_VIDEOTOOLBOX;
123 hw_frame_ctx_out->sw_format = hw_frame_ctx_in->sw_format;
124 hw_frame_ctx_out->width = outlink->w;
125 hw_frame_ctx_out->height = outlink->h;
126 ((AVVTFramesContext *)hw_frame_ctx_out->hwctx)->color_range = ((AVVTFramesContext *)hw_frame_ctx_in->hwctx)->color_range;
127
128 err = ff_filter_init_hw_frames(avctx, outlink, 1);
129 if (err < 0)
130 return err;
131
133 if (err < 0) {
134 av_log(avctx, AV_LOG_ERROR,
135 "Failed to init videotoolbox frame context, %s\n",
136 av_err2str(err));
137 return err;
138 }
139
140 return 0;
141}
142
144{
145 int err;
146 FilterLink *outl = ff_filter_link(outlink);
147 AVFilterContext *avctx = outlink->src;
148 TransposeVtContext *s = avctx->priv;
149 AVFilterLink *inlink = outlink->src->inputs[0];
150 FilterLink *inl = ff_filter_link(inlink);
151 CFStringRef rotation = kVTRotation_0;
152 CFBooleanRef vflip = kCFBooleanFalse;
153 CFBooleanRef hflip = kCFBooleanFalse;
154 int swap_w_h = 0;
155
158
159 if ((inlink->w >= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
160 (inlink->w <= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
161 av_log(avctx, AV_LOG_VERBOSE,
162 "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
163 inlink->w, inlink->h, inlink->w, inlink->h);
164 return 0;
165 }
166
167 s->passthrough = TRANSPOSE_PT_TYPE_NONE;
168
169 switch (s->dir) {
171 rotation = kVTRotation_CCW90;
172 vflip = kCFBooleanTrue;
173 swap_w_h = 1;
174 break;
175 case TRANSPOSE_CCLOCK:
176 rotation = kVTRotation_CCW90;
177 swap_w_h = 1;
178 break;
179 case TRANSPOSE_CLOCK:
180 rotation = kVTRotation_CW90;
181 swap_w_h = 1;
182 break;
184 rotation = kVTRotation_CW90;
185 vflip = kCFBooleanTrue;
186 swap_w_h = 1;
187 break;
189 rotation = kVTRotation_180;
190 break;
191 case TRANSPOSE_HFLIP:
192 hflip = kCFBooleanTrue;
193 break;
194 case TRANSPOSE_VFLIP:
195 vflip = kCFBooleanTrue;
196 break;
197 default:
198 av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", s->dir);
199 return AVERROR(EINVAL);
200 }
201
202 err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_Rotation,
203 rotation);
204 if (err != noErr) {
205 av_log(avctx, AV_LOG_ERROR, "Set rotation property failed, %d\n", err);
206 return AVERROR_EXTERNAL;
207 }
208 err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipVerticalOrientation,
209 vflip);
210 if (err != noErr) {
211 av_log(avctx, AV_LOG_ERROR, "Set vertical flip property failed, %d\n", err);
212 return AVERROR_EXTERNAL;
213 }
214 err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipHorizontalOrientation,
215 hflip);
216 if (err != noErr) {
217 av_log(avctx, AV_LOG_ERROR, "Set horizontal flip property failed, %d\n", err);
218 return AVERROR_EXTERNAL;
219 }
220
221 if (!swap_w_h)
222 return 0;
223
224 outlink->w = inlink->h;
225 outlink->h = inlink->w;
226 return transpose_vt_recreate_hw_ctx(outlink);
227}
228
229#define OFFSET(x) offsetof(TransposeVtContext, x)
230#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
232 { "dir", "set transpose direction",
233 OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, .unit = "dir" },
234 { "cclock_flip", "rotate counter-clockwise with vertical flip",
235 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" },
236 { "clock", "rotate clockwise",
237 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" },
238 { "cclock", "rotate counter-clockwise",
239 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" },
240 { "clock_flip", "rotate clockwise with vertical flip",
241 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" },
242 { "reversal", "rotate by half-turn",
243 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "dir" },
244 { "hflip", "flip horizontally",
245 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "dir" },
246 { "vflip", "flip vertically",
247 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "dir" },
248
249 { "passthrough", "do not apply transposition if the input matches the specified geometry",
250 OFFSET(passthrough), AV_OPT_TYPE_INT, { .i64=TRANSPOSE_PT_TYPE_NONE }, 0, INT_MAX, FLAGS, .unit = "passthrough" },
251 { "none", "always apply transposition",
252 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" },
253 { "portrait", "preserve portrait geometry",
254 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_PORTRAIT }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" },
255 { "landscape", "preserve landscape geometry",
256 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_LANDSCAPE }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" },
257
258 { NULL }
259};
260
262
264 {
265 .name = "default",
266 .type = AVMEDIA_TYPE_VIDEO,
267 .filter_frame = &transpose_vt_filter_frame,
268 },
269};
270
272 {
273 .name = "default",
274 .type = AVMEDIA_TYPE_VIDEO,
275 .config_props = &transpose_vt_config_output,
276 },
277};
278
280 .p.name = "transpose_vt",
281 .p.description = NULL_IF_CONFIG_SMALL("Transpose Videotoolbox frames"),
282 .p.priv_class = &transpose_vt_class,
283 .p.flags = AVFILTER_FLAG_HWDEVICE,
284 .priv_size = sizeof(TransposeVtContext),
290 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
291};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
const FFFilter ff_vf_transpose_vt
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_filter_init_hw_frames(AVFilterContext *avctx, AVFilterLink *link, int default_pool_size)
Perform any additional setup required for hardware frames.
Definition avfilter.c:1668
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
#define AVFILTER_FLAG_HWDEVICE
The filter can create hardware frames using AVFilterContext.hw_device_ctx.
Definition avfilter.h:187
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:103
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition error.h:122
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition hwcontext.c:337
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition hwcontext.c:263
An API-specific header for AV_HWDEVICE_TYPE_VIDEOTOOLBOX.
static av_cold void uninit(AVBitStreamFilterContext *ctx)
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition filters.h:208
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:254
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
AVOptions.
@ AV_PIX_FMT_VIDEOTOOLBOX
hardware decoding through Videotoolbox
Definition pixfmt.h:305
uint8_t * data
The data buffer.
Definition buffer.h:90
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
AVFilterLink ** inputs
array of pointers to input links
Definition avfilter.h:281
void * priv
private data for use by the filter
Definition avfilter.h:288
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
Definition hwcontext.h:200
AVBufferRef * device_ref
A reference to the parent AVHWDeviceContext.
Definition hwcontext.h:129
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
Definition hwcontext.h:153
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition hwcontext.h:213
int width
The allocated dimensions of the frames in this pool.
Definition hwcontext.h:220
AVOption.
Definition opt.h:428
VTPixelRotationSessionRef session
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
static FILE * out
Definition movenc.c:55
static AVFormatContext * ctx
Definition movenc.c:49
@ TRANSPOSE_CLOCK_FLIP
Definition transpose.h:34
@ TRANSPOSE_HFLIP
Definition transpose.h:36
@ TRANSPOSE_CCLOCK
Definition transpose.h:33
@ TRANSPOSE_CCLOCK_FLIP
Definition transpose.h:31
@ TRANSPOSE_VFLIP
Definition transpose.h:37
@ TRANSPOSE_REVERSAL
Definition transpose.h:35
@ TRANSPOSE_CLOCK
Definition transpose.h:32
@ TRANSPOSE_PT_TYPE_NONE
Definition transpose.h:25
@ TRANSPOSE_PT_TYPE_PORTRAIT
Definition transpose.h:27
@ TRANSPOSE_PT_TYPE_LANDSCAPE
Definition transpose.h:26
static av_cold void transpose_vt_uninit(AVFilterContext *avctx)
static int transpose_vt_config_output(AVFilterLink *outlink)
static const AVFilterPad transpose_vt_inputs[]
static av_cold int transpose_vt_init(AVFilterContext *avctx)
static int transpose_vt_filter_frame(AVFilterLink *link, AVFrame *in)
static const AVOption transpose_vt_options[]
#define OFFSET(x)
static const AVFilterPad transpose_vt_outputs[]
static int transpose_vt_recreate_hw_ctx(AVFilterLink *outlink)
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:89