summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-04-30 00:52:32 +0200
committerwm4 <wm4@nowhere>2013-04-30 00:52:32 +0200
commitea7b920184a865a3343001318fc4a32dcfc7b4f5 (patch)
treefbefd8539b77ee7473ad73740cc8ffa97bc4ffbe /video
parent3dcc83a70609d392c8ecd917dd5c16995424e9c4 (diff)
parentd98e61ea438db66323734ad1b6bea66411a3c97b (diff)
downloadmpv-ea7b920184a865a3343001318fc4a32dcfc7b4f5.tar.bz2
mpv-ea7b920184a865a3343001318fc4a32dcfc7b4f5.tar.xz
Merge branch 'master' into low_quality_intel_crap
Conflicts: video/out/gl_video_shaders.glsl video/out/vo_opengl.c
Diffstat (limited to 'video')
-rw-r--r--video/decode/dec_video.c4
-rw-r--r--video/decode/dec_video.h3
-rw-r--r--video/decode/lavc.h10
-rw-r--r--video/decode/vd.h3
-rw-r--r--video/decode/vd_lavc.c212
-rw-r--r--video/filter/vf.c4
-rw-r--r--video/filter/vf_divtc.c5
-rw-r--r--video/filter/vf_lavfi.c348
-rw-r--r--video/filter/vf_phase.c2
-rw-r--r--video/image_writer.c37
-rw-r--r--video/mp_image.c139
-rw-r--r--video/mp_image.h11
-rw-r--r--video/out/aspect.c94
-rw-r--r--video/out/aspect.h10
-rw-r--r--video/out/cocoa_common.m82
-rw-r--r--video/out/gl_cocoa.c67
-rw-r--r--video/out/gl_common.c855
-rw-r--r--video/out/gl_common.h25
-rw-r--r--video/out/gl_header_fixes.h3
-rw-r--r--video/out/gl_lcms.c219
-rw-r--r--video/out/gl_lcms.h16
-rw-r--r--video/out/gl_video.c1799
-rw-r--r--video/out/gl_video.h72
-rw-r--r--video/out/gl_video_shaders.glsl (renamed from video/out/vo_opengl_shaders.glsl)47
-rw-r--r--video/out/gl_w32.c218
-rw-r--r--video/out/gl_wayland.c261
-rw-r--r--video/out/gl_x11.c317
-rw-r--r--video/out/vo.c34
-rw-r--r--video/out/vo.h29
-rw-r--r--video/out/vo_corevideo.m103
-rw-r--r--video/out/vo_direct3d.c1
-rw-r--r--video/out/vo_opengl.c2082
-rw-r--r--video/out/vo_opengl_old.c99
-rw-r--r--video/out/vo_sdl.c15
-rw-r--r--video/out/vo_vdpau.c1
-rw-r--r--video/out/vo_x11.c236
-rw-r--r--video/out/vo_xv.c34
-rw-r--r--video/out/w32_common.c12
-rw-r--r--video/out/w32_common.h2
-rw-r--r--video/out/wayland_common.c53
-rw-r--r--video/out/x11_common.c86
-rw-r--r--video/out/x11_common.h5
42 files changed, 4238 insertions, 3417 deletions
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index 9888881e26..465791a1ed 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -275,7 +275,6 @@ int init_best_video_codec(sh_video_t *sh_video, char* video_decoders)
}
void *decode_video(sh_video_t *sh_video, struct demux_packet *packet,
- unsigned char *start, int in_size,
int drop_frame, double pts)
{
mp_image_t *mpi = NULL;
@@ -311,8 +310,7 @@ void *decode_video(sh_video_t *sh_video, struct demux_packet *packet,
}
}
- mpi = sh_video->vd_driver->decode(sh_video, packet, start, in_size,
- drop_frame, &pts);
+ mpi = sh_video->vd_driver->decode(sh_video, packet, drop_frame, &pts);
//------------------------ frame decoded. --------------------
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
index b6b85e86f6..94bd2bce3a 100644
--- a/video/decode/dec_video.h
+++ b/video/decode/dec_video.h
@@ -31,8 +31,7 @@ void uninit_video(sh_video_t *sh_video);
struct demux_packet;
void *decode_video(sh_video_t *sh_video, struct demux_packet *packet,
- unsigned char *start, int in_size, int drop_frame,
- double pts);
+ int drop_frame, double pts);
int get_video_quality_max(sh_video_t *sh_video);
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index d23c3e85f6..41701be1d6 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -5,6 +5,8 @@
#include <libavcodec/avcodec.h>
+#include "config.h"
+
#include "demux/stheader.h"
#include "video/mp_image.h"
@@ -13,25 +15,25 @@ typedef struct ffmpeg_ctx {
AVFrame *pic;
struct hwdec *hwdec;
enum PixelFormat pix_fmt;
- int do_hw_dr1, do_dr1;
+ int do_hw_dr1;
int vo_initialized;
int best_csp;
AVRational last_sample_aspect_ratio;
enum AVDiscard skip_frame;
const char *software_fallback_decoder;
+
+ bool do_dr1;
struct FramePool *dr1_buffer_pool;
struct mp_image_pool *non_dr1_pool;
} vd_ffmpeg_ctx;
+// lavc_dr1.c
int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame);
void mp_codec_release_buffer(AVCodecContext *s, AVFrame *frame);
-
struct FrameBuffer;
-
void mp_buffer_ref(struct FrameBuffer *buffer);
void mp_buffer_unref(struct FrameBuffer *buffer);
bool mp_buffer_is_unique(struct FrameBuffer *buffer);
-
void mp_buffer_pool_free(struct FramePool **pool);
#endif
diff --git a/video/decode/vd.h b/video/decode/vd.h
index c4c2b7f465..88ce4b2f59 100644
--- a/video/decode/vd.h
+++ b/video/decode/vd.h
@@ -34,8 +34,7 @@ typedef struct vd_functions
void (*uninit)(sh_video_t *sh);
int (*control)(sh_video_t *sh, int cmd, void *arg);
struct mp_image *(*decode)(struct sh_video *sh, struct demux_packet *pkt,
- void *data, int len, int flags,
- double *reordered_pts);
+ int flags, double *reordered_pts);
} vd_functions_t;
// NULL terminated array of all drivers
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 4922f57387..8a989feea7 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -59,10 +59,9 @@
#include "core/m_option.h"
-static int init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec);
+static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec);
static void uninit_avctx(sh_video_t *sh);
-static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic);
-static void release_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic);
+static void setup_refcounting_hw(struct AVCodecContext *s);
static void draw_slice_hwdec(struct AVCodecContext *s, const AVFrame *src,
int offset[4], int y, int type, int height);
@@ -168,16 +167,18 @@ static int init(sh_video_t *sh, const char *decoder)
}
}
- if (!init_avctx(sh, decoder, hwdec)) {
+ init_avctx(sh, decoder, hwdec);
+ if (!ctx->avctx) {
if (ctx->software_fallback_decoder) {
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Error initializing hardware "
"decoding, falling back to software decoding.\n");
decoder = ctx->software_fallback_decoder;
ctx->software_fallback_decoder = NULL;
- if (!init_avctx(sh, decoder, NULL)) {
- uninit(sh);
- return 0;
- }
+ init_avctx(sh, decoder, NULL);
+ }
+ if (!ctx->avctx) {
+ uninit(sh);
+ return 0;
}
}
return 1;
@@ -241,12 +242,14 @@ static void set_from_bih(AVCodecContext *avctx, uint32_t format,
avctx->coded_height = bih->biHeight;
}
-static int init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
+static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
{
vd_ffmpeg_ctx *ctx = sh->context;
struct lavc_param *lavc_param = &sh->opts->lavc_param;
bool mp_rawvideo = false;
+ assert(!ctx->avctx);
+
if (strcmp(decoder, "mp-rawvideo") == 0) {
mp_rawvideo = true;
decoder = "rawvideo";
@@ -254,7 +257,7 @@ static int init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
AVCodec *lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec)
- return 0;
+ return;
ctx->do_dr1 = ctx->do_hw_dr1 = 0;
ctx->pix_fmt = PIX_FMT_NONE;
@@ -274,17 +277,22 @@ static int init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
ctx->do_hw_dr1 = true;
avctx->thread_count = 1;
avctx->get_format = get_format_hwdec;
- avctx->get_buffer = get_buffer_hwdec;
- avctx->release_buffer = release_buffer_hwdec;
+ setup_refcounting_hw(avctx);
if (ctx->hwdec->api == HWDEC_VDPAU) {
avctx->draw_horiz_band = draw_slice_hwdec;
avctx->slice_flags =
SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
}
- } else if (lavc_codec->capabilities & CODEC_CAP_DR1) {
- ctx->do_dr1 = true;
- avctx->get_buffer = mp_codec_get_buffer;
- avctx->release_buffer = mp_codec_release_buffer;
+ } else {
+#if HAVE_AVUTIL_REFCOUNTING
+ avctx->refcounted_frames = 1;
+#else
+ if (lavc_codec->capabilities & CODEC_CAP_DR1) {
+ ctx->do_dr1 = true;
+ avctx->get_buffer = mp_codec_get_buffer;
+ avctx->release_buffer = mp_codec_release_buffer;
+ }
+#endif
}
if (avctx->thread_count == 0) {
@@ -321,8 +329,8 @@ static int init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
mp_msg(MSGT_DECVIDEO, MSGL_ERR,
"Your options /%s/ look like gibberish to me pal\n",
lavc_param->avopt);
- uninit(sh);
- return 0;
+ uninit_avctx(sh);
+ return;
}
}
@@ -352,9 +360,8 @@ static int init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
if (avcodec_open2(avctx, lavc_codec, NULL) < 0) {
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Could not open codec.\n");
uninit_avctx(sh);
- return 0;
+ return;
}
- return 1;
}
static void uninit_avctx(sh_video_t *sh)
@@ -370,10 +377,12 @@ static void uninit_avctx(sh_video_t *sh)
av_freep(&avctx->slice_offset);
}
- av_freep(&avctx);
+ av_freep(&ctx->avctx);
avcodec_free_frame(&ctx->pic);
+#if !HAVE_AVUTIL_REFCOUNTING
mp_buffer_pool_free(&ctx->dr1_buffer_pool);
+#endif
}
static void uninit(sh_video_t *sh)
@@ -466,9 +475,8 @@ static void draw_slice_hwdec(struct AVCodecContext *s,
vf->control(vf, VFCTRL_HWDEC_DECODER_RENDER, state_ptr);
}
-static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
+static struct mp_image *get_surface_hwdec(struct sh_video *sh, AVFrame *pic)
{
- sh_video_t *sh = avctx->opaque;
vd_ffmpeg_ctx *ctx = sh->context;
/* Decoders using ffmpeg's hwaccel architecture (everything except vdpau)
@@ -484,10 +492,10 @@ static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
*/
int imgfmt = pixfmt2imgfmt(pic->format);
if (!IMGFMT_IS_HWACCEL(imgfmt))
- return -1;
+ return NULL;
if (init_vo(sh, pic) < 0)
- return -1;
+ return NULL;
assert(IMGFMT_IS_HWACCEL(ctx->best_csp));
@@ -495,11 +503,52 @@ static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
struct vf_instance *vf = sh->vfilter;
vf->control(vf, VFCTRL_HWDEC_ALLOC_SURFACE, &mpi);
+
+ if (mpi) {
+ for (int i = 0; i < 4; i++)
+ pic->data[i] = mpi->planes[i];
+ }
+
+ return mpi;
+}
+
+#if HAVE_AVUTIL_REFCOUNTING
+
+static void free_mpi(void *opaque, uint8_t *data)
+{
+ struct mp_image *mpi = opaque;
+ talloc_free(mpi);
+}
+
+static int get_buffer2_hwdec(AVCodecContext *avctx, AVFrame *pic, int flags)
+{
+ sh_video_t *sh = avctx->opaque;
+
+ struct mp_image *mpi = get_surface_hwdec(sh, pic);
+ if (!mpi)
+ return -1;
+
+ pic->buf[0] = av_buffer_create(NULL, 0, free_mpi, mpi, 0);
+
+ return 0;
+}
+
+static void setup_refcounting_hw(AVCodecContext *avctx)
+{
+ avctx->get_buffer2 = get_buffer2_hwdec;
+ avctx->refcounted_frames = 1;
+}
+
+#else /* HAVE_AVUTIL_REFCOUNTING */
+
+static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
+{
+ sh_video_t *sh = avctx->opaque;
+
+ struct mp_image *mpi = get_surface_hwdec(sh, pic);
if (!mpi)
return -1;
- for (int i = 0; i < 4; i++)
- pic->data[i] = mpi->planes[i];
pic->opaque = mpi;
pic->type = FF_BUFFER_TYPE_USER;
@@ -525,6 +574,29 @@ static void release_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
pic->data[i] = NULL;
}
+static void setup_refcounting_hw(AVCodecContext *avctx)
+{
+ avctx->get_buffer = get_buffer_hwdec;
+ avctx->release_buffer = release_buffer_hwdec;
+}
+
+#endif /* HAVE_AVUTIL_REFCOUNTING */
+
+#if HAVE_AVUTIL_REFCOUNTING
+
+static struct mp_image *image_from_decoder(struct sh_video *sh)
+{
+ vd_ffmpeg_ctx *ctx = sh->context;
+ AVFrame *pic = ctx->pic;
+
+ struct mp_image *img = mp_image_from_av_frame(pic);
+ av_frame_unref(pic);
+
+ return img;
+}
+
+#else /* HAVE_AVUTIL_REFCOUNTING */
+
static void fb_ref(void *b)
{
mp_buffer_ref(b);
@@ -540,9 +612,35 @@ static bool fb_is_unique(void *b)
return mp_buffer_is_unique(b);
}
-static int decode(struct sh_video *sh, struct demux_packet *packet, void *data,
- int len, int flags, double *reordered_pts,
- struct mp_image **out_image)
+static struct mp_image *image_from_decoder(struct sh_video *sh)
+{
+ vd_ffmpeg_ctx *ctx = sh->context;
+ AVFrame *pic = ctx->pic;
+
+ struct mp_image new = {0};
+ mp_image_copy_fields_from_av_frame(&new, pic);
+
+ struct mp_image *mpi;
+ if (ctx->do_hw_dr1 && pic->opaque) {
+ mpi = pic->opaque; // reordered frame
+ assert(mpi);
+ mpi = mp_image_new_ref(mpi);
+ mp_image_copy_attributes(mpi, &new);
+ } else if (ctx->do_dr1 && pic->opaque) {
+ struct FrameBuffer *fb = pic->opaque;
+ mp_buffer_ref(fb); // initial reference for mpi
+ mpi = mp_image_new_external_ref(&new, fb, fb_ref, fb_unref,
+ fb_is_unique, NULL);
+ } else {
+ mpi = mp_image_pool_new_copy(ctx->non_dr1_pool, &new);
+ }
+ return mpi;
+}
+
+#endif /* HAVE_AVUTIL_REFCOUNTING */
+
+static int decode(struct sh_video *sh, struct demux_packet *packet,
+ int flags, double *reordered_pts, struct mp_image **out_image)
{
int got_picture = 0;
int ret;
@@ -559,8 +657,8 @@ static int decode(struct sh_video *sh, struct demux_packet *packet, void *data,
avctx->skip_frame = ctx->skip_frame;
av_init_packet(&pkt);
- pkt.data = data;
- pkt.size = len;
+ pkt.data = packet ? packet->buffer : NULL;
+ pkt.size = packet ? packet->len : 0;
/* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
* from demuxer. */
if (packet && packet->keyframe)
@@ -585,63 +683,26 @@ static int decode(struct sh_video *sh, struct demux_packet *packet, void *data,
if (init_vo(sh, pic) < 0)
return -1;
- struct mp_image *mpi = NULL;
- if (ctx->do_hw_dr1 && pic->opaque) {
- mpi = pic->opaque; // reordered frame
- assert(mpi);
- mpi = mp_image_new_ref(mpi);
- }
-
- if (!mpi) {
- struct mp_image new = {0};
- mp_image_set_size(&new, pic->width, pic->height);
- mp_image_setfmt(&new, ctx->best_csp);
- for (int i = 0; i < 4; i++) {
- new.planes[i] = pic->data[i];
- new.stride[i] = pic->linesize[i];
- }
- if (ctx->do_dr1 && pic->opaque) {
- struct FrameBuffer *fb = pic->opaque;
- mp_buffer_ref(fb); // initial reference for mpi
- mpi = mp_image_new_external_ref(&new, fb, fb_ref, fb_unref,
- fb_is_unique);
- } else {
- mpi = mp_image_pool_get(ctx->non_dr1_pool, new.imgfmt,
- new.w, new.h);
- mp_image_copy(mpi, &new);
- }
- }
-
+ struct mp_image *mpi = image_from_decoder(sh);
assert(mpi->planes[0]);
mpi->colorspace = sh->colorspace;
mpi->levels = sh->color_range;
- mpi->qscale = pic->qscale_table;
- mpi->qstride = pic->qstride;
- mpi->pict_type = pic->pict_type;
- mpi->qscale_type = pic->qscale_type;
- mpi->fields = MP_IMGFIELD_ORDERED;
- if (pic->interlaced_frame)
- mpi->fields |= MP_IMGFIELD_INTERLACED;
- if (pic->top_field_first)
- mpi->fields |= MP_IMGFIELD_TOP_FIRST;
- if (pic->repeat_pict == 1)
- mpi->fields |= MP_IMGFIELD_REPEAT_FIRST;
*out_image = mpi;
return 1;
}
static struct mp_image *decode_with_fallback(struct sh_video *sh,
- struct demux_packet *packet, void *data,
- int len, int flags, double *reordered_pts)
+ struct demux_packet *packet,
+ int flags, double *reordered_pts)
{
vd_ffmpeg_ctx *ctx = sh->context;
if (!ctx->avctx)
return NULL;
struct mp_image *mpi = NULL;
- int res = decode(sh, packet, data, len, flags, reordered_pts, &mpi);
+ int res = decode(sh, packet, flags, reordered_pts, &mpi);
if (res >= 0)
return mpi;
@@ -653,9 +714,10 @@ static struct mp_image *decode_with_fallback(struct sh_video *sh,
"decoding, falling back to software decoding.\n");
const char *decoder = ctx->software_fallback_decoder;
ctx->software_fallback_decoder = NULL;
- if (init_avctx(sh, decoder, NULL)) {
+ init_avctx(sh, decoder, NULL);
+ if (ctx->avctx) {
mpi = NULL;
- decode(sh, packet, data, len, flags, reordered_pts, &mpi);
+ decode(sh, packet, flags, reordered_pts, &mpi);
return mpi;
}
}
diff --git a/video/filter/vf.c b/video/filter/vf.c
index aa1de0848b..257d65e58b 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -69,6 +69,7 @@ extern const vf_info_t vf_info_sub;
extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d;
extern const vf_info_t vf_info_dlopen;
+extern const vf_info_t vf_info_lavfi;
// list of available filters:
static const vf_info_t *const filter_list[] = {
@@ -85,6 +86,9 @@ static const vf_info_t *const filter_list[] = {
#ifdef CONFIG_LIBPOSTPROC
&vf_info_pp,
#endif
+#ifdef CONFIG_VF_LAVFI
+ &vf_info_lavfi,
+#endif
&vf_info_screenshot,
diff --git a/video/filter/vf_divtc.c b/video/filter/vf_divtc.c
index 5b57e5a4cb..b4718b355f 100644
--- a/video/filter/vf_divtc.c
+++ b/video/filter/vf_divtc.c
@@ -202,8 +202,9 @@ static int imgop(int(*planeop)(unsigned char *, unsigned char *,
int sum = 0;
for (int p = 0; p < dst->num_planes; p++) {
sum += planeop(dst->planes[p], src ? src->planes[p] : NULL,
- dst->w * (dst->fmt.bpp[p] / 8), dst->plane_h[p],
- dst->stride[p], src ? src->stride[p] : 0, arg);
+ (dst->w * dst->fmt.bytes[p]) >> dst->fmt.xs[p],
+ dst->plane_h[p], dst->stride[p],
+ src ? src->stride[p] : 0, arg);
}
return sum;
}
diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c
new file mode 100644
index 0000000000..0b01e9b425
--- /dev/null
+++ b/video/filter/vf_lavfi.c
@@ -0,0 +1,348 @@
+/*
+ * This file is part of mpv.
+ *
+ * Filter graph creation code taken from Libav avplay.c (LGPL 2.1 or later)
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <libavutil/avstring.h>
+#include <libavutil/mem.h>
+#include <libavutil/mathematics.h>
+#include <libavutil/rational.h>
+#include <libavutil/pixdesc.h>
+#include <libavutil/time.h>
+#include <libswscale/swscale.h>
+#include <libavfilter/avfilter.h>
+#include <libavfilter/avfiltergraph.h>
+#include <libavfilter/buffersink.h>
+#include <libavfilter/buffersrc.h>
+
+#include "core/mp_msg.h"
+#include "core/m_option.h"
+#include "core/m_struct.h"
+
+#include "video/img_format.h"
+#include "video/mp_image.h"
+#include "video/sws_utils.h"
+#include "video/fmt-conversion.h"
+#include "vf.h"
+
+#define IS_LIBAV_FORK (LIBAVFILTER_VERSION_MICRO < 100)
+
+// FFmpeg and Libav have slightly different APIs, just enough to cause us
+// unnecessary pain. <Expletive deleted.>
+#if IS_LIBAV_FORK
+#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
+ avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx)
+#else
+#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
+ avfilter_graph_parse(graph, filters, &(inputs), &(outputs), log_ctx)
+#endif
+
+// ":" is deprecated, but "|" doesn't work in earlier versions.
+#if (IS_LIBAV_FORK && LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(3, 7, 0)) || \
+ (!IS_LIBAV_FORK && LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(3, 50, 100))
+#define FMTSEP "|"
+#else
+#define FMTSEP ":"
+#endif
+
+struct vf_priv_s {
+ AVFilterGraph *graph;
+ AVFilterContext *in;
+ AVFilterContext *out;
+
+ AVRational timebase_in;
+ AVRational timebase_out;
+ AVRational par_in;
+
+ // options
+ char *cfg_graph;
+ int64_t cfg_sws_flags;
+};
+
+static const struct vf_priv_s vf_priv_dflt = {
+ .cfg_sws_flags = SWS_BICUBIC,
+};
+
+static void destroy_graph(struct vf_instance *vf)
+{
+ struct vf_priv_s *p = vf->priv;
+ avfilter_graph_free(&p->graph);
+ p->in = p->out = NULL;
+}
+
+static AVRational par_from_sar_dar(int width, int height,
+ int d_width, int d_height)
+{
+ return av_div_q((AVRational){d_width, d_height},
+ (AVRational){width, height});
+}
+
+static void dar_from_sar_par(int width, int height, AVRational par,
+ int *out_dw, int *out_dh)
+{
+ *out_dw = width;
+ *out_dh = height;
+ if (par.num != 0 && par.den != 0) {
+ double d = av_q2d(par);
+ if (d > 1.0) {
+ *out_dw *= d;
+ } else {
+ *out_dh /= d;
+ }
+ }
+}
+
+static bool recreate_graph(struct vf_instance *vf, int width, int height,
+ int d_width, int d_height, unsigned int fmt)
+{
+ void *tmp = talloc_new(NULL);
+ struct vf_priv_s *p = vf->priv;
+ AVFilterContext *in = NULL, *out = NULL, *f_format = NULL;
+
+ if (bstr0(p->cfg_graph).len == 0) {
+ mp_msg(MSGT_VFILTER, MSGL_FATAL, "lavfi: no filter graph set\n");
+ return false;
+ }
+
+ destroy_graph(vf);
+ mp_msg(MSGT_VFILTER, MSGL_V, "lavfi: create graph: '%s'\n", p->cfg_graph);
+
+ AVFilterGraph *graph = avfilter_graph_alloc();
+ if (!graph)
+ goto error;
+
+ AVFilterInOut *outputs = avfilter_inout_alloc();
+ AVFilterInOut *inputs = avfilter_inout_alloc();
+ if (!outputs || !inputs)
+ goto error;
+
+ // Build list of acceptable output pixel formats. libavfilter will insert
+ // conversion filters if needed.
+ char *fmtstr = talloc_strdup(tmp, "");
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
+ if (vf_next_query_format(vf, n)) {
+ const char *name = av_get_pix_fmt_name(imgfmt2pixfmt(n));
+ if (name) {
+ const char *s = fmtstr[0] ? FMTSEP : "";
+ fmtstr = talloc_asprintf_append_buffer(fmtstr, "%s%s", s, name);
+ }
+ }
+ }
+
+ char *sws_flags = talloc_asprintf(tmp, "flags=%"PRId64, p->cfg_sws_flags);
+ graph->scale_sws_opts = av_strdup(sws_flags);
+
+ AVRational par = par_from_sar_dar(width, height, d_width, d_height);
+ AVRational timebase = AV_TIME_BASE_Q;
+
+ char *src_args = talloc_asprintf(tmp, "%d:%d:%d:%d:%d:%d:%d",
+ width, height, imgfmt2pixfmt(fmt),
+ timebase.num, timebase.den,
+ par.num, par.den);
+
+ if (avfilter_graph_create_filter(&in, avfilter_get_by_name("buffer"),
+ "src", src_args, NULL, graph) < 0)
+ goto error;
+
+ if (avfilter_graph_create_filter(&out, avfilter_get_by_name("buffersink"),
+ "out", NULL, NULL, graph) < 0)
+ goto error;
+
+ if (avfilter_graph_create_filter(&f_format, avfilter_get_by_name("format"),
+ "format", fmtstr, NULL, graph) < 0)
+ goto error;
+
+ if (avfilter_link(f_format, 0, out, 0) < 0)
+ goto error;
+
+ outputs->name = av_strdup("in");
+ outputs->filter_ctx = in;
+ outputs->pad_idx = 0;
+ outputs->next = NULL;
+
+ inputs->name = av_strdup("out");
+ inputs->filter_ctx = f_format;
+ inputs->pad_idx = 0;
+ inputs->next = NULL;
+
+ if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
+ goto error;
+
+ if (avfilter_graph_config(graph, NULL) < 0)
+ goto error;
+
+ p->in = in;
+ p->out = out;
+ p->graph = graph;
+
+ assert(out->nb_inputs == 1);
+ assert(in->nb_outputs == 1);
+
+ talloc_free(tmp);
+ return true;