Last active
March 20, 2022 13:17
-
-
Save Patman86/8f7ae3ef3f5a6631093548ed905f338f to your computer and use it in GitHub Desktop.
Alternative_Vapoursynth_Demuxer_new.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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