Skip to content

Instantly share code, notes, and snippets.

@Patman86
Last active March 20, 2022 13:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Patman86/8f7ae3ef3f5a6631093548ed905f338f to your computer and use it in GitHub Desktop.
Save Patman86/8f7ae3ef3f5a6631093548ed905f338f to your computer and use it in GitHub Desktop.
Alternative_Vapoursynth_Demuxer_new.patch
From 524ec54573997214cc8fa9360380534d4ef100c4 Mon Sep 17 00:00:00 2001
From: Patman86 <54327252+Patman86@users.noreply.github.com>
Date: Fri, 18 Mar 2022 21:51:16 +0100
Subject: [PATCH] Add Alternative Vapoursynth Demuxer New
---
configure | 1 +
libavformat/Makefile | 1 +
libavformat/allformats.c | 1 +
libavformat/vapoursynth_new.c | 376 ++++++++++++++++++++++++++++++++++
4 files changed, 379 insertions(+)
create mode 100644 libavformat/vapoursynth_new.c
diff --git a/configure b/configure
index 7f087d31a0..f3b77442f5 100755
--- a/configure
+++ b/configure
@@ -3378,6 +3378,7 @@ libxvid_encoder_deps="libxvid"
libzvbi_teletext_decoder_deps="libzvbi"
vapoursynth_demuxer_deps="vapoursynth"
vapoursynth_alt_demuxer_deps="vapoursynth"
+vapoursynth_new_demuxer_deps="vapoursynth"
videotoolbox_suggest="coreservices"
videotoolbox_deps="corefoundation coremedia corevideo"
videotoolbox_encoder_deps="videotoolbox VTCompressionSessionPrepareToEncodeFrames"
diff --git a/libavformat/Makefile b/libavformat/Makefile
index f4659e35cf..d0e7cd6ea4 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -623,6 +623,7 @@ OBJS-$(CONFIG_LIBMODPLUG_DEMUXER) += libmodplug.o
OBJS-$(CONFIG_LIBOPENMPT_DEMUXER) += libopenmpt.o
OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o
OBJS-$(CONFIG_VAPOURSYNTH_ALT_DEMUXER) += vapoursynth_alt.o
+OBJS-$(CONFIG_VAPOURSYNTH_NEW_DEMUXER) += vapoursynth_new.o
# protocols I/O
OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 73f9fcd80a..1d0e097d4d 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -538,6 +538,7 @@ extern const AVInputFormat ff_libmodplug_demuxer;
extern const AVInputFormat ff_libopenmpt_demuxer;
extern const AVInputFormat ff_vapoursynth_demuxer;
extern const AVInputFormat ff_vapoursynth_alt_demuxer;
+extern const AVInputFormat ff_vapoursynth_new_demuxer;
#include "libavformat/muxer_list.c"
#include "libavformat/demuxer_list.c"
diff --git a/libavformat/vapoursynth_new.c b/libavformat/vapoursynth_new.c
new file mode 100644
index 0000000000..818f90165f
--- /dev/null
+++ b/libavformat/vapoursynth_new.c
@@ -0,0 +1,376 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+* @file
+* VapourSynth demuxer
+*
+* Synthesizes vapour (?)
+*/
+
+#include <stdatomic.h>
+
+#include <vapoursynth/VSScript4.h>
+#include <vapoursynth/VSHelper4.h>
+
+#include "libavutil/cpu.h"
+#include "libavutil/internal.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/time.h"
+#include "libavcodec/internal.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef const VSSCRIPTAPI* (VS_CC* type_getVSScriptAPI)(int version);
+
+typedef struct VSContext
+{
+ type_getVSScriptAPI func_getVSScriptAPI;
+ const VSSCRIPTAPI * vssapi;
+ const VSAPI * vsapi;
+ const VSVideoInfo * vi;
+ VSNode * node;
+ VSScript * script;
+ atomic_int async_pending;
+ int current_frame;
+ int ncpu;
+}VSContext;
+
+static void VS_CC async_callback(void* user_data, const VSFrame * f, int n, VSNode * node, const char* error_msg)
+{
+ AVFormatContext * s = user_data;
+ VSContext * vs = s->priv_data;
+
+ if (!f)
+ av_log(s, AV_LOG_WARNING, "Async frame request failed: %s\n", error_msg);
+
+ vs->vsapi->freeFrame(f);
+ atomic_fetch_sub(&vs->async_pending, 1);
+}
+
+static av_cold int map_vsformat(AVFormatContext * s, AVStream * st, const VSVideoFormat * vsformat)
+{
+ if (vsformat.subSamplingW > 2 || vsformat.subSamplingH > 2)
+ return AV_PIX_FMT_NONE;
+
+ switch (vsformat.colorFamily)
+{
+ case cfYUV:
+ switch ((vsformat.subSamplingW << 2) | vsformat.subSamplingH)
+{
+ case ((0 << 2) | 0): /* 4:4:4 */
+ switch (vsformat.bitsPerSample)
+{
+ case 8: return AV_PIX_FMT_YUV444P;
+ case 9: return AV_PIX_FMT_YUV444P9;
+ case 10: return AV_PIX_FMT_YUV444P10;
+ case 12: return AV_PIX_FMT_YUV444P12;
+ case 14: return AV_PIX_FMT_YUV444P14;
+ case 16: return AV_PIX_FMT_YUV444P16;
+ default: return AV_PIX_FMT_NONE;
+}
+ case ((0 << 2) | 1): /* 4:4:0 */
+ switch (vsformat.bitsPerSample)
+{
+ case 8: return AV_PIX_FMT_YUV440P;
+ case 10: return AV_PIX_FMT_YUV440P10;
+ case 12: return AV_PIX_FMT_YUV440P12;
+ default: return AV_PIX_FMT_NONE;
+}
+ case ((1 << 2) | 0): /* 4:2:2 */
+ switch (vsformat.bitsPerSample)
+{
+ case 8: return AV_PIX_FMT_YUV422P;
+ case 9: return AV_PIX_FMT_YUV422P9;
+ case 10: return AV_PIX_FMT_YUV422P10;
+ case 12: return AV_PIX_FMT_YUV422P12;
+ case 14: return AV_PIX_FMT_YUV422P14;
+ case 16: return AV_PIX_FMT_YUV422P16;
+ default: return AV_PIX_FMT_NONE;
+}
+ case ((1 << 2) | 1): /* 4:2:0 */
+ switch (vsformat.bitsPerSample)
+{
+ case 8: return AV_PIX_FMT_YUV420P;
+ case 9: return AV_PIX_FMT_YUV420P9;
+ case 10: return AV_PIX_FMT_YUV420P10;
+ case 12: return AV_PIX_FMT_YUV420P12;
+ case 14: return AV_PIX_FMT_YUV420P14;
+ case 16: return AV_PIX_FMT_YUV420P16;
+ default: return AV_PIX_FMT_NONE;
+}
+ case ((2 << 2) | 0): /* 4:1:1 */
+ return vsformat.bitsPerSample == 8 ? AV_PIX_FMT_YUV411P : AV_PIX_FMT_NONE;
+ case ((2 << 2) | 2): /* 4:1:0 */
+ return vsformat.bitsPerSample == 8 ? AV_PIX_FMT_YUV410P : AV_PIX_FMT_NONE;
+}
+ case cfRGB:
+ switch (vsformat.bitsPerSample)
+{
+ case 8: return AV_PIX_FMT_GBRP;
+ case 9: return AV_PIX_FMT_GBRP9;
+ case 10: return AV_PIX_FMT_GBRP10;
+ case 12: return AV_PIX_FMT_GBRP12;
+ case 14: return AV_PIX_FMT_GBRP14;
+ case 16: return AV_PIX_FMT_GBRP16;
+ case 32: return AV_PIX_FMT_GBRPF32;
+ default: return AV_PIX_FMT_NONE;
+}
+ case cfGray:
+ switch (vsformat.bitsPerSample)
+{
+ case 8: return AV_PIX_FMT_GRAY8;
+ case 9: return AV_PIX_FMT_GRAY9;
+ case 10: return AV_PIX_FMT_GRAY10;
+ case 12: return AV_PIX_FMT_GRAY12;
+ case 14: return AV_PIX_FMT_GRAY14;
+ case 16: return AV_PIX_FMT_GRAY16;
+ case 32: return AV_PIX_FMT_GRAYF32;
+ default: return AV_PIX_FMT_NONE;
+}
+ default:
+ return AV_PIX_FMT_NONE;
+}
+}
+
+static av_cold int create_video_stream(AVFormatContext * s)
+{
+ AVStream * st;
+ VSContext * vs = s->priv_data;
+ char formatNname[32];
+ const VSVideoInfo * vi = vs->vi;
+
+ if (!isConstantVideoFormat(vi))
+{
+ av_log(s, AV_LOG_ERROR, "Non-constant input format not supported\n");
+ return AVERROR_PATCHWELCOME;
+}
+
+ if (!(st = avformat_new_stream(s, NULL)))
+ return AVERROR_UNKNOWN;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ st->codecpar->width = vi->width;
+ st->codecpar->height = vi->height;
+ st->codecpar->format = map_vsformat(s, st, vi->format);
+
+ st->avg_frame_rate = (AVRational){ vi->fpsNum, vi->fpsDen };
+ st->start_time = 0;
+ st->duration = vi->numFrames;
+ st->nb_frames = vi->numFrames;
+ avpriv_set_pts_info(st, 64, vi->fpsDen, vi->fpsNum);
+
+ if (st->codecpar->format == AV_PIX_FMT_NONE)
+{
+ av_log(s, AV_LOG_ERROR, "Unsupported VS pixel format %s\n", vsapi->getVideoFormatName(&vi->format, formatNname));
+ return AVERROR_EXTERNAL;
+
+}
+
+ av_log(s, AV_LOG_VERBOSE, "VS format %s -> pixfmt %s\n", vsapi->getVideoFormatName(&vi->format, formatNname),
+ av_get_pix_fmt_name(st->codecpar->format));
+
+ return 0;
+}
+
+static av_cold int open_script(AVFormatContext * s)
+{
+ VSContext * vs = s->priv_data;
+ VSCore * core = vs->vsapi->createCore(0);
+ vs->script = vs->vssapi->createScript(core);
+ vs->vssapi->evalSetWorkingDir(vs->script, 1);
+ int ret;
+
+ if (vs->vssapi->evaluateFile(&vs->script, s->url, 0))
+{
+ av_log(s, AV_LOG_ERROR, "VapourSynth script evaluation failed.\n");
+ return AVERROR_EXTERNAL;
+}
+
+ if (!(vs->node = vsscript_getOutputNode(vs->script, 0)))
+{
+ av_log(s, AV_LOG_ERROR, "Could not get script output node.\n");
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+}
+
+ vs->vi = vs->vsapi->getVideoInfo(vs->node);
+ if (ret = create_video_stream(s))
+ goto fail;
+
+ return 0;
+fail:
+ vs->vsapi->freeNode(vs->node);
+ vsscript_freeScript(vs->script);
+ return ret;
+}
+
+static av_cold int read_header(AVFormatContext * s)
+{
+ VSContext * vs = s->priv_data;
+ int ret;
+
+ if (!vs->vssapi = vs->func_getVSScriptAPI(VSSCRIPT_API_VERSION))
+{
+ av_log(s, AV_LOG_ERROR, "Failed to initialize VapourSynth environment.\n");
+ return AVERROR_EXTERNAL;
+}
+
+ if ((ret = vsscript_getApiVersion()) < VSSCRIPT_API_VERSION)
+{
+ av_log(s, AV_LOG_ERROR, "VSScript API too old: %d versus %d.\n", ret, VSSCRIPT_API_VERSION);
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+}
+
+ if (!(vs->vsapi = vsscript_getVSApi(VAPOURSYNTH_API_VERSION)))
+{
+ av_log(s, AV_LOG_ERROR, "VapourSynth API too old: %d required.\n", VAPOURSYNTH_API_VERSION);
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+}
+
+ if (ret = open_script(s))
+ goto fail;
+
+ vs->ncpu = av_cpu_count();
+ return 0;
+fail:
+ return ret;
+}
+
+static int read_packet(AVFormatContext * s, AVPacket * pkt)
+{
+ static const int gbr_order[3] = { 1, 2, 0 };
+
+ AVStream * st = s->streams[0];
+ VSContext * vs = s->priv_data;
+ const VSFrame * vs_frame = NULL;
+ uint8_t * dst_ptr;
+ char vserr[256];
+ int i, n, plane, ret;
+
+ if (vs->current_frame >= vs->vi->numFrames)
+ return AVERROR_EOF;
+
+ n = vs->current_frame++;
+ if (st->discard == AVDISCARD_ALL)
+ return 0;
+
+ pkt->size = 0;
+ for (plane = 0; plane < vs->vi->format.numPlanes; ++plane)
+{
+ int width = vs->vi->width;
+ int height = vs->vi->height;
+
+ if (plane == 1 || plane == 2)
+{
+ width >>= vs->vi->format.subSamplingW;
+ height >>= vs->vi->format.subSamplingH;
+}
+
+ pkt->size += (int64_t)width * height * vs->vi->format.bytesPerSample;
+
+}
+
+ if ((ret = av_new_packet(pkt, pkt->size)) < 0)
+ return ret;
+
+ pkt->pts = n;
+ pkt->dts = n;
+ pkt->duration = 1;
+ pkt->stream_index = 0;
+
+ vs_frame = vs->vsapi->getFrame(n, vs->node, vserr, sizeof(vserr));
+ if (!vs_frame)
+{
+ av_log(s, AV_LOG_ERROR, "Error retrieving frame %d: %s\n", n, vserr);
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+}
+
+ /* Prefetch the subsequent frames. */
+ for (i = 0; i < vs->ncpu; ++i)
+{
+ if (i >= vs->vi->numFrames - n)
+ break;
+ vs->vsapi->getFrameAsync(n + i, vs->node, async_callback, s);
+ atomic_fetch_add(&vs->async_pending, 1);
+}
+
+ dst_ptr = pkt->data;
+ for (plane = 0; plane < vs->vi->format.numPlanes; ++plane)
+{
+ int src_plane = vs->vi->format.colorFamily == cfRGB ? gbr_order[plane] : plane;
+ const uint8_t * src_ptr = vs->vsapi->getReadPtr(vs_frame, src_plane);
+ int width = vs->vsapi->getFrameWidth(vs_frame, src_plane);
+ int height = vs->vsapi->getFrameHeight(vs_frame, src_plane);
+ int stride = vs->vsapi->getStride(vs_frame, src_plane);
+ int row_size = width * vs->vi->format.bytesPerSample;
+ vsh_bitblt(dst_ptr, row_size, src_ptr, stride, row_size, height);
+ dst_ptr += (int64_t)row_size * height;
+
+}
+
+ vs->vsapi->freeFrame(vs_frame);
+ return 0;
+fail:
+ vs->vsapi->freeFrame(vs_frame);
+ av_packet_unref(pkt);
+ return ret;
+}
+
+static av_cold int read_close(AVFormatContext * s)
+{
+ VSContext * vs = s->priv_data;
+
+ /* Wait for any async requests to complete. */
+ while (atomic_load(&vs->async_pending))
+{
+ av_usleep(1000);
+}
+ vs->vsapi->freeNode(vs->node);
+ vsscript_freeScript(vs->script);
+ return 0;
+}
+
+static int read_seek(AVFormatContext * s, int stream_index, int64_t timestamp, int flags)
+{
+ VSContext * vs = s->priv_data;
+
+ vs->current_frame = FFMIN(FFMAX(0, timestamp), s->streams[0]->duration);
+ return 0;
+}
+
+static av_cold int read_probe(const AVProbeData * p)
+{
+ // Explicitly do not support this. VS scripts are written in Python, and
+ // can run arbitrary code on the user's system.
+ return 0;
+}
+
+const AVInputFormat ff_vapoursynth_new_demuxer = {
+.name = "vapoursynth_new",
+.long_name = NULL_IF_CONFIG_SMALL("Alternative VapourSynth demuxer"),
+.priv_data_size = sizeof(VSContext),
+.read_probe = read_probe,
+.read_header = read_header,
+.read_packet = read_packet,
+.read_close = read_close,
+.read_seek = read_seek,
+};
+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment