2 * This file is part of FFmpeg.
4 * FFmpeg 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.
9 * FFmpeg 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 GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * Synthesizes vapour (?)
28 #include <vapoursynth/VSScript4.h>
30 #include "libavutil/avassert.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/eval.h"
33 #include "libavutil/frame.h"
34 #include "libavutil/imgutils.h"
35 #include "libavutil/mem.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/pixdesc.h"
42 /* Platform-specific directives. */
45 #include "compat/w32dlfcn.h"
46 #include "libavutil/wchar_filename.h"
48 #define VSSCRIPT_LIB "VSScript.dll"
51 #define VSSCRIPT_NAME "libvapoursynth-script"
52 #define VSSCRIPT_LIB VSSCRIPT_NAME SLIBSUF
56 const VSSCRIPTAPI
*vssapi
;
60 typedef const VSSCRIPTAPI
*(*VSScriptGetAPIFunc
)(int version
);
62 typedef struct VSContext
{
65 AVBufferRef
*vss_state
;
67 const VSSCRIPTAPI
*vssapi
;
78 int64_t max_script_size
;
81 #define OFFSET(x) offsetof(VSContext, x)
82 #define A AV_OPT_FLAG_AUDIO_PARAM
83 #define D AV_OPT_FLAG_DECODING_PARAM
84 static const AVOption options
[] = {
85 {"max_script_size", "set max file size supported (in bytes)", OFFSET(max_script_size
), AV_OPT_TYPE_INT64
, {.i64
= 1 * 1024 * 1024}, 0, SIZE_MAX
- 1, A
|D
},
89 static av_cold
void* vs_load_library(VSScriptGetAPIFunc
*get_vssapi
)
91 void *vslibrary
= NULL
;
93 const HKEY hkeys
[] = {HKEY_CURRENT_USER
, HKEY_LOCAL_MACHINE
};
96 DWORD buf_size
= sizeof(vss_path
) - 2;
100 for (i
= 0; i
< FF_ARRAY_ELEMS(hkeys
); i
++) {
101 if ((r
= RegGetValueW(hkeys
[i
], L
"SOFTWARE\\VapourSynth",
102 L
"VSScriptDLL", RRF_RT_REG_SZ
, NULL
,
103 &vss_path
, &buf_size
)) == ERROR_SUCCESS
)
106 if (r
== ERROR_SUCCESS
&& wchartoutf8(vss_path
, &vss_path_utf8
) == 0) {
107 vslibrary
= dlopen(vss_path_utf8
, RTLD_NOW
| RTLD_GLOBAL
);
108 av_free(vss_path_utf8
);
112 vslibrary
= dlopen(VSSCRIPT_LIB
, RTLD_NOW
| RTLD_GLOBAL
);
114 if (vslibrary
!= NULL
) {
115 if (!(*get_vssapi
= (VSScriptGetAPIFunc
)dlsym(vslibrary
, "getVSScriptAPI"))) {
123 static void free_vss_state(void *opaque
, uint8_t *data
)
125 struct VSState
*vss
= opaque
;
128 vss
->vssapi
->freeScript(vss
->vss
);
132 static av_cold
int read_close_vs(AVFormatContext
*s
)
134 VSContext
*vs
= s
->priv_data
;
137 vs
->vsapi
->freeNode(vs
->outnode
);
139 av_buffer_unref(&vs
->vss_state
);
145 dlclose(vs
->vslibrary
);
150 static av_cold
int is_native_endian(enum AVPixelFormat pixfmt
)
152 enum AVPixelFormat other
= av_pix_fmt_swap_endianness(pixfmt
);
153 const AVPixFmtDescriptor
*pd
;
154 if (other
== AV_PIX_FMT_NONE
|| other
== pixfmt
)
155 return 1; // not affected by byte order
156 pd
= av_pix_fmt_desc_get(pixfmt
);
157 return pd
&& (!!HAVE_BIGENDIAN
== !!(pd
->flags
& AV_PIX_FMT_FLAG_BE
));
160 static av_cold
enum AVPixelFormat
match_pixfmt(const VSVideoFormat
*vsf
, int c_order
[4])
162 static const int yuv_order
[4] = {0, 1, 2, 0};
163 static const int rgb_order
[4] = {1, 2, 0, 0};
164 const AVPixFmtDescriptor
*pd
;
166 for (pd
= av_pix_fmt_desc_next(NULL
); pd
; pd
= av_pix_fmt_desc_next(pd
)) {
167 int is_rgb
, is_yuv
, i
;
169 enum AVPixelFormat pixfmt
;
171 pixfmt
= av_pix_fmt_desc_get_id(pd
);
173 if (pd
->flags
& (AV_PIX_FMT_FLAG_BAYER
| AV_PIX_FMT_FLAG_ALPHA
|
174 AV_PIX_FMT_FLAG_HWACCEL
| AV_PIX_FMT_FLAG_BITSTREAM
|
175 AV_PIX_FMT_FLAG_XYZ
))
178 if (pd
->log2_chroma_w
!= vsf
->subSamplingW
||
179 pd
->log2_chroma_h
!= vsf
->subSamplingH
)
182 is_rgb
= vsf
->colorFamily
== cfRGB
;
183 if (is_rgb
!= !!(pd
->flags
& AV_PIX_FMT_FLAG_RGB
))
186 is_yuv
= vsf
->colorFamily
== cfYUV
||
187 vsf
->colorFamily
== cfGray
;
188 if (!is_rgb
&& !is_yuv
)
191 if (vsf
->sampleType
!= ((pd
->flags
& AV_PIX_FMT_FLAG_FLOAT
) ? stFloat
: stInteger
))
194 if (av_pix_fmt_count_planes(pixfmt
) != vsf
->numPlanes
)
197 if (!is_native_endian(pixfmt
))
200 order
= is_yuv
? yuv_order
: rgb_order
;
202 for (i
= 0; i
< pd
->nb_components
; i
++) {
203 const AVComponentDescriptor
*c
= &pd
->comp
[i
];
204 if (order
[c
->plane
] != i
||
205 c
->offset
!= 0 || c
->shift
!= 0 ||
206 c
->step
!= vsf
->bytesPerSample
||
207 c
->depth
!= vsf
->bitsPerSample
)
212 memcpy(c_order
, order
, sizeof(int[4]));
218 return AV_PIX_FMT_NONE
;
221 static av_cold
int read_header_vs(AVFormatContext
*s
)
224 AVIOContext
*pb
= s
->pb
;
225 VSContext
*vs
= s
->priv_data
;
226 VSScriptGetAPIFunc get_vssapi
;
227 int64_t sz
= avio_size(pb
);
231 const VSVideoInfo
*info
;
232 struct VSState
*vss_state
;
235 if (!(vs
->vslibrary
= vs_load_library(&get_vssapi
))) {
236 av_log(s
, AV_LOG_ERROR
, "Could not open " VSSCRIPT_LIB
". "
237 "Check VapourSynth installation.\n");
238 err
= AVERROR_EXTERNAL
;
242 if (!(vs
->vssapi
= get_vssapi(VSSCRIPT_API_VERSION
))) {
243 av_log(s
, AV_LOG_ERROR
, "Failed to initialize VSScript (possibly PYTHONPATH not set).\n");
244 err
= AVERROR_EXTERNAL
;
248 if (!(vs
->vsapi
= vs
->vssapi
->getVSAPI(VAPOURSYNTH_API_VERSION
))) {
249 av_log(s
, AV_LOG_ERROR
, "Could not get VSAPI. "
250 "Check VapourSynth installation.\n");
251 err
= AVERROR_EXTERNAL
;
255 vss_state
= av_mallocz(sizeof(*vss_state
));
257 err
= AVERROR(ENOMEM
);
260 vss_state
->vssapi
= vs
->vssapi
;
262 vs
->vss_state
= av_buffer_create(NULL
, 0, free_vss_state
, vss_state
, 0);
263 if (!vs
->vss_state
) {
264 err
= AVERROR(ENOMEM
);
269 if (!(vss_state
->vss
= vs
->vssapi
->createScript(NULL
))) {
270 av_log(s
, AV_LOG_ERROR
, "Failed to create script instance.\n");
271 err
= AVERROR_EXTERNAL
;
275 if (sz
< 0 || sz
> vs
->max_script_size
) {
277 av_log(s
, AV_LOG_WARNING
, "Could not determine file size\n");
278 sz
= vs
->max_script_size
;
281 buf
= av_malloc(sz
+ 1);
283 err
= AVERROR(ENOMEM
);
286 sz
= avio_read(pb
, buf
, sz
);
289 av_log(s
, AV_LOG_ERROR
, "Could not read script.\n");
294 // Data left means our buffer (the max_script_size option) is too small
295 if (avio_read(pb
, &dummy
, 1) == 1) {
296 av_log(s
, AV_LOG_ERROR
, "File size is larger than max_script_size option "
297 "value %"PRIi64
", consider increasing the max_script_size option\n",
298 vs
->max_script_size
);
299 err
= AVERROR_BUFFER_TOO_SMALL
;
304 if (vs
->vssapi
->evaluateBuffer(vss_state
->vss
, buf
, s
->url
)) {
305 const char *msg
= vs
->vssapi
->getError(vss_state
->vss
);
306 av_log(s
, AV_LOG_ERROR
, "Failed to parse script: %s\n", msg
? msg
: "(unknown)");
307 err
= AVERROR_EXTERNAL
;
311 vs
->outnode
= vs
->vssapi
->getOutputNode(vss_state
->vss
, 0);
313 av_log(s
, AV_LOG_ERROR
, "Could not get script output node.\n");
314 err
= AVERROR_EXTERNAL
;
318 st
= avformat_new_stream(s
, NULL
);
320 err
= AVERROR(ENOMEM
);
324 info
= vs
->vsapi
->getVideoInfo(vs
->outnode
);
326 if (!info
->format
.colorFamily
|| !info
->width
|| !info
->height
) {
327 av_log(s
, AV_LOG_ERROR
, "Non-constant input format not supported.\n");
328 err
= AVERROR_PATCHWELCOME
;
334 avpriv_set_pts_info(st
, 64, info
->fpsDen
, info
->fpsNum
);
335 st
->duration
= info
->numFrames
;
337 // VFR. Just set "something".
338 avpriv_set_pts_info(st
, 64, 1, AV_TIME_BASE
);
339 s
->ctx_flags
|= AVFMTCTX_UNSEEKABLE
;
342 st
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
343 st
->codecpar
->codec_id
= AV_CODEC_ID_WRAPPED_AVFRAME
;
344 st
->codecpar
->width
= info
->width
;
345 st
->codecpar
->height
= info
->height
;
346 st
->codecpar
->format
= match_pixfmt(&info
->format
, vs
->c_order
);
348 if (st
->codecpar
->format
== AV_PIX_FMT_NONE
) {
349 av_log(s
, AV_LOG_ERROR
, "Unsupported VS pixel format %s\n",
350 vs
->vsapi
->getVideoFormatName(&info
->format
, vsfmt
) ? vsfmt
: "(unknown)");
351 err
= AVERROR_EXTERNAL
;
354 av_log(s
, AV_LOG_VERBOSE
, "VS format %s -> pixfmt %s\n",
355 vs
->vsapi
->getVideoFormatName(&info
->format
, vsfmt
) ? vsfmt
: "(unknown)",
356 av_get_pix_fmt_name(st
->codecpar
->format
));
363 static void free_frame(void *opaque
, uint8_t *data
)
365 AVFrame
*frame
= (AVFrame
*)data
;
367 av_frame_free(&frame
);
370 static int get_vs_prop_int(AVFormatContext
*s
, const VSMap
*map
, const char *name
, int def
)
372 VSContext
*vs
= s
->priv_data
;
376 res
= vs
->vsapi
->mapGetInt(map
, name
, 0, &err
);
377 return err
|| res
< INT_MIN
|| res
> INT_MAX
? def
: res
;
380 struct vsframe_ref_data
{
382 const VSFrame
*frame
;
383 AVBufferRef
*vss_state
;
386 static void free_vsframe_ref(void *opaque
, uint8_t *data
)
388 struct vsframe_ref_data
*d
= opaque
;
391 d
->vsapi
->freeFrame(d
->frame
);
393 av_buffer_unref(&d
->vss_state
);
398 static int read_packet_vs(AVFormatContext
*s
, AVPacket
*pkt
)
400 VSContext
*vs
= s
->priv_data
;
401 AVStream
*st
= s
->streams
[0];
402 AVFrame
*frame
= NULL
;
404 const VSFrame
*vsframe
;
405 const VSVideoInfo
*info
= vs
->vsapi
->getVideoInfo(vs
->outnode
);
407 const AVPixFmtDescriptor
*desc
;
408 AVBufferRef
*vsframe_ref
= NULL
;
409 struct vsframe_ref_data
*ref_data
;
413 if (vs
->current_frame
>= info
->numFrames
)
416 ref_data
= av_mallocz(sizeof(*ref_data
));
418 err
= AVERROR(ENOMEM
);
422 // (the READONLY flag is important because the ref is reused for plane data)
423 vsframe_ref
= av_buffer_create(NULL
, 0, free_vsframe_ref
, ref_data
, AV_BUFFER_FLAG_READONLY
);
425 err
= AVERROR(ENOMEM
);
430 vsframe
= vs
->vsapi
->getFrame(vs
->current_frame
, vs
->outnode
, vserr
, sizeof(vserr
));
432 av_log(s
, AV_LOG_ERROR
, "Error getting frame: %s\n", vserr
);
433 err
= AVERROR_EXTERNAL
;
437 ref_data
->vsapi
= vs
->vsapi
;
438 ref_data
->frame
= vsframe
;
440 ref_data
->vss_state
= av_buffer_ref(vs
->vss_state
);
441 if (!ref_data
->vss_state
) {
442 err
= AVERROR(ENOMEM
);
446 props
= vs
->vsapi
->getFramePropertiesRO(vsframe
);
448 frame
= av_frame_alloc();
450 err
= AVERROR(ENOMEM
);
454 frame
->format
= st
->codecpar
->format
;
455 frame
->width
= st
->codecpar
->width
;
456 frame
->height
= st
->codecpar
->height
;
457 frame
->colorspace
= st
->codecpar
->color_space
;
459 // Values according to ISO/IEC 14496-10.
460 frame
->colorspace
= get_vs_prop_int(s
, props
, "_Matrix", frame
->colorspace
);
461 frame
->color_primaries
= get_vs_prop_int(s
, props
, "_Primaries", frame
->color_primaries
);
462 frame
->color_trc
= get_vs_prop_int(s
, props
, "_Transfer", frame
->color_trc
);
464 if (get_vs_prop_int(s
, props
, "_ColorRange", 1) == 0)
465 frame
->color_range
= AVCOL_RANGE_JPEG
;
467 frame
->sample_aspect_ratio
.num
= get_vs_prop_int(s
, props
, "_SARNum", 0);
468 frame
->sample_aspect_ratio
.den
= get_vs_prop_int(s
, props
, "_SARDen", 1);
470 av_assert0(vs
->vsapi
->getFrameWidth(vsframe
, 0) == frame
->width
);
471 av_assert0(vs
->vsapi
->getFrameHeight(vsframe
, 0) == frame
->height
);
473 desc
= av_pix_fmt_desc_get(frame
->format
);
475 for (i
= 0; i
< info
->format
.numPlanes
; i
++) {
476 int p
= vs
->c_order
[i
];
477 ptrdiff_t plane_h
= frame
->height
;
479 frame
->data
[i
] = (void *)vs
->vsapi
->getReadPtr(vsframe
, p
);
480 frame
->linesize
[i
] = vs
->vsapi
->getStride(vsframe
, p
);
482 frame
->buf
[i
] = av_buffer_ref(vsframe_ref
);
483 if (!frame
->buf
[i
]) {
484 err
= AVERROR(ENOMEM
);
488 // Each plane needs an AVBufferRef that indicates the correct plane
489 // memory range. VapourSynth doesn't even give us the memory range,
490 // so make up a bad guess to make FFmpeg happy (even if almost nothing
491 // checks the memory range).
492 if (i
== 1 || i
== 2)
493 plane_h
= AV_CEIL_RSHIFT(plane_h
, desc
->log2_chroma_h
);
494 frame
->buf
[i
]->data
= frame
->data
[i
];
495 frame
->buf
[i
]->size
= frame
->linesize
[i
] * plane_h
;
498 pkt
->buf
= av_buffer_create((uint8_t*)frame
, sizeof(*frame
),
499 free_frame
, NULL
, 0);
501 err
= AVERROR(ENOMEM
);
505 frame
= NULL
; // pkt owns it now
507 pkt
->data
= pkt
->buf
->data
;
508 pkt
->size
= pkt
->buf
->size
;
509 pkt
->flags
|= AV_PKT_FLAG_TRUSTED
;
512 pkt
->pts
= vs
->current_frame
;
517 av_frame_free(&frame
);
518 av_buffer_unref(&vsframe_ref
);
522 static int read_seek_vs(AVFormatContext
*s
, int stream_idx
, int64_t ts
, int flags
)
524 VSContext
*vs
= s
->priv_data
;
527 return AVERROR(ENOSYS
);
529 vs
->current_frame
= FFMIN(FFMAX(0, ts
), s
->streams
[0]->duration
);
533 static av_cold
int probe_vs(const AVProbeData
*p
)
535 // Explicitly do not support this. VS scripts are written in Python, and
536 // can run arbitrary code on the user's system.
540 static const AVClass class_vs
= {
541 .class_name
= "VapourSynth demuxer",
542 .item_name
= av_default_item_name
,
544 .version
= LIBAVUTIL_VERSION_INT
,
547 const FFInputFormat ff_vapoursynth_demuxer
= {
548 .p
.name
= "vapoursynth",
549 .p
.long_name
= NULL_IF_CONFIG_SMALL("VapourSynth demuxer"),
550 .p
.priv_class
= &class_vs
,
551 .priv_data_size
= sizeof(VSContext
),
552 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
553 .read_probe
= probe_vs
,
554 .read_header
= read_header_vs
,
555 .read_packet
= read_packet_vs
,
556 .read_close
= read_close_vs
,
557 .read_seek
= read_seek_vs
,