/src/mpv/video/sws_utils.c
Line | Count | Source |
1 | | /* |
2 | | * This file is part of mpv. |
3 | | * |
4 | | * mpv is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Lesser General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2.1 of the License, or (at your option) any later version. |
8 | | * |
9 | | * mpv is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | * GNU Lesser General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Lesser General Public |
15 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #include <assert.h> |
19 | | |
20 | | #include <libswscale/swscale.h> |
21 | | #include <libavcodec/avcodec.h> |
22 | | #include <libavutil/bswap.h> |
23 | | #include <libavutil/opt.h> |
24 | | #include <libavutil/pixdesc.h> |
25 | | #include <libplacebo/utils/libav.h> |
26 | | |
27 | | #include "config.h" |
28 | | |
29 | | #include "sws_utils.h" |
30 | | |
31 | | #include "common/common.h" |
32 | | #include "options/m_config.h" |
33 | | #include "options/m_option.h" |
34 | | #include "video/mp_image.h" |
35 | | #include "video/img_format.h" |
36 | | #include "fmt-conversion.h" |
37 | | #include "csputils.h" |
38 | | #include "common/msg.h" |
39 | | #include "osdep/endian.h" |
40 | | |
41 | | #if HAVE_ZIMG |
42 | | #include "zimg.h" |
43 | | #endif |
44 | | |
45 | | //global sws_flags from the command line |
46 | | struct sws_opts { |
47 | | int scaler; |
48 | | float lum_gblur; |
49 | | float chr_gblur; |
50 | | int chr_vshift; |
51 | | int chr_hshift; |
52 | | float chr_sharpen; |
53 | | float lum_sharpen; |
54 | | bool fast; |
55 | | bool bitexact; |
56 | | bool zimg; |
57 | | }; |
58 | | |
59 | | #define OPT_BASE_STRUCT struct sws_opts |
60 | | const struct m_sub_options sws_conf = { |
61 | | .opts = (const m_option_t[]) { |
62 | | {"scaler", OPT_CHOICE(scaler, |
63 | | {"fast-bilinear", SWS_FAST_BILINEAR}, |
64 | | {"bilinear", SWS_BILINEAR}, |
65 | | {"bicubic", SWS_BICUBIC}, |
66 | | {"x", SWS_X}, |
67 | | {"point", SWS_POINT}, |
68 | | {"area", SWS_AREA}, |
69 | | {"bicublin", SWS_BICUBLIN}, |
70 | | {"gauss", SWS_GAUSS}, |
71 | | {"sinc", SWS_SINC}, |
72 | | {"lanczos", SWS_LANCZOS}, |
73 | | {"spline", SWS_SPLINE})}, |
74 | | {"lgb", OPT_FLOAT(lum_gblur), M_RANGE(0, 100.0)}, |
75 | | {"cgb", OPT_FLOAT(chr_gblur), M_RANGE(0, 100.0)}, |
76 | | {"cvs", OPT_INT(chr_vshift), M_RANGE(-100, 100)}, |
77 | | {"chs", OPT_INT(chr_hshift), M_RANGE(-100, 100)}, |
78 | | {"ls", OPT_FLOAT(lum_sharpen), M_RANGE(-100.0, 100.0)}, |
79 | | {"cs", OPT_FLOAT(chr_sharpen), M_RANGE(-100.0, 100.0)}, |
80 | | {"fast", OPT_BOOL(fast)}, |
81 | | {"bitexact", OPT_BOOL(bitexact)}, |
82 | | {"allow-zimg", OPT_BOOL(zimg)}, |
83 | | {0} |
84 | | }, |
85 | | .size = sizeof(struct sws_opts), |
86 | | .defaults = &(const struct sws_opts){ |
87 | | .scaler = SWS_LANCZOS, |
88 | | .zimg = true, |
89 | | }, |
90 | | }; |
91 | | |
92 | | // Highest quality, but also slowest. |
93 | | static const int mp_sws_hq_flags = SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | |
94 | | SWS_ACCURATE_RND; |
95 | | |
96 | | // Fast, lossy. |
97 | | const int mp_sws_fast_flags = SWS_BILINEAR; |
98 | | |
99 | | // Set ctx parameters to global command line flags. |
100 | | static void mp_sws_update_from_cmdline(struct mp_sws_context *ctx) |
101 | 14.3k | { |
102 | 14.3k | m_config_cache_update(ctx->opts_cache); |
103 | 14.3k | struct sws_opts *opts = ctx->opts_cache->opts; |
104 | | |
105 | 14.3k | sws_freeFilter(ctx->src_filter); |
106 | 14.3k | ctx->src_filter = sws_getDefaultFilter(opts->lum_gblur, opts->chr_gblur, |
107 | 14.3k | opts->lum_sharpen, opts->chr_sharpen, |
108 | 14.3k | opts->chr_hshift, opts->chr_vshift, 0); |
109 | 14.3k | ctx->force_reload = true; |
110 | | |
111 | 14.3k | ctx->flags = SWS_PRINT_INFO; |
112 | 14.3k | ctx->flags |= opts->scaler; |
113 | 14.3k | if (!opts->fast) |
114 | 14.3k | ctx->flags |= mp_sws_hq_flags; |
115 | 14.3k | if (opts->bitexact) |
116 | 0 | ctx->flags |= SWS_BITEXACT; |
117 | | |
118 | 14.3k | ctx->allow_zimg = opts->zimg; |
119 | 14.3k | } |
120 | | |
121 | | bool mp_sws_supported_format(int imgfmt) |
122 | 15.0k | { |
123 | 15.0k | enum AVPixelFormat av_format = imgfmt2pixfmt(imgfmt); |
124 | | |
125 | 15.0k | return av_format != AV_PIX_FMT_NONE && sws_isSupportedInput(av_format) |
126 | 6.21k | && sws_isSupportedOutput(av_format); |
127 | 15.0k | } |
128 | | |
129 | | #if HAVE_ZIMG |
130 | | static bool allow_zimg(struct mp_sws_context *ctx) |
131 | | { |
132 | | return ctx->force_scaler == MP_SWS_ZIMG || |
133 | | (ctx->force_scaler == MP_SWS_AUTO && ctx->allow_zimg); |
134 | | } |
135 | | #endif |
136 | | |
137 | | static bool allow_sws(struct mp_sws_context *ctx) |
138 | 13.8k | { |
139 | 13.8k | return ctx->force_scaler == MP_SWS_SWS || ctx->force_scaler == MP_SWS_AUTO; |
140 | 13.8k | } |
141 | | |
142 | | bool mp_sws_supports_formats(struct mp_sws_context *ctx, |
143 | | int imgfmt_out, int imgfmt_in) |
144 | 6.99k | { |
145 | | #if HAVE_ZIMG |
146 | | if (allow_zimg(ctx)) { |
147 | | if (mp_zimg_supports_in_format(imgfmt_in) && |
148 | | mp_zimg_supports_out_format(imgfmt_out)) |
149 | | return true; |
150 | | } |
151 | | #endif |
152 | | |
153 | 6.99k | return allow_sws(ctx) && |
154 | 6.99k | sws_isSupportedInput(imgfmt2pixfmt(imgfmt_in)) && |
155 | 6.98k | sws_isSupportedOutput(imgfmt2pixfmt(imgfmt_out)); |
156 | 6.99k | } |
157 | | |
158 | | static int pl_csp_to_sws_colorspace(enum pl_color_system csp) |
159 | 13.7k | { |
160 | | // The SWS_CS_* macros are just convenience redefinitions of the |
161 | | // AVCOL_SPC_* macros, inside swscale.h. |
162 | 13.7k | return pl_system_to_av(csp); |
163 | 13.7k | } |
164 | | |
165 | | static bool cache_valid(struct mp_sws_context *ctx) |
166 | 19.2k | { |
167 | 19.2k | struct mp_sws_context *old = ctx->cached; |
168 | 19.2k | if (ctx->force_reload) |
169 | 6.89k | return false; |
170 | 12.3k | return mp_image_params_equal(&ctx->src, &old->src) && |
171 | 12.3k | mp_image_params_equal(&ctx->dst, &old->dst) && |
172 | 12.3k | ctx->flags == old->flags && |
173 | 12.3k | ctx->allow_zimg == old->allow_zimg && |
174 | 12.3k | ctx->force_scaler == old->force_scaler && |
175 | 12.3k | (!ctx->opts_cache || !m_config_cache_update(ctx->opts_cache)); |
176 | 19.2k | } |
177 | | |
178 | | static void free_mp_sws(void *p) |
179 | 7.41k | { |
180 | 7.41k | struct mp_sws_context *ctx = p; |
181 | 7.41k | sws_freeContext(ctx->sws); |
182 | 7.41k | sws_freeFilter(ctx->src_filter); |
183 | 7.41k | sws_freeFilter(ctx->dst_filter); |
184 | 7.41k | TA_FREEP(&ctx->aligned_src); |
185 | 7.41k | TA_FREEP(&ctx->aligned_dst); |
186 | 7.41k | } |
187 | | |
188 | | // You're supposed to set your scaling parameters on the returned context. |
189 | | // Free the context with talloc_free(). |
190 | | struct mp_sws_context *mp_sws_alloc(void *talloc_ctx) |
191 | 7.41k | { |
192 | 7.41k | struct mp_sws_context *ctx = talloc_ptrtype(talloc_ctx, ctx); |
193 | 7.41k | *ctx = (struct mp_sws_context) { |
194 | 7.41k | .log = mp_null_log, |
195 | 7.41k | .flags = SWS_BILINEAR, |
196 | 7.41k | .force_reload = true, |
197 | 7.41k | .params = {SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT}, |
198 | 7.41k | .cached = talloc_zero(ctx, struct mp_sws_context), |
199 | 7.41k | }; |
200 | 7.41k | talloc_set_destructor(ctx, free_mp_sws); |
201 | | |
202 | | #if HAVE_ZIMG |
203 | | ctx->zimg = mp_zimg_alloc(); |
204 | | talloc_steal(ctx, ctx->zimg); |
205 | | #endif |
206 | | |
207 | 7.41k | return ctx; |
208 | 7.41k | } |
209 | | |
210 | | // Enable auto-update of parameters from command line. Don't try to set custom |
211 | | // options (other than possibly .src/.dst), because they might be overwritten |
212 | | // if the user changes any options. |
213 | | void mp_sws_enable_cmdline_opts(struct mp_sws_context *ctx, struct mpv_global *g) |
214 | 7.41k | { |
215 | | // Should only ever be NULL for tests. |
216 | 7.41k | if (!g) |
217 | 0 | return; |
218 | 7.41k | if (ctx->opts_cache) |
219 | 0 | return; |
220 | | |
221 | 7.41k | ctx->opts_cache = m_config_cache_alloc(ctx, g, &sws_conf); |
222 | 7.41k | ctx->force_reload = true; |
223 | 7.41k | mp_sws_update_from_cmdline(ctx); |
224 | | |
225 | | #if HAVE_ZIMG |
226 | | mp_zimg_enable_cmdline_opts(ctx->zimg, g); |
227 | | #endif |
228 | 7.41k | } |
229 | | |
230 | | // Reinitialize (if needed) - return error code. |
231 | | // Optional, but possibly useful to avoid having to handle mp_sws_scale errors. |
232 | | int mp_sws_reinit(struct mp_sws_context *ctx) |
233 | 19.2k | { |
234 | 19.2k | struct mp_image_params src = ctx->src; |
235 | 19.2k | struct mp_image_params dst = ctx->dst; |
236 | | |
237 | 19.2k | if (cache_valid(ctx)) |
238 | 12.3k | return 0; |
239 | | |
240 | 6.89k | if (ctx->opts_cache) |
241 | 6.89k | mp_sws_update_from_cmdline(ctx); |
242 | | |
243 | 6.89k | sws_freeContext(ctx->sws); |
244 | 6.89k | ctx->sws = NULL; |
245 | 6.89k | ctx->zimg_ok = false; |
246 | 6.89k | TA_FREEP(&ctx->aligned_src); |
247 | 6.89k | TA_FREEP(&ctx->aligned_dst); |
248 | | |
249 | | #if HAVE_ZIMG |
250 | | if (allow_zimg(ctx)) { |
251 | | ctx->zimg->log = ctx->log; |
252 | | ctx->zimg->src = src; |
253 | | ctx->zimg->dst = dst; |
254 | | if (ctx->zimg_opts) |
255 | | ctx->zimg->opts = *ctx->zimg_opts; |
256 | | if (mp_zimg_config(ctx->zimg)) { |
257 | | ctx->zimg_ok = true; |
258 | | MP_VERBOSE(ctx, "Using zimg.\n"); |
259 | | goto success; |
260 | | } |
261 | | MP_WARN(ctx, "Not using zimg, falling back to swscale.\n"); |
262 | | } |
263 | | #endif |
264 | | |
265 | 6.89k | if (!allow_sws(ctx)) { |
266 | 0 | MP_ERR(ctx, "No scaler.\n"); |
267 | 0 | return -1; |
268 | 0 | } |
269 | | |
270 | 6.89k | ctx->sws = sws_alloc_context(); |
271 | 6.89k | if (!ctx->sws) |
272 | 0 | return -1; |
273 | | |
274 | 6.89k | mp_image_params_guess_csp(&src); // sanitize colorspace/colorlevels |
275 | 6.89k | mp_image_params_guess_csp(&dst); |
276 | | |
277 | 6.89k | enum AVPixelFormat s_fmt = imgfmt2pixfmt(src.imgfmt); |
278 | 6.89k | if (s_fmt == AV_PIX_FMT_NONE || sws_isSupportedInput(s_fmt) < 1) { |
279 | 0 | MP_ERR(ctx, "Input image format %s not supported by libswscale.\n", |
280 | 0 | mp_imgfmt_to_name(src.imgfmt)); |
281 | 0 | return -1; |
282 | 0 | } |
283 | | |
284 | 6.89k | enum AVPixelFormat d_fmt = imgfmt2pixfmt(dst.imgfmt); |
285 | 6.89k | if (d_fmt == AV_PIX_FMT_NONE || sws_isSupportedOutput(d_fmt) < 1) { |
286 | 0 | MP_ERR(ctx, "Output image format %s not supported by libswscale.\n", |
287 | 0 | mp_imgfmt_to_name(dst.imgfmt)); |
288 | 0 | return -1; |
289 | 0 | } |
290 | | |
291 | 6.89k | int s_csp = pl_csp_to_sws_colorspace(src.repr.sys); |
292 | 6.89k | int s_range = src.repr.levels == PL_COLOR_LEVELS_FULL; |
293 | | |
294 | 6.89k | int d_csp = pl_csp_to_sws_colorspace(src.repr.sys); |
295 | 6.89k | int d_range = dst.repr.levels == PL_COLOR_LEVELS_FULL; |
296 | | |
297 | 6.89k | av_opt_set_int(ctx->sws, "sws_flags", ctx->flags, 0); |
298 | | |
299 | 6.89k | av_opt_set_int(ctx->sws, "srcw", src.w, 0); |
300 | 6.89k | av_opt_set_int(ctx->sws, "srch", src.h, 0); |
301 | 6.89k | av_opt_set_int(ctx->sws, "src_format", s_fmt, 0); |
302 | | |
303 | 6.89k | av_opt_set_int(ctx->sws, "dstw", dst.w, 0); |
304 | 6.89k | av_opt_set_int(ctx->sws, "dsth", dst.h, 0); |
305 | 6.89k | av_opt_set_int(ctx->sws, "dst_format", d_fmt, 0); |
306 | | |
307 | 6.89k | av_opt_set_double(ctx->sws, "param0", ctx->params[0], 0); |
308 | 6.89k | av_opt_set_double(ctx->sws, "param1", ctx->params[1], 0); |
309 | | |
310 | 6.89k | int cr_src = pl_chroma_to_av(src.chroma_location); |
311 | 6.89k | int cr_dst = pl_chroma_to_av(dst.chroma_location); |
312 | 6.89k | int cr_xpos, cr_ypos; |
313 | 6.89k | if (av_chroma_location_enum_to_pos(&cr_xpos, &cr_ypos, cr_src) >= 0) { |
314 | 6.89k | av_opt_set_int(ctx->sws, "src_h_chr_pos", cr_xpos, 0); |
315 | 6.89k | av_opt_set_int(ctx->sws, "src_v_chr_pos", cr_ypos, 0); |
316 | 6.89k | } |
317 | 6.89k | if (av_chroma_location_enum_to_pos(&cr_xpos, &cr_ypos, cr_dst) >= 0) { |
318 | 6.89k | av_opt_set_int(ctx->sws, "dst_h_chr_pos", cr_xpos, 0); |
319 | 6.89k | av_opt_set_int(ctx->sws, "dst_v_chr_pos", cr_ypos, 0); |
320 | 6.89k | } |
321 | | |
322 | | // This can fail even with normal operation, e.g. if a conversion path |
323 | | // simply does not support these settings. |
324 | 6.89k | int r = |
325 | 6.89k | sws_setColorspaceDetails(ctx->sws, sws_getCoefficients(s_csp), s_range, |
326 | 6.89k | sws_getCoefficients(d_csp), d_range, |
327 | 6.89k | 0, 1 << 16, 1 << 16); |
328 | 6.89k | ctx->supports_csp = r >= 0; |
329 | | |
330 | 6.89k | if (sws_init_context(ctx->sws, ctx->src_filter, ctx->dst_filter) < 0) |
331 | 3 | return -1; |
332 | | |
333 | | #if HAVE_ZIMG |
334 | | success: |
335 | | #endif |
336 | | |
337 | 6.89k | ctx->force_reload = false; |
338 | 6.89k | *ctx->cached = *ctx; |
339 | 6.89k | return 1; |
340 | 6.89k | } |
341 | | |
342 | | static struct mp_image *check_alignment(struct mp_log *log, |
343 | | struct mp_image **alloc, |
344 | | struct mp_image *img) |
345 | 37.9k | { |
346 | | // It's completely unclear which alignment libswscale wants (for performance) |
347 | | // or requires (for avoiding crashes and memory corruption). |
348 | | // Is it av_cpu_max_align()? Is it the hardcoded AVFrame "default" of 32 |
349 | | // in get_video_buffer()? Is it whatever avcodec_align_dimensions2() |
350 | | // determines? It's like you can't win if you try to prevent libswscale from |
351 | | // corrupting memory... |
352 | | // So use 32, a value that has been experimentally determined to be safe, |
353 | | // and which in most cases is not larger than decoder output. It is smaller |
354 | | // or equal to what most image allocators in mpv/ffmpeg use. |
355 | 37.9k | size_t align = 32; |
356 | 37.9k | mp_assert(align <= MP_IMAGE_BYTE_ALIGN); // or mp_image_alloc will not cut it |
357 | | |
358 | 37.9k | bool is_aligned = true; |
359 | 123k | for (int p = 0; p < img->num_planes; p++) { |
360 | 85.1k | is_aligned &= MP_IS_ALIGNED((uintptr_t)img->planes[p], align); |
361 | 85.1k | is_aligned &= MP_IS_ALIGNED(labs(img->stride[p]), align); |
362 | 85.1k | } |
363 | | |
364 | 37.9k | if (is_aligned) |
365 | 33.8k | return img; |
366 | | |
367 | 4.09k | if (!*alloc) { |
368 | 1.41k | mp_verbose(log, "unaligned libswscale parameter; using slow copy.\n"); |
369 | 1.41k | *alloc = mp_image_alloc(img->imgfmt, img->w, img->h); |
370 | 1.41k | if (!*alloc) |
371 | 0 | return NULL; |
372 | 1.41k | } |
373 | | |
374 | 4.09k | mp_image_copy_attributes(*alloc, img); |
375 | 4.09k | return *alloc; |
376 | 4.09k | } |
377 | | |
378 | | // Scale from src to dst - if src/dst have different parameters from previous |
379 | | // calls, the context is reinitialized. Return error code. (It can fail if |
380 | | // reinitialization was necessary, and swscale returned an error.) |
381 | | int mp_sws_scale(struct mp_sws_context *ctx, struct mp_image *dst, |
382 | | struct mp_image *src) |
383 | 18.9k | { |
384 | 18.9k | ctx->src = src->params; |
385 | 18.9k | ctx->dst = dst->params; |
386 | | |
387 | 18.9k | int r = mp_sws_reinit(ctx); |
388 | 18.9k | if (r < 0) { |
389 | 3 | MP_ERR(ctx, "libswscale initialization failed.\n"); |
390 | 3 | return r; |
391 | 3 | } |
392 | | |
393 | | #if HAVE_ZIMG |
394 | | if (ctx->zimg_ok) |
395 | | return mp_zimg_convert(ctx->zimg, dst, src) ? 0 : -1; |
396 | | #endif |
397 | | |
398 | 18.9k | if (src->params.repr.sys == PL_COLOR_SYSTEM_XYZ && dst->params.repr.sys != PL_COLOR_SYSTEM_XYZ) { |
399 | | // swsscale has hardcoded gamma 2.2 internally and 2.6 for XYZ |
400 | 445 | dst->params.color.transfer = PL_COLOR_TRC_GAMMA22; |
401 | | // and sRGB primaries... |
402 | 445 | dst->params.color.primaries = PL_COLOR_PRIM_BT_709; |
403 | | // it doesn't adjust white point though, but it is not worth to support |
404 | | // this case. It would require custom prim with equal energy white point |
405 | | // and sRGB primaries. |
406 | 445 | } |
407 | | |
408 | 18.9k | struct mp_image *a_src = check_alignment(ctx->log, &ctx->aligned_src, src); |
409 | 18.9k | struct mp_image *a_dst = check_alignment(ctx->log, &ctx->aligned_dst, dst); |
410 | 18.9k | if (!a_src || !a_dst) { |
411 | 0 | MP_ERR(ctx, "image allocation failed.\n"); |
412 | 0 | return -1; |
413 | 0 | } |
414 | | |
415 | 18.9k | if (a_src != src) |
416 | 4.09k | mp_image_copy(a_src, src); |
417 | | |
418 | 18.9k | sws_scale(ctx->sws, (const uint8_t *const *) a_src->planes, a_src->stride, |
419 | 18.9k | 0, a_src->h, a_dst->planes, a_dst->stride); |
420 | | |
421 | 18.9k | if (a_dst != dst) |
422 | 0 | mp_image_copy(dst, a_dst); |
423 | | |
424 | 18.9k | return 0; |
425 | 18.9k | } |
426 | | |
427 | | int mp_image_sw_blur_scale(struct mp_image *dst, struct mp_image *src, |
428 | | float gblur) |
429 | 0 | { |
430 | 0 | struct mp_sws_context *ctx = mp_sws_alloc(NULL); |
431 | 0 | ctx->flags = SWS_LANCZOS | mp_sws_hq_flags; |
432 | 0 | ctx->src_filter = sws_getDefaultFilter(gblur, gblur, 0, 0, 0, 0, 0); |
433 | 0 | ctx->force_reload = true; |
434 | 0 | int res = mp_sws_scale(ctx, dst, src); |
435 | 0 | talloc_free(ctx); |
436 | 0 | return res; |
437 | 0 | } |
438 | | |
439 | | static const int endian_swaps[][2] = { |
440 | | #if BYTE_ORDER == LITTLE_ENDIAN |
441 | | #if defined(AV_PIX_FMT_YA16) && defined(AV_PIX_FMT_RGBA64) |
442 | | {AV_PIX_FMT_YA16BE, AV_PIX_FMT_YA16LE}, |
443 | | {AV_PIX_FMT_RGBA64BE, AV_PIX_FMT_RGBA64LE}, |
444 | | {AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16LE}, |
445 | | {AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGB48LE}, |
446 | | #endif |
447 | | #endif |
448 | | {AV_PIX_FMT_NONE, AV_PIX_FMT_NONE} |
449 | | }; |
450 | | |
451 | | // Swap _some_ non-native endian formats to native. We do this specifically |
452 | | // for pixel formats used by PNG, to avoid going through libswscale, which |
453 | | // might reduce the effective bit depth in some cases. |
454 | | struct mp_image *mp_img_swap_to_native(struct mp_image *img) |
455 | 381k | { |
456 | 381k | int avfmt = imgfmt2pixfmt(img->imgfmt); |
457 | 381k | int to = AV_PIX_FMT_NONE; |
458 | 1.90M | for (int n = 0; endian_swaps[n][0] != AV_PIX_FMT_NONE; n++) { |
459 | 1.52M | if (endian_swaps[n][0] == avfmt) |
460 | 4 | to = endian_swaps[n][1]; |
461 | 1.52M | } |
462 | 381k | if (to == AV_PIX_FMT_NONE || !mp_image_make_writeable(img)) |
463 | 381k | return img; |
464 | 4 | int elems = img->fmt.bpp[0] / 8 / 2 * img->w; |
465 | 100 | for (int y = 0; y < img->h; y++) { |
466 | 96 | uint16_t *p = (uint16_t *)(img->planes[0] + y * img->stride[0]); |
467 | 3.82k | for (int i = 0; i < elems; i++) |
468 | 3.72k | p[i] = av_be2ne16(p[i]); |
469 | 96 | } |
470 | 4 | mp_image_setfmt(img, pixfmt2imgfmt(to)); |
471 | 4 | return img; |
472 | 381k | } |
473 | | |
474 | | // vim: ts=4 sw=4 et tw=80 |