1/*
2 * This file is part of FFmpeg.
3 *
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.
8 *
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.
13 *
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
17 */
18
19/**
20* @file
21* VapourSynth demuxer
22*
23* Synthesizes vapour (?)
24*/
25
26#include <limits.h>
27
28#include <VapourSynth.h>
29#include <VSScript.h>
30
31#include "libavutil/avassert.h"
32#include "libavutil/avstring.h"
33#include "libavutil/eval.h"
34#include "libavutil/imgutils.h"
35#include "libavutil/opt.h"
36#include "libavutil/pixdesc.h"
37#include "avformat.h"
38#include "internal.h"
39
40struct VSState {
41    VSScript *vss;
42};
43
44typedef struct VSContext {
45    const AVClass *class;
46
47    AVBufferRef *vss_state;
48
49    const VSAPI *vsapi;
50    VSCore *vscore;
51
52    VSNodeRef *outnode;
53    int is_cfr;
54    int current_frame;
55
56    int c_order[4];
57
58    /* options */
59    int64_t max_script_size;
60} VSContext;
61
62#define OFFSET(x) offsetof(VSContext, x)
63#define A AV_OPT_FLAG_AUDIO_PARAM
64#define D AV_OPT_FLAG_DECODING_PARAM
65static const AVOption options[] = {
66    {"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},
67    {NULL}
68};
69
70static void free_vss_state(void *opaque, uint8_t *data)
71{
72    struct VSState *vss = opaque;
73
74    if (vss->vss) {
75        vsscript_freeScript(vss->vss);
76        vsscript_finalize();
77    }
78}
79
80static av_cold int read_close_vs(AVFormatContext *s)
81{
82    VSContext *vs = s->priv_data;
83
84    if (vs->outnode)
85        vs->vsapi->freeNode(vs->outnode);
86
87    av_buffer_unref(&vs->vss_state);
88
89    vs->vsapi = NULL;
90    vs->vscore = NULL;
91    vs->outnode = NULL;
92
93    return 0;
94}
95
96static av_cold int is_native_endian(enum AVPixelFormat pixfmt)
97{
98    enum AVPixelFormat other = av_pix_fmt_swap_endianness(pixfmt);
99    const AVPixFmtDescriptor *pd;
100    if (other == AV_PIX_FMT_NONE || other == pixfmt)
101        return 1; // not affected by byte order
102    pd = av_pix_fmt_desc_get(pixfmt);
103    return pd && (!!HAVE_BIGENDIAN == !!(pd->flags & AV_PIX_FMT_FLAG_BE));
104}
105
106static av_cold enum AVPixelFormat match_pixfmt(const VSFormat *vsf, int c_order[4])
107{
108    static const int yuv_order[4] = {0, 1, 2, 0};
109    static const int rgb_order[4] = {1, 2, 0, 0};
110    const AVPixFmtDescriptor *pd;
111
112    for (pd = av_pix_fmt_desc_next(NULL); pd; pd = av_pix_fmt_desc_next(pd)) {
113        int is_rgb, is_yuv, i;
114        const int *order;
115        enum AVPixelFormat pixfmt;
116
117        pixfmt = av_pix_fmt_desc_get_id(pd);
118
119        if (pd->flags & (AV_PIX_FMT_FLAG_BAYER | AV_PIX_FMT_FLAG_ALPHA |
120                         AV_PIX_FMT_FLAG_HWACCEL | AV_PIX_FMT_FLAG_BITSTREAM))
121            continue;
122
123        if (pd->log2_chroma_w != vsf->subSamplingW ||
124            pd->log2_chroma_h != vsf->subSamplingH)
125            continue;
126
127        is_rgb = vsf->colorFamily == cmRGB;
128        if (is_rgb != !!(pd->flags & AV_PIX_FMT_FLAG_RGB))
129            continue;
130
131        is_yuv = vsf->colorFamily == cmYUV ||
132                 vsf->colorFamily == cmYCoCg ||
133                 vsf->colorFamily == cmGray;
134        if (!is_rgb && !is_yuv)
135            continue;
136
137        if (vsf->sampleType != ((pd->flags & AV_PIX_FMT_FLAG_FLOAT) ? stFloat : stInteger))
138            continue;
139
140        if (av_pix_fmt_count_planes(pixfmt) != vsf->numPlanes)
141            continue;
142
143        if (strncmp(pd->name, "xyz", 3) == 0)
144            continue;
145
146        if (!is_native_endian(pixfmt))
147            continue;
148
149        order = is_yuv ? yuv_order : rgb_order;
150
151        for (i = 0; i < pd->nb_components; i++) {
152            const AVComponentDescriptor *c = &pd->comp[i];
153            if (order[c->plane] != i ||
154                c->offset != 0 || c->shift != 0 ||
155                c->step != vsf->bytesPerSample ||
156                c->depth != vsf->bitsPerSample)
157                goto cont;
158        }
159
160        // Use it.
161        memcpy(c_order, order, sizeof(int[4]));
162        return pixfmt;
163
164    cont: ;
165    }
166
167    return AV_PIX_FMT_NONE;
168}
169
170static av_cold int read_header_vs(AVFormatContext *s)
171{
172    AVStream *st;
173    AVIOContext *pb = s->pb;
174    VSContext *vs = s->priv_data;
175    int64_t sz = avio_size(pb);
176    char *buf = NULL;
177    char dummy;
178    const VSVideoInfo *info;
179    struct VSState *vss_state;
180    int err = 0;
181
182    vss_state = av_mallocz(sizeof(*vss_state));
183    if (!vss_state) {
184        err = AVERROR(ENOMEM);
185        goto done;
186    }
187
188    vs->vss_state = av_buffer_create(NULL, 0, free_vss_state, vss_state, 0);
189    if (!vs->vss_state) {
190        err = AVERROR(ENOMEM);
191        av_free(vss_state);
192        goto done;
193    }
194
195    if (!vsscript_init()) {
196        av_log(s, AV_LOG_ERROR, "Failed to initialize VSScript (possibly PYTHONPATH not set).\n");
197        err = AVERROR_EXTERNAL;
198        goto done;
199    }
200
201    if (vsscript_createScript(&vss_state->vss)) {
202        av_log(s, AV_LOG_ERROR, "Failed to create script instance.\n");
203        err = AVERROR_EXTERNAL;
204        vsscript_finalize();
205        goto done;
206    }
207
208    if (sz < 0 || sz > vs->max_script_size) {
209        if (sz < 0)
210            av_log(s, AV_LOG_WARNING, "Could not determine file size\n");
211        sz = vs->max_script_size;
212    }
213
214    buf = av_malloc(sz + 1);
215    if (!buf) {
216        err = AVERROR(ENOMEM);
217        goto done;
218    }
219    sz = avio_read(pb, buf, sz);
220
221    if (sz < 0) {
222        av_log(s, AV_LOG_ERROR, "Could not read script.\n");
223        err = sz;
224        goto done;
225    }
226
227    // Data left means our buffer (the max_script_size option) is too small
228    if (avio_read(pb, &dummy, 1) == 1) {
229        av_log(s, AV_LOG_ERROR, "File size is larger than max_script_size option "
230               "value %"PRIi64", consider increasing the max_script_size option\n",
231               vs->max_script_size);
232        err = AVERROR_BUFFER_TOO_SMALL;
233        goto done;
234    }
235
236    buf[sz] = '\0';
237    if (vsscript_evaluateScript(&vss_state->vss, buf, s->url, 0)) {
238        const char *msg = vsscript_getError(vss_state->vss);
239        av_log(s, AV_LOG_ERROR, "Failed to parse script: %s\n", msg ? msg : "(unknown)");
240        err = AVERROR_EXTERNAL;
241        goto done;
242    }
243
244    vs->vsapi = vsscript_getVSApi();
245    vs->vscore = vsscript_getCore(vss_state->vss);
246
247    vs->outnode = vsscript_getOutput(vss_state->vss, 0);
248    if (!vs->outnode) {
249        av_log(s, AV_LOG_ERROR, "Could not get script output node.\n");
250        err = AVERROR_EXTERNAL;
251        goto done;
252    }
253
254    st = avformat_new_stream(s, NULL);
255    if (!st) {
256        err = AVERROR(ENOMEM);
257        goto done;
258    }
259
260    info = vs->vsapi->getVideoInfo(vs->outnode);
261
262    if (!info->format || !info->width || !info->height) {
263        av_log(s, AV_LOG_ERROR, "Non-constant input format not supported.\n");
264        err = AVERROR_PATCHWELCOME;
265        goto done;
266    }
267
268    if (info->fpsDen) {
269        vs->is_cfr = 1;
270        avpriv_set_pts_info(st, 64, info->fpsDen, info->fpsNum);
271        st->duration = info->numFrames;
272    } else {
273        // VFR. Just set "something".
274        avpriv_set_pts_info(st, 64, 1, AV_TIME_BASE);
275        s->ctx_flags |= AVFMTCTX_UNSEEKABLE;
276    }
277
278    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
279    st->codecpar->codec_id = AV_CODEC_ID_WRAPPED_AVFRAME;
280    st->codecpar->width = info->width;
281    st->codecpar->height = info->height;
282    st->codecpar->format = match_pixfmt(info->format, vs->c_order);
283
284    if (st->codecpar->format == AV_PIX_FMT_NONE) {
285        av_log(s, AV_LOG_ERROR, "Unsupported VS pixel format %s\n", info->format->name);
286        err = AVERROR_EXTERNAL;
287        goto done;
288    }
289    av_log(s, AV_LOG_VERBOSE, "VS format %s -> pixfmt %s\n", info->format->name,
290           av_get_pix_fmt_name(st->codecpar->format));
291
292    if (info->format->colorFamily == cmYCoCg)
293        st->codecpar->color_space = AVCOL_SPC_YCGCO;
294
295done:
296    av_free(buf);
297    return err;
298}
299
300static void free_frame(void *opaque, uint8_t *data)
301{
302    AVFrame *frame = (AVFrame *)data;
303
304    av_frame_free(&frame);
305}
306
307static int get_vs_prop_int(AVFormatContext *s, const VSMap *map, const char *name, int def)
308{
309    VSContext *vs = s->priv_data;
310    int64_t res;
311    int err = 1;
312
313    res = vs->vsapi->propGetInt(map, name, 0, &err);
314    return err || res < INT_MIN || res > INT_MAX ? def : res;
315}
316
317struct vsframe_ref_data {
318    const VSAPI *vsapi;
319    const VSFrameRef *frame;
320    AVBufferRef *vss_state;
321};
322
323static void free_vsframe_ref(void *opaque, uint8_t *data)
324{
325    struct vsframe_ref_data *d = opaque;
326
327    if (d->frame)
328        d->vsapi->freeFrame(d->frame);
329
330    av_buffer_unref(&d->vss_state);
331
332    av_free(d);
333}
334
335static int read_packet_vs(AVFormatContext *s, AVPacket *pkt)
336{
337    VSContext *vs = s->priv_data;
338    AVStream *st = s->streams[0];
339    AVFrame *frame = NULL;
340    char vserr[80];
341    const VSFrameRef *vsframe;
342    const VSVideoInfo *info = vs->vsapi->getVideoInfo(vs->outnode);
343    const VSMap *props;
344    const AVPixFmtDescriptor *desc;
345    AVBufferRef *vsframe_ref = NULL;
346    struct vsframe_ref_data *ref_data;
347    int err = 0;
348    int i;
349
350    if (vs->current_frame >= info->numFrames)
351        return AVERROR_EOF;
352
353    ref_data = av_mallocz(sizeof(*ref_data));
354    if (!ref_data) {
355        err = AVERROR(ENOMEM);
356        goto end;
357    }
358
359    // (the READONLY flag is important because the ref is reused for plane data)
360    vsframe_ref = av_buffer_create(NULL, 0, free_vsframe_ref, ref_data, AV_BUFFER_FLAG_READONLY);
361    if (!vsframe_ref) {
362        err = AVERROR(ENOMEM);
363        av_free(ref_data);
364        goto end;
365    }
366
367    vsframe = vs->vsapi->getFrame(vs->current_frame, vs->outnode, vserr, sizeof(vserr));
368    if (!vsframe) {
369        av_log(s, AV_LOG_ERROR, "Error getting frame: %s\n", vserr);
370        err = AVERROR_EXTERNAL;
371        goto end;
372    }
373
374    ref_data->vsapi = vs->vsapi;
375    ref_data->frame = vsframe;
376
377    ref_data->vss_state = av_buffer_ref(vs->vss_state);
378    if (!ref_data->vss_state) {
379        err = AVERROR(ENOMEM);
380        goto end;
381    }
382
383    props = vs->vsapi->getFramePropsRO(vsframe);
384
385    frame = av_frame_alloc();
386    if (!frame) {
387        err = AVERROR(ENOMEM);
388        goto end;
389    }
390
391    frame->format       = st->codecpar->format;
392    frame->width        = st->codecpar->width;
393    frame->height       = st->codecpar->height;
394    frame->colorspace   = st->codecpar->color_space;
395
396    // Values according to ISO/IEC 14496-10.
397    frame->colorspace       = get_vs_prop_int(s, props, "_Matrix",      frame->colorspace);
398    frame->color_primaries  = get_vs_prop_int(s, props, "_Primaries",   frame->color_primaries);
399    frame->color_trc        = get_vs_prop_int(s, props, "_Transfer",    frame->color_trc);
400
401    if (get_vs_prop_int(s, props, "_ColorRange", 1) == 0)
402        frame->color_range = AVCOL_RANGE_JPEG;
403
404    frame->sample_aspect_ratio.num = get_vs_prop_int(s, props, "_SARNum", 0);
405    frame->sample_aspect_ratio.den = get_vs_prop_int(s, props, "_SARDen", 1);
406
407    av_assert0(vs->vsapi->getFrameWidth(vsframe, 0) == frame->width);
408    av_assert0(vs->vsapi->getFrameHeight(vsframe, 0) == frame->height);
409
410    desc = av_pix_fmt_desc_get(frame->format);
411
412    for (i = 0; i < info->format->numPlanes; i++) {
413        int p = vs->c_order[i];
414        ptrdiff_t plane_h = frame->height;
415
416        frame->data[i] = (void *)vs->vsapi->getReadPtr(vsframe, p);
417        frame->linesize[i] = vs->vsapi->getStride(vsframe, p);
418
419        frame->buf[i] = av_buffer_ref(vsframe_ref);
420        if (!frame->buf[i]) {
421            err = AVERROR(ENOMEM);
422            goto end;
423        }
424
425        // Each plane needs an AVBufferRef that indicates the correct plane
426        // memory range. VapourSynth doesn't even give us the memory range,
427        // so make up a bad guess to make FFmpeg happy (even if almost nothing
428        // checks the memory range).
429        if (i == 1 || i == 2)
430            plane_h = AV_CEIL_RSHIFT(plane_h, desc->log2_chroma_h);
431        frame->buf[i]->data = frame->data[i];
432        frame->buf[i]->size = frame->linesize[i] * plane_h;
433    }
434
435    pkt->buf = av_buffer_create((uint8_t*)frame, sizeof(*frame),
436                                free_frame, NULL, 0);
437    if (!pkt->buf) {
438        err = AVERROR(ENOMEM);
439        goto end;
440    }
441
442    frame = NULL; // pkt owns it now
443
444    pkt->data   = pkt->buf->data;
445    pkt->size   = pkt->buf->size;
446    pkt->flags |= AV_PKT_FLAG_TRUSTED;
447
448    if (vs->is_cfr)
449        pkt->pts = vs->current_frame;
450
451    vs->current_frame++;
452
453end:
454    av_frame_free(&frame);
455    av_buffer_unref(&vsframe_ref);
456    return err;
457}
458
459static int read_seek_vs(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
460{
461    VSContext *vs = s->priv_data;
462
463    if (!vs->is_cfr)
464        return AVERROR(ENOSYS);
465
466    vs->current_frame = FFMIN(FFMAX(0, ts), s->streams[0]->duration);
467    return 0;
468}
469
470static av_cold int probe_vs(const AVProbeData *p)
471{
472    // Explicitly do not support this. VS scripts are written in Python, and
473    // can run arbitrary code on the user's system.
474    return 0;
475}
476
477static const AVClass class_vs = {
478    .class_name = "VapourSynth demuxer",
479    .item_name  = av_default_item_name,
480    .option     = options,
481    .version    = LIBAVUTIL_VERSION_INT,
482};
483
484const AVInputFormat ff_vapoursynth_demuxer = {
485    .name           = "vapoursynth",
486    .long_name      = NULL_IF_CONFIG_SMALL("VapourSynth demuxer"),
487    .priv_data_size = sizeof(VSContext),
488    .flags_internal = FF_FMT_INIT_CLEANUP,
489    .read_probe     = probe_vs,
490    .read_header    = read_header_vs,
491    .read_packet    = read_packet_vs,
492    .read_close     = read_close_vs,
493    .read_seek      = read_seek_vs,
494    .priv_class     = &class_vs,
495};
496