git: 2d664816a4a4 - main - audio/cmus: Fix build with FFmpeg 8

From: Nuno Teixeira <eduardo_at_FreeBSD.org>
Date: Sun, 24 Aug 2025 19:25:52 UTC
The branch main has been updated by eduardo:

URL: https://cgit.FreeBSD.org/ports/commit/?id=2d664816a4a4ec943cfde8227013744c3d3719fb

commit 2d664816a4a4ec943cfde8227013744c3d3719fb
Author:     Nuno Teixeira <eduardo@FreeBSD.org>
AuthorDate: 2025-08-24 19:13:46 +0000
Commit:     Nuno Teixeira <eduardo@FreeBSD.org>
CommitDate: 2025-08-24 19:19:00 +0000

    audio/cmus: Fix build with FFmpeg 8
    
    Apply upstream patch to fix build
---
 audio/cmus/files/patch-fix-ffmpeg8 | 1841 ++++++++++++++++++++++++++++++++++++
 1 file changed, 1841 insertions(+)

diff --git a/audio/cmus/files/patch-fix-ffmpeg8 b/audio/cmus/files/patch-fix-ffmpeg8
new file mode 100644
index 000000000000..ff1407a55467
--- /dev/null
+++ b/audio/cmus/files/patch-fix-ffmpeg8
@@ -0,0 +1,1841 @@
+From 9f3b9efd8bd5508ffd069cbd0c228857ee11e1e5 Mon Sep 17 00:00:00 2001
+From: ihy123 <aladinandreyy@gmail.com>
+Date: Thu, 14 Aug 2025 12:44:10 +0300
+Subject: [PATCH 01/12] ip/ffmpeg: more precise seeking
+
+av_seek_frame() and avformat_seek_file() seek to nearest "keyframe". For
+codecs like, for example, ape this means that seeking will be very off
+(5 seconds or more). So what we do is:
+1. seek to nearest "keyframe" before the desired time,
+2. discard some frames to approach the desired time.
+---
+ ip/ffmpeg.c | 154 ++++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 94 insertions(+), 60 deletions(-)
+
+diff --git  ip/ffmpeg.c  ip/ffmpeg.c
+index 21b9a01f4..ecbf00582 100644
+---  ip/ffmpeg.c
++++  ip/ffmpeg.c
+@@ -44,6 +44,8 @@ struct ffmpeg_input {
+ 	AVPacket pkt;
+ 	int curr_pkt_size;
+ 	uint8_t *curr_pkt_buf;
++	int64_t seek_ts;
++	int64_t prev_frame_end;
+ 	int stream_index;
+ 
+ 	unsigned long curr_size;
+@@ -76,6 +78,8 @@ static struct ffmpeg_input *ffmpeg_input_create(void)
+ 		return NULL;
+ 	}
+ 	input->curr_pkt_size = 0;
++	input->seek_ts = -1;
++	input->prev_frame_end = -1;
+ 	input->curr_pkt_buf = input->pkt.data;
+ 	return input;
+ }
+@@ -314,10 +318,7 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ #else
+ 	AVFrame *frame = avcodec_alloc_frame();
+ #endif
+-	int got_frame;
+ 	while (1) {
+-		int len;
+-
+ 		if (input->curr_pkt_size <= 0) {
+ #if LIBAVCODEC_VERSION_MAJOR >= 56
+ 			av_packet_unref(&input->pkt);
+@@ -333,78 +334,108 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ #endif
+ 				return 0;
+ 			}
+-			if (input->pkt.stream_index == input->stream_index) {
+-				input->curr_pkt_size = input->pkt.size;
+-				input->curr_pkt_buf = input->pkt.data;
+-				input->curr_size += input->pkt.size;
+-				input->curr_duration += input->pkt.duration;
+-			}
+-			continue;
+-		}
+ 
+-		{
+-			AVPacket avpkt;
+-			av_new_packet(&avpkt, input->curr_pkt_size);
+-			memcpy(avpkt.data, input->curr_pkt_buf, input->curr_pkt_size);
++			if (input->pkt.stream_index != input->stream_index)
++				continue;
++			input->curr_pkt_size = input->pkt.size;
++			input->curr_pkt_buf = input->pkt.data;
++			input->curr_size += input->pkt.size;
++			input->curr_duration += input->pkt.duration;
++
+ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+-			int send_result = avcodec_send_packet(cc, &avpkt);
+-			if (send_result != 0) {
+-				if (send_result != AVERROR(EAGAIN)) {
+-					d_print("avcodec_send_packet() returned %d\n", send_result);
+-					char errstr[AV_ERROR_MAX_STRING_SIZE];
+-					if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE ))
+-					{
+-						d_print("av_strerror(): %s\n", errstr);
+-					} else {
+-						d_print("av_strerror(): Description for error cannot be found\n");
+-					}
+-					av_packet_unref(&avpkt);
+-					return -IP_ERROR_INTERNAL;
++			int send_result = avcodec_send_packet(cc, &input->pkt);
++			if (send_result != 0 && send_result != AVERROR(EAGAIN)) {
++				d_print("avcodec_send_packet() returned %d\n", send_result);
++				char errstr[AV_ERROR_MAX_STRING_SIZE];
++				if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE ))
++				{
++					d_print("av_strerror(): %s\n", errstr);
++				} else {
++					d_print("av_strerror(): Description for error cannot be found\n");
+ 				}
+-				len = 0;
+-			} else {
+-				len = input->curr_pkt_size;
++				return -IP_ERROR_INTERNAL;
+ 			}
+-
+-			int recv_result = avcodec_receive_frame(cc, frame);
+-			got_frame = (recv_result == 0) ? 1 : 0;
+-#else
+-			len = avcodec_decode_audio4(cc, frame, &got_frame, &avpkt);
+-#endif
+-#if LIBAVCODEC_VERSION_MAJOR >= 56
+-			av_packet_unref(&avpkt);
+-#else
+-			av_free_packet(&avpkt);
+ #endif
+ 		}
++
++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++		int recv_result = avcodec_receive_frame(cc, frame);
++		if (recv_result < 0) {
++			input->curr_pkt_size = 0;
++			continue;
++		}
++#else
++		int got_frame;
++		int len = avcodec_decode_audio4(cc, frame, &got_frame, &input->pkt);
+ 		if (len < 0) {
+ 			/* this is often reached when seeking, not sure why */
+ 			input->curr_pkt_size = 0;
+ 			continue;
+ 		}
+-		input->curr_pkt_size -= len;
+-		input->curr_pkt_buf += len;
+-		if (got_frame) {
+-			int res = swr_convert(swr,
+-					&output->buffer,
+-					frame->nb_samples,
+-					(const uint8_t **)frame->extended_data,
+-					frame->nb_samples);
+-			if (res < 0)
+-				res = 0;
+-			output->buffer_pos = output->buffer;
++		if (!got_frame)
++			continue;
++#endif
++
++		int64_t frame_ts = -1;
++		if (frame->pts)
++			frame_ts = frame->pts;
++		else if (frame->pkt_pts)
++			frame_ts = frame->pkt_pts;
++		else if (frame->pkt_dts)
++			frame_ts = frame->pkt_dts;
++
++		const uint8_t **in = (const uint8_t **)frame->extended_data;
++		int in_count = frame->nb_samples;
++		if (input->seek_ts > 0 && (frame_ts >= 0 || input->prev_frame_end >= 0)) {
++			struct ffmpeg_private *priv = ip_data->private;
++			AVStream *st = priv->input_context->streams[priv->input->stream_index];
++			if (frame_ts >= 0)
++				frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q);
++			else
++				frame_ts = input->prev_frame_end;
++			int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
++			int64_t frame_end = frame_ts + frame_dur;
++			input->prev_frame_end = frame_end;
++			d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
++			if (frame_end <= input->seek_ts)
++				continue;
++
++			/* skip part of this frame */
++			int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
++			in_count -= skip_samples;
++			if (av_sample_fmt_is_planar(frame->format)) {
++				for (int i = 0; i < cc->channels; i++) {
++					in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
++				}
++			} else {
++				*in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
++			}
++
++			input->seek_ts = -1;
++			input->prev_frame_end = -1;
++		}
++
++		int res = swr_convert(swr,
++				&output->buffer,
++				frame->nb_samples,
++				in,
++				in_count);
++		if (res < 0)
++			res = 0;
++
++		output->buffer_pos = output->buffer;
+ #if LIBAVCODEC_VERSION_MAJOR >= 60
+-			output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf);
++		output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf);
+ #else
+-			output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf);
++		output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf);
+ #endif
++
+ #if LIBAVCODEC_VERSION_MAJOR >= 56
+-			av_frame_free(&frame);
++		av_frame_free(&frame);
+ #else
+-			avcodec_free_frame(&frame);
++		avcodec_free_frame(&frame);
+ #endif
+-			return output->buffer_used_len;
+-		}
++		return output->buffer_used_len;
+ 	}
+ 	/* This should never get here. */
+ 	return -IP_ERROR_INTERNAL;
+@@ -437,13 +468,16 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
+ 	AVStream *st = priv->input_context->streams[priv->input->stream_index];
+ 	int ret;
+ 
+-	int64_t pts = av_rescale_q(offset * AV_TIME_BASE, AV_TIME_BASE_Q, st->time_base);
++	priv->input->seek_ts = offset * AV_TIME_BASE;
++	priv->input->prev_frame_end = -1;
++	int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num);
+ 
+ 	avcodec_flush_buffers(priv->codec_context);
+ 	/* Force reading a new packet in next ffmpeg_fill_buffer(). */
+ 	priv->input->curr_pkt_size = 0;
+ 
+-	ret = av_seek_frame(priv->input_context, priv->input->stream_index, pts, 0);
++	ret = avformat_seek_file(priv->input_context,
++			priv->input->stream_index, 0, ts, ts, 0);
+ 
+ 	if (ret < 0) {
+ 		return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
+
+From ec84fa7b4b4a72c19e2ab04eac864c99df6d2e4e Mon Sep 17 00:00:00 2001
+From: ihy123 <aladinandreyy@gmail.com>
+Date: Fri, 15 Aug 2025 21:42:19 +0300
+Subject: [PATCH 02/12] ip/ffmpeg: skip samples only when needed
+
+---
+ ip/ffmpeg.c | 32 ++++++++++++++++++--------------
+ 1 file changed, 18 insertions(+), 14 deletions(-)
+
+diff --git  ip/ffmpeg.c  ip/ffmpeg.c
+index ecbf00582..5f5a4f37b 100644
+---  ip/ffmpeg.c
++++  ip/ffmpeg.c
+@@ -393,22 +393,26 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ 				frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q);
+ 			else
+ 				frame_ts = input->prev_frame_end;
+-			int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
+-			int64_t frame_end = frame_ts + frame_dur;
+-			input->prev_frame_end = frame_end;
+-			d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
+-			if (frame_end <= input->seek_ts)
+-				continue;
+ 
+-			/* skip part of this frame */
+-			int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
+-			in_count -= skip_samples;
+-			if (av_sample_fmt_is_planar(frame->format)) {
+-				for (int i = 0; i < cc->channels; i++) {
+-					in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
++			if (frame_ts < input->seek_ts) {
++				int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
++				int64_t frame_end = frame_ts + frame_dur;
++				input->prev_frame_end = frame_end;
++				d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
++				if (frame_end <= input->seek_ts)
++					continue;
++
++				/* skip part of this frame */
++				int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
++				in_count -= skip_samples;
++				if (av_sample_fmt_is_planar(frame->format)) {
++					for (int i = 0; i < cc->channels; i++) {
++						in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
++					}
++				} else {
++					*in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
+ 				}
+-			} else {
+-				*in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
++				d_print("skipping %ld samples\n", skip_samples);
+ 			}
+ 
+ 			input->seek_ts = -1;
+
+From 70a8761fc1d30bfa302332d0807b89c3776d3f31 Mon Sep 17 00:00:00 2001
+From: ihy123 <aladinandreyy@gmail.com>
+Date: Sat, 16 Aug 2025 02:43:55 +0300
+Subject: [PATCH 03/12] ip/ffmpeg: remove excessive version checks
+
+ffmpeg download page states that v4.0.6 has
+- libavutil 56.14.100
+- libavcodec 58.18.100
+- libavformat 58.12.100
+(https://ffmpeg.org/olddownload.html)
+
+After removing all checks for versions lower than these, the plugin
+still compiles with v3.3.9 headers.
+
+After all, why be better with compatibility than developers themselves?
+---
+ ip/ffmpeg.c | 109 +++++++++++-----------------------------------------
+ 1 file changed, 23 insertions(+), 86 deletions(-)
+
+diff --git  ip/ffmpeg.c  ip/ffmpeg.c
+index 5f5a4f37b..f6a11f450 100644
+---  ip/ffmpeg.c
++++  ip/ffmpeg.c
+@@ -25,7 +25,6 @@
+ #include "../config/ffmpeg.h"
+ #endif
+ 
+-#include <stdio.h>
+ #include <libavcodec/avcodec.h>
+ #include <libavformat/avformat.h>
+ #include <libavformat/avio.h>
+@@ -43,7 +42,6 @@
+ struct ffmpeg_input {
+ 	AVPacket pkt;
+ 	int curr_pkt_size;
+-	uint8_t *curr_pkt_buf;
+ 	int64_t seek_ts;
+ 	int64_t prev_frame_end;
+ 	int stream_index;
+@@ -80,17 +78,12 @@ static struct ffmpeg_input *ffmpeg_input_create(void)
+ 	input->curr_pkt_size = 0;
+ 	input->seek_ts = -1;
+ 	input->prev_frame_end = -1;
+-	input->curr_pkt_buf = input->pkt.data;
+ 	return input;
+ }
+ 
+ static void ffmpeg_input_free(struct ffmpeg_input *input)
+ {
+-#if LIBAVCODEC_VERSION_MAJOR >= 56
+ 	av_packet_unref(&input->pkt);
+-#else
+-	av_free_packet(&input->pkt);
+-#endif
+ 	free(input);
+ }
+ 
+@@ -132,7 +125,7 @@ static void ffmpeg_init(void)
+ 
+ 	av_log_set_level(AV_LOG_QUIET);
+ 
+-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100)
++#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
+ 	/* We could register decoders explicitly to save memory, but we have to
+ 	 * be careful about compatibility. */
+ 	av_register_all();
+@@ -149,9 +142,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 	AVCodec const *codec;
+ 	AVCodecContext *cc = NULL;
+ 	AVFormatContext *ic = NULL;
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 	AVCodecParameters *cp = NULL;
+-#endif
+ 	SwrContext *swr = NULL;
+ 
+ 	ffmpeg_init();
+@@ -171,20 +162,11 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 		}
+ 
+ 		for (i = 0; i < ic->nb_streams; i++) {
+-
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 			cp = ic->streams[i]->codecpar;
+ 			if (cp->codec_type == AVMEDIA_TYPE_AUDIO) {
+ 				stream_index = i;
+ 				break;
+ 			}
+-#else
+-			cc = ic->streams[i]->codec;
+-			if (cc->codec_type == AVMEDIA_TYPE_AUDIO) {
+-				stream_index = i;
+-				break;
+-			}
+-#endif
+ 		}
+ 
+ 		if (stream_index == -1) {
+@@ -193,13 +175,9 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 			break;
+ 		}
+ 
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 		codec = avcodec_find_decoder(cp->codec_id);
+ 		cc = avcodec_alloc_context3(codec);
+ 		avcodec_parameters_to_context(cc, cp);
+-#else
+-		codec = avcodec_find_decoder(cc->codec_id);
+-#endif
+ 		if (!codec) {
+ 			d_print("codec not found: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id));
+ 			err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
+@@ -217,9 +195,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 
+ 	if (err < 0) {
+ 		/* Clean up.  cc is never opened at this point.  (See above assumption.) */
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 		avcodec_free_context(&cc);
+-#endif
+ 		avformat_close_input(&ic);
+ 		return err;
+ 	}
+@@ -231,9 +207,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 	priv->input = ffmpeg_input_create();
+ 	if (priv->input == NULL) {
+ 		avcodec_close(cc);
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 		avcodec_free_context(&cc);
+-#endif
+ 		avformat_close_input(&ic);
+ 		free(priv);
+ 		return -IP_ERROR_INTERNAL;
+@@ -244,7 +218,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 	/* Prepare for resampling. */
+ 	out_sample_rate = min_u(cc->sample_rate, 384000);
+ 	swr = swr_alloc();
+-#if LIBAVCODEC_VERSION_MAJOR >= 60
++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
+ 	if (cc->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
+ 		av_channel_layout_default(&cc->ch_layout, cc->ch_layout.nb_channels);
+ 	av_opt_set_chlayout(swr, "in_chlayout",   &cc->ch_layout, 0);
+@@ -259,7 +233,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 	priv->swr = swr;
+ 
+ 	ip_data->private = priv;
+-#if LIBAVCODEC_VERSION_MAJOR >= 60
++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
+ 	ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->ch_layout.nb_channels);
+ #else
+ 	ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->channels);
+@@ -281,10 +255,12 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
+ 	}
+ 	swr_init(swr);
+ 	ip_data->sf |= sf_host_endian();
+-#if LIBAVCODEC_VERSION_MAJOR >= 60
+-	channel_map_init_waveex(cc->ch_layout.nb_channels, cc->ch_layout.u.mask, ip_data->channel_map);
++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++	channel_map_init_waveex(cc->ch_layout.nb_channels,
++			cc->ch_layout.u.mask, ip_data->channel_map);
+ #else
+-	channel_map_init_waveex(cc->channels, cc->channel_layout, ip_data->channel_map);
++	channel_map_init_waveex(cc->channels,
++			cc->channel_layout, ip_data->channel_map);
+ #endif
+ 	return 0;
+ }
+@@ -294,9 +270,7 @@ static int ffmpeg_close(struct input_plugin_data *ip_data)
+ 	struct ffmpeg_private *priv = ip_data->private;
+ 
+ 	avcodec_close(priv->codec_context);
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 	avcodec_free_context(&priv->codec_context);
+-#endif
+ 	avformat_close_input(&priv->input_context);
+ 	swr_free(&priv->swr);
+ 	ffmpeg_input_free(priv->input);
+@@ -310,39 +284,27 @@ static int ffmpeg_close(struct input_plugin_data *ip_data)
+  * This returns the number of bytes added to the buffer.
+  * It returns < 0 on error.  0 on EOF.
+  */
+-static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext *ic, AVCodecContext *cc,
+-			      struct ffmpeg_input *input, struct ffmpeg_output *output, SwrContext *swr)
++static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data,
++		AVFormatContext *ic, AVCodecContext *cc,
++		struct ffmpeg_input *input, struct ffmpeg_output *output,
++		SwrContext *swr)
+ {
+-#if LIBAVCODEC_VERSION_MAJOR >= 56
+ 	AVFrame *frame = av_frame_alloc();
+-#else
+-	AVFrame *frame = avcodec_alloc_frame();
+-#endif
+ 	while (1) {
+ 		if (input->curr_pkt_size <= 0) {
+-#if LIBAVCODEC_VERSION_MAJOR >= 56
+ 			av_packet_unref(&input->pkt);
+-#else
+-			av_free_packet(&input->pkt);
+-#endif
+ 			if (av_read_frame(ic, &input->pkt) < 0) {
+ 				/* Force EOF once we can read no longer. */
+-#if LIBAVCODEC_VERSION_MAJOR >= 56
+ 				av_frame_free(&frame);
+-#else
+-				avcodec_free_frame(&frame);
+-#endif
+ 				return 0;
+ 			}
+ 
+ 			if (input->pkt.stream_index != input->stream_index)
+ 				continue;
+ 			input->curr_pkt_size = input->pkt.size;
+-			input->curr_pkt_buf = input->pkt.data;
+ 			input->curr_size += input->pkt.size;
+ 			input->curr_duration += input->pkt.duration;
+ 
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 			int send_result = avcodec_send_packet(cc, &input->pkt);
+ 			if (send_result != 0 && send_result != AVERROR(EAGAIN)) {
+ 				d_print("avcodec_send_packet() returned %d\n", send_result);
+@@ -355,32 +317,17 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ 				}
+ 				return -IP_ERROR_INTERNAL;
+ 			}
+-#endif
+ 		}
+ 
+-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+ 		int recv_result = avcodec_receive_frame(cc, frame);
+ 		if (recv_result < 0) {
+ 			input->curr_pkt_size = 0;
+ 			continue;
+ 		}
+-#else
+-		int got_frame;
+-		int len = avcodec_decode_audio4(cc, frame, &got_frame, &input->pkt);
+-		if (len < 0) {
+-			/* this is often reached when seeking, not sure why */
+-			input->curr_pkt_size = 0;
+-			continue;
+-		}
+-		if (!got_frame)
+-			continue;
+-#endif
+ 
+ 		int64_t frame_ts = -1;
+ 		if (frame->pts)
+ 			frame_ts = frame->pts;
+-		else if (frame->pkt_pts)
+-			frame_ts = frame->pkt_pts;
+ 		else if (frame->pkt_dts)
+ 			frame_ts = frame->pkt_dts;
+ 
+@@ -395,7 +342,7 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ 				frame_ts = input->prev_frame_end;
+ 
+ 			if (frame_ts < input->seek_ts) {
+-				int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
++				int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, frame->sample_rate);
+ 				int64_t frame_end = frame_ts + frame_dur;
+ 				input->prev_frame_end = frame_end;
+ 				d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
+@@ -403,14 +350,14 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ 					continue;
+ 
+ 				/* skip part of this frame */
+-				int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
++				int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, frame->sample_rate, AV_TIME_BASE);
+ 				in_count -= skip_samples;
+ 				if (av_sample_fmt_is_planar(frame->format)) {
+-					for (int i = 0; i < cc->channels; i++) {
++					for (int i = 0; i < sf_get_channels(ip_data->sf); i++) {
+ 						in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
+ 					}
+ 				} else {
+-					*in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
++					*in += skip_samples * sf_get_frame_size(ip_data->sf);
+ 				}
+ 				d_print("skipping %ld samples\n", skip_samples);
+ 			}
+@@ -428,17 +375,9 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
+ 			res = 0;
+ 
+ 		output->buffer_pos = output->buffer;
+-#if LIBAVCODEC_VERSION_MAJOR >= 60
+-		output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf);
+-#else
+-		output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf);
+-#endif
++		output->buffer_used_len = res * sf_get_frame_size(ip_data->sf);
+ 
+-#if LIBAVCODEC_VERSION_MAJOR >= 56
+ 		av_frame_free(&frame);
+-#else
+-		avcodec_free_frame(&frame);
+-#endif
+ 		return output->buffer_used_len;
+ 	}
+ 	/* This should never get here. */
+@@ -453,11 +392,11 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun
+ 	int out_size;
+ 
+ 	if (output->buffer_used_len == 0) {
+-		rc = ffmpeg_fill_buffer(ip_data, priv->input_context, priv->codec_context,
++		rc = ffmpeg_fill_buffer(ip_data,
++				priv->input_context, priv->codec_context,
+ 				priv->input, priv->output, priv->swr);
+-		if (rc <= 0) {
++		if (rc <= 0)
+ 			return rc;
+-		}
+ 	}
+ 	out_size = min_i(output->buffer_used_len, count);
+ 	memcpy(buffer, output->buffer_pos, out_size);
+@@ -477,6 +416,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
+ 	int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num);
+ 
+ 	avcodec_flush_buffers(priv->codec_context);
++	/* TODO: also flush swresample buffers */
+ 	/* Force reading a new packet in next ffmpeg_fill_buffer(). */
+ 	priv->input->curr_pkt_size = 0;
+ 
+@@ -501,7 +441,8 @@ static void ffmpeg_read_metadata(struct growing_keyvals *c, AVDictionary *metada
+ 	}
+ }
+ 
+-static int ffmpeg_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
++static int ffmpeg_read_comments(struct input_plugin_data *ip_data,
++		struct keyval **comments)
+ {
+ 	struct ffmpeg_private *priv = ip_data->private;
+ 	AVFormatContext *ic = priv->input_context;
+@@ -538,11 +479,7 @@ static long ffmpeg_current_bitrate(struct input_plugin_data *ip_data)
+ 	AVStream *st = priv->input_context->streams[priv->input->stream_index];
+ 	long bitrate = -1;
+ 	/* ape codec returns silly numbers */
+-#if LIBAVCODEC_VERSION_MAJOR >= 55
+ 	if (priv->codec->id == AV_CODEC_ID_APE)
+-#else
+-	if (priv->codec->id == CODEC_ID_APE)
+-#endif
+ 		return -1;
+ 	if (priv->input->curr_duration > 0) {
+ 		double seconds = priv->input->curr_duration * av_q2d(st->time_base);
+
+From e1a2374a60a41987f95c7d892ebc1b150df7acb1 Mon Sep 17 00:00:00 2001
+From: ihy123 <aladinandreyy@gmail.com>
+Date: Sun, 17 Aug 2025 04:05:36 +0300
+Subject: [PATCH 04/12] ip/ffmpeg: major refactor
+
+---
+ ip/ffmpeg.c | 643 +++++++++++++++++++++++++++-------------------------
+ 1 file changed, 330 insertions(+), 313 deletions(-)
+
+diff --git  ip/ffmpeg.c  ip/ffmpeg.c
+index f6a11f450..42f630ee7 100644
+---  ip/ffmpeg.c
++++  ip/ffmpeg.c
+@@ -35,84 +35,32 @@
+ #include <libavutil/mathematics.h>
+ #endif
+ 
+-#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE
+-#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
+-#endif
++struct ffmpeg_private {
++	AVCodecContext *codec_ctx;
++	AVFormatContext *format_ctx;
++	AVCodec const *codec;
++	SwrContext *swr;
++	int stream_index;
+ 
+-struct ffmpeg_input {
+-	AVPacket pkt;
+-	int curr_pkt_size;
++	AVPacket *pkt;
++	AVFrame *frame;
+ 	int64_t seek_ts;
+ 	int64_t prev_frame_end;
+-	int stream_index;
+ 
++	/* A buffer to hold swr_convert()-ed samples */
++	AVFrame *swr_frame;
++	int swr_frame_start;
++
++	/* Bitrate estimation */
+ 	unsigned long curr_size;
+ 	unsigned long curr_duration;
+ };
+ 
+-struct ffmpeg_output {
+-	uint8_t *buffer;
+-	uint8_t *buffer_malloc;
+-	uint8_t *buffer_pos;	/* current buffer position */
+-	int buffer_used_len;
+-};
+-
+-struct ffmpeg_private {
+-	AVCodecContext *codec_context;
+-	AVFormatContext *input_context;
+-	AVCodec const *codec;
+-	SwrContext *swr;
+-
+-	struct ffmpeg_input *input;
+-	struct ffmpeg_output *output;
+-};
+-
+-static struct ffmpeg_input *ffmpeg_input_create(void)
+-{
+-	struct ffmpeg_input *input = xnew(struct ffmpeg_input, 1);
+-
+-	if (av_new_packet(&input->pkt, 0) != 0) {
+-		free(input);
+-		return NULL;
+-	}
+-	input->curr_pkt_size = 0;
+-	input->seek_ts = -1;
+-	input->prev_frame_end = -1;
+-	return input;
+-}
+-
+-static void ffmpeg_input_free(struct ffmpeg_input *input)
+-{
+-	av_packet_unref(&input->pkt);
+-	free(input);
+-}
+-
+-static struct ffmpeg_output *ffmpeg_output_create(void)
+-{
+-	struct ffmpeg_output *output = xnew(struct ffmpeg_output, 1);
+-
+-	output->buffer_malloc = xnew(uint8_t, AVCODEC_MAX_AUDIO_FRAME_SIZE + 15);
+-	output->buffer = output->buffer_malloc;
+-	/* align to 16 bytes so avcodec can SSE/Altivec/etc */
+-	while ((intptr_t) output->buffer % 16)
+-		output->buffer += 1;
+-	output->buffer_pos = output->buffer;
+-	output->buffer_used_len = 0;
+-	return output;
+-}
+-
+-static void ffmpeg_output_free(struct ffmpeg_output *output)
+-{
+-	free(output->buffer_malloc);
+-	output->buffer_malloc = NULL;
+-	output->buffer = NULL;
+-	free(output);
+-}
+-
+-static inline void ffmpeg_buffer_flush(struct ffmpeg_output *output)
++static const char *ffmpeg_errmsg(int err)
+ {
+-	output->buffer_pos = output->buffer;
+-	output->buffer_used_len = 0;
++	static char errstr[AV_ERROR_MAX_STRING_SIZE];
++	av_strerror(err, errstr, AV_ERROR_MAX_STRING_SIZE);
++	return errstr;
+ }
+ 
+ static void ffmpeg_init(void)
+@@ -132,303 +80,372 @@ static void ffmpeg_init(void)
+ #endif
+ }
+ 
+-static int ffmpeg_open(struct input_plugin_data *ip_data)
++static int ffmpeg_open_input(struct input_plugin_data *ip_data,
++		struct ffmpeg_private *priv)
+ {
+-	struct ffmpeg_private *priv;
+-	int err = 0;
+-	int i;
+-	int stream_index = -1;
+-	int out_sample_rate;
+-	AVCodec const *codec;
+-	AVCodecContext *cc = NULL;
+ 	AVFormatContext *ic = NULL;
++	AVCodecContext *cc = NULL;
+ 	AVCodecParameters *cp = NULL;
+-	SwrContext *swr = NULL;
+-
+-	ffmpeg_init();
++	AVCodec const *codec = NULL;
++	int stream_index = -1;
+ 
+-	err = avformat_open_input(&ic, ip_data->filename, NULL, NULL);
+-	if (err < 0) {
+-		d_print("av_open failed: %d\n", err);
+-		return -IP_ERROR_FILE_FORMAT;
++	int err;
++	int res = avformat_open_input(&ic, ip_data->filename, NULL, NULL);
++	if (res < 0) {
++		err = -IP_ERROR_FILE_FORMAT;
++		goto err;
+ 	}
+ 
+-	do {
+-		err = avformat_find_stream_info(ic, NULL);
+-		if (err < 0) {
+-			d_print("unable to find stream info: %d\n", err);
+-			err = -IP_ERROR_FILE_FORMAT;
+-			break;
+-		}
+-
+-		for (i = 0; i < ic->nb_streams; i++) {
+-			cp = ic->streams[i]->codecpar;
+-			if (cp->codec_type == AVMEDIA_TYPE_AUDIO) {
+-				stream_index = i;
+-				break;
+-			}
+-		}
+-
+-		if (stream_index == -1) {
+-			d_print("could not find audio stream\n");
+-			err = -IP_ERROR_FILE_FORMAT;
+-			break;
+-		}
+-
+-		codec = avcodec_find_decoder(cp->codec_id);
+-		cc = avcodec_alloc_context3(codec);
+-		avcodec_parameters_to_context(cc, cp);
+-		if (!codec) {
+-			d_print("codec not found: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id));
+-			err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
+-			break;
+-		}
++	res = avformat_find_stream_info(ic, NULL);
++	if (res < 0) {
++		d_print("unable to find stream info\n");
++		err = -IP_ERROR_FILE_FORMAT;
++		goto err;
++	}
+ 
+-		if (avcodec_open2(cc, codec, NULL) < 0) {
+-			d_print("could not open codec: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id));
+-			err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
++	for (int i = 0; i < ic->nb_streams; i++) {
++		cp = ic->streams[i]->codecpar;
++		if (cp->codec_type == AVMEDIA_TYPE_AUDIO) {
++			stream_index = i;
+ 			break;
+ 		}
++	}
+ 
+-		/* We assume below that no more errors follow. */
+-	} while (0);
++	if (stream_index == -1) {
++		d_print("could not find audio stream\n");
++		err = -IP_ERROR_FILE_FORMAT;
++		goto err_silent;
++	}
+ 
+-	if (err < 0) {
+-		/* Clean up.  cc is never opened at this point.  (See above assumption.) */
+-		avcodec_free_context(&cc);
+-		avformat_close_input(&ic);
+-		return err;
++	codec = avcodec_find_decoder(cp->codec_id);
++	if (!codec) {
++		d_print("codec (id: %d, name: %s) not found\n",
++				cc->codec_id, avcodec_get_name(cc->codec_id));
++		err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
++		goto err_silent;
++	}
++	cc = avcodec_alloc_context3(codec);
++	avcodec_parameters_to_context(cc, cp);
++
++	res = avcodec_open2(cc, codec, NULL);
++	if (res < 0) {
++		d_print("could not open codec (id: %d, name: %s)\n",
++				cc->codec_id, avcodec_get_name(cc->codec_id));
++		err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
++		goto err;
+ 	}
+ 
+-	priv = xnew(struct ffmpeg_private, 1);
+-	priv->codec_context = cc;
+-	priv->input_context = ic;
++	priv->format_ctx = ic;
++	priv->codec_ctx = cc;
+ 	priv->codec = codec;
+-	priv->input = ffmpeg_input_create();
+-	if (priv->input == NULL) {
+-		avcodec_close(cc);
+-		avcodec_free_context(&cc);
+-		avformat_close_input(&ic);
+-		free(priv);
+-		return -IP_ERROR_INTERNAL;
++	priv->stream_index = stream_index;
++	return 0;
++err:
++	d_print("%s\n", ffmpeg_errmsg(res));
++err_silent:
++	avcodec_free_context(&cc);
++	avformat_close_input(&ic);
++	return err;
++}
++
++static void ffmpeg_set_sf_and_swr_opts(SwrContext *swr, AVCodecContext *cc,
++		sample_format_t *sf_out, enum AVSampleFormat *out_sample_fmt)
++{
++	int out_sample_rate = min_u(cc->sample_rate, 384000);
++	sample_format_t sf = sf_rate(out_sample_rate) | sf_host_endian();
++	av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0);
++	av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
++
++	*out_sample_fmt = cc->sample_fmt;
++	switch (*out_sample_fmt) {
++		case AV_SAMPLE_FMT_U8:
++			sf |= sf_bits(8) | sf_signed(0);
++			break;
++		case AV_SAMPLE_FMT_S32:
++			sf |= sf_bits(32) | sf_signed(1);
++			break;
++		default:
++			sf |= sf_bits(16) | sf_signed(1);
++			*out_sample_fmt = AV_SAMPLE_FMT_S16;
+ 	}
+-	priv->input->stream_index = stream_index;
+-	priv->output = ffmpeg_output_create();
++	av_opt_set_sample_fmt(swr, "in_sample_fmt", cc->sample_fmt, 0);
++	av_opt_set_sample_fmt(swr, "out_sample_fmt", *out_sample_fmt, 0);
+ 
+-	/* Prepare for resampling. */
+-	out_sample_rate = min_u(cc->sample_rate, 384000);
+-	swr = swr_alloc();
+ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++	sf |= sf_channels(cc->ch_layout.nb_channels);
++
+ 	if (cc->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
+ 		av_channel_layout_default(&cc->ch_layout, cc->ch_layout.nb_channels);
+-	av_opt_set_chlayout(swr, "in_chlayout",   &cc->ch_layout, 0);
+-	av_opt_set_chlayout(swr, "out_chlayout",  &cc->ch_layout, 0);
++	av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0);
++	av_opt_set_chlayout(swr, "out_chlayout", &cc->ch_layout, 0);
+ #else
+-	av_opt_set_int(swr, "in_channel_layout",  av_get_default_channel_layout(cc->channels), 0);
+-	av_opt_set_int(swr, "out_channel_layout", av_get_default_channel_layout(cc->channels), 0);
++	sf |= sf_channels(cc->channels);
++
++	av_opt_set_int(swr, "in_channel_layout",
++			av_get_default_channel_layout(cc->channels), 0);
++	av_opt_set_int(swr, "out_channel_layout",
++			av_get_default_channel_layout(cc->channels), 0);
+ #endif
+-	av_opt_set_int(swr, "in_sample_rate",     cc->sample_rate, 0);
+-	av_opt_set_int(swr, "out_sample_rate",    out_sample_rate, 0);
+-	av_opt_set_sample_fmt(swr, "in_sample_fmt",  cc->sample_fmt, 0);
+-	priv->swr = swr;
+ 
+-	ip_data->private = priv;
++	*sf_out = sf;
++}
++
++static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv,
++		sample_format_t sf, enum AVSampleFormat out_sample_fmt)
++{
++	AVCodecContext *cc = priv->codec_ctx;
++	AVFrame *frame = av_frame_alloc();
++
+ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
+-	ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->ch_layout.nb_channels);
++	av_channel_layout_copy(&frame->ch_layout, &cc->ch_layout);
+ #else
+-	ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->channels);
++	frame->channel_layout = av_get_default_channel_layout(cc->channels);
+ #endif
+-	switch (cc->sample_fmt) {
+-	case AV_SAMPLE_FMT_U8:
+-		ip_data->sf |= sf_bits(8) | sf_signed(0);
+-		av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_U8,  0);
+-		break;
+-	case AV_SAMPLE_FMT_S32:
+-		ip_data->sf |= sf_bits(32) | sf_signed(1);
+-		av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S32,  0);
+-		break;
+-	/* AV_SAMPLE_FMT_S16 */
+-	default:
+-		ip_data->sf |= sf_bits(16) | sf_signed(1);
*** 878 LINES SKIPPED ***