avcodec/jpegxl_parse{,r}: fix integer overflow for some malformed files
[FFMpeg-mirror.git] / libavfilter / vf_transpose_vt.c
blobf204030825fa0fdf9c13e2f7756dafd286ebef32
1 /*
2 * Copyright (c) 2023 Zhao Zhili <zhilizhao@tencent.com>
4 * This file is part of FFmpeg.
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.
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.
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
21 #include <VideoToolbox/VideoToolbox.h>
23 #include "libavutil/hwcontext.h"
24 #include "libavutil/hwcontext_videotoolbox.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
28 #include "filters.h"
29 #include "transpose.h"
30 #include "video.h"
32 typedef struct TransposeVtContext {
33 AVClass *class;
35 VTPixelRotationSessionRef session;
36 int dir;
37 int passthrough;
38 } TransposeVtContext;
40 static av_cold int transpose_vt_init(AVFilterContext *avctx)
42 TransposeVtContext *s = avctx->priv;
43 int ret;
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;
51 return 0;
54 static av_cold void transpose_vt_uninit(AVFilterContext *avctx)
56 TransposeVtContext *s = avctx->priv;
58 if (s->session) {
59 VTPixelRotationSessionInvalidate(s->session);
60 CFRelease(s->session);
61 s->session = NULL;
65 static int transpose_vt_filter_frame(AVFilterLink *link, AVFrame *in)
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;
75 if (s->passthrough)
76 return ff_filter_frame(outlink, in);
78 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
79 if (!out) {
80 ret = AVERROR(ENOMEM);
81 goto fail;
84 ret = av_frame_copy_props(out, in);
85 if (ret < 0)
86 goto fail;
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;
97 av_frame_free(&in);
99 return ff_filter_frame(outlink, out);
101 fail:
102 av_frame_free(&in);
103 av_frame_free(&out);
104 return ret;
107 static int transpose_vt_recreate_hw_ctx(AVFilterLink *outlink)
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;
117 av_buffer_unref(&outl->hw_frames_ctx);
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;
128 err = ff_filter_init_hw_frames(avctx, outlink, 1);
129 if (err < 0)
130 return err;
132 err = av_hwframe_ctx_init(outl->hw_frames_ctx);
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;
140 return 0;
143 static int transpose_vt_config_output(AVFilterLink *outlink)
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;
156 av_buffer_unref(&outl->hw_frames_ctx);
157 outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
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;
167 s->passthrough = TRANSPOSE_PT_TYPE_NONE;
169 switch (s->dir) {
170 case TRANSPOSE_CCLOCK_FLIP:
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;
183 case TRANSPOSE_CLOCK_FLIP:
184 rotation = kVTRotation_CW90;
185 vflip = kCFBooleanTrue;
186 swap_w_h = 1;
187 break;
188 case TRANSPOSE_REVERSAL:
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);
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;
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;
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;
221 if (!swap_w_h)
222 return 0;
224 outlink->w = inlink->h;
225 outlink->h = inlink->w;
226 return transpose_vt_recreate_hw_ctx(outlink);
229 #define OFFSET(x) offsetof(TransposeVtContext, x)
230 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
231 static const AVOption transpose_vt_options[] = {
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" },
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" },
258 { NULL }
261 AVFILTER_DEFINE_CLASS(transpose_vt);
263 static const AVFilterPad transpose_vt_inputs[] = {
265 .name = "default",
266 .type = AVMEDIA_TYPE_VIDEO,
267 .filter_frame = &transpose_vt_filter_frame,
271 static const AVFilterPad transpose_vt_outputs[] = {
273 .name = "default",
274 .type = AVMEDIA_TYPE_VIDEO,
275 .config_props = &transpose_vt_config_output,
279 const FFFilter ff_vf_transpose_vt = {
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),
285 .init = transpose_vt_init,
286 .uninit = transpose_vt_uninit,
287 FILTER_INPUTS(transpose_vt_inputs),
288 FILTER_OUTPUTS(transpose_vt_outputs),
289 FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VIDEOTOOLBOX),
290 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,