Files
Henrik RydgårdandClaude Opus 5.5 d0e3141f43 FFmpeg: Vendor what PPSSPP uses into ext/ffmpeg, drop the submodule
The ffmpeg submodule held prebuilt libraries for every platform next to the
sources (about 1.5 GB checked out), and nothing uses those any more. Only the
needed sources are copied in, from the submodule's commit, along with every
header and template they include. The path is new, so checking out commits on
either side of this works.

It's also cut down to what PPSSPP uses: the H.264, AAC, MP3, ATRAC3 and ATRAC3+
decoders, the H.264 parser, the demuxers as before, and the FFV1 encoder, AVI
muxer and file protocol for AVI dumping. config.h takes its CONFIG_ values from
FFmpeg 3.0.2's own configure run with just those enabled. The raw H.264 demuxer
stays even though nothing opens raw H.264: probing PSMF data uses it to identify
the video stream's codec, which otherwise comes out as none.

On top of that:
- The files nothing links (configure would build them, but they're never pulled
  out of the static library, going by the GCC link map) are left out: mostly
  libavutil's crypto and hashing and the alternative DCT/IDCT/FFT versions.
- libswresample isn't built, now that nothing uses it.
- The H.264 decoder only does 8-bit video, which is all the PSP has. The SPS
  parser rejects any other bit depth, and the DSP files no longer build their 9-
  to 16-bit variants, which the linker kept because the init functions pick one
  by bit depth at run time.

That's 160 files to compile instead of 277, and 391 of FFmpeg's vendored, about
5.8 MB. The README in ext/ffmpeg has what was done, the PPSSPP patches in the
copy, how to add a component, and the tradeoffs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:54 +01:00

440 lines
14 KiB
C

/*
* samplerate conversion for both audio and video
* Copyright (c) 2000 Fabrice Bellard
*
* 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
* samplerate conversion for both audio and video
*/
#include <string.h>
#include "avcodec.h"
#include "audioconvert.h"
#include "libavutil/opt.h"
#include "libavutil/mem.h"
#include "libavutil/samplefmt.h"
#if FF_API_AVCODEC_RESAMPLE
FF_DISABLE_DEPRECATION_WARNINGS
#define MAX_CHANNELS 8
struct AVResampleContext;
static const char *context_to_name(void *ptr)
{
return "audioresample";
}
static const AVOption options[] = {{NULL}};
static const AVClass audioresample_context_class = {
"ReSampleContext", context_to_name, options, LIBAVUTIL_VERSION_INT
};
struct ReSampleContext {
struct AVResampleContext *resample_context;
short *temp[MAX_CHANNELS];
int temp_len;
float ratio;
/* channel convert */
int input_channels, output_channels, filter_channels;
AVAudioConvert *convert_ctx[2];
enum AVSampleFormat sample_fmt[2]; ///< input and output sample format
unsigned sample_size[2]; ///< size of one sample in sample_fmt
short *buffer[2]; ///< buffers used for conversion to S16
unsigned buffer_size[2]; ///< sizes of allocated buffers
};
/* n1: number of samples */
static void stereo_to_mono(short *output, short *input, int n1)
{
short *p, *q;
int n = n1;
p = input;
q = output;
while (n >= 4) {
q[0] = (p[0] + p[1]) >> 1;
q[1] = (p[2] + p[3]) >> 1;
q[2] = (p[4] + p[5]) >> 1;
q[3] = (p[6] + p[7]) >> 1;
q += 4;
p += 8;
n -= 4;
}
while (n > 0) {
q[0] = (p[0] + p[1]) >> 1;
q++;
p += 2;
n--;
}
}
/* n1: number of samples */
static void mono_to_stereo(short *output, short *input, int n1)
{
short *p, *q;
int n = n1;
int v;
p = input;
q = output;
while (n >= 4) {
v = p[0]; q[0] = v; q[1] = v;
v = p[1]; q[2] = v; q[3] = v;
v = p[2]; q[4] = v; q[5] = v;
v = p[3]; q[6] = v; q[7] = v;
q += 8;
p += 4;
n -= 4;
}
while (n > 0) {
v = p[0]; q[0] = v; q[1] = v;
q += 2;
p += 1;
n--;
}
}
/*
5.1 to stereo input: [fl, fr, c, lfe, rl, rr]
- Left = front_left + rear_gain * rear_left + center_gain * center
- Right = front_right + rear_gain * rear_right + center_gain * center
Where rear_gain is usually around 0.5-1.0 and
center_gain is almost always 0.7 (-3 dB)
*/
static void surround_to_stereo(short **output, short *input, int channels, int samples)
{
int i;
short l, r;
for (i = 0; i < samples; i++) {
int fl,fr,c,rl,rr;
fl = input[0];
fr = input[1];
c = input[2];
// lfe = input[3];
rl = input[4];
rr = input[5];
l = av_clip_int16(fl + (0.5 * rl) + (0.7 * c));
r = av_clip_int16(fr + (0.5 * rr) + (0.7 * c));
/* output l & r. */
*output[0]++ = l;
*output[1]++ = r;
/* increment input. */
input += channels;
}
}
static void deinterleave(short **output, short *input, int channels, int samples)
{
int i, j;
for (i = 0; i < samples; i++) {
for (j = 0; j < channels; j++) {
*output[j]++ = *input++;
}
}
}
static void interleave(short *output, short **input, int channels, int samples)
{
int i, j;
for (i = 0; i < samples; i++) {
for (j = 0; j < channels; j++) {
*output++ = *input[j]++;
}
}
}
static void ac3_5p1_mux(short *output, short *input1, short *input2, int n)
{
int i;
short l, r;
for (i = 0; i < n; i++) {
l = *input1++;
r = *input2++;
*output++ = l; /* left */
*output++ = (l / 2) + (r / 2); /* center */
*output++ = r; /* right */
*output++ = 0; /* left surround */
*output++ = 0; /* right surroud */
*output++ = 0; /* low freq */
}
}
#define SUPPORT_RESAMPLE(ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8) \
ch8<<7 | ch7<<6 | ch6<<5 | ch5<<4 | ch4<<3 | ch3<<2 | ch2<<1 | ch1<<0
static const uint8_t supported_resampling[MAX_CHANNELS] = {
// output ch: 1 2 3 4 5 6 7 8
SUPPORT_RESAMPLE(1, 1, 0, 0, 0, 0, 0, 0), // 1 input channel
SUPPORT_RESAMPLE(1, 1, 0, 0, 0, 1, 0, 0), // 2 input channels
SUPPORT_RESAMPLE(0, 0, 1, 0, 0, 0, 0, 0), // 3 input channels
SUPPORT_RESAMPLE(0, 0, 0, 1, 0, 0, 0, 0), // 4 input channels
SUPPORT_RESAMPLE(0, 0, 0, 0, 1, 0, 0, 0), // 5 input channels
SUPPORT_RESAMPLE(0, 1, 0, 0, 0, 1, 0, 0), // 6 input channels
SUPPORT_RESAMPLE(0, 0, 0, 0, 0, 0, 1, 0), // 7 input channels
SUPPORT_RESAMPLE(0, 0, 0, 0, 0, 0, 0, 1), // 8 input channels
};
ReSampleContext *av_audio_resample_init(int output_channels, int input_channels,
int output_rate, int input_rate,
enum AVSampleFormat sample_fmt_out,
enum AVSampleFormat sample_fmt_in,
int filter_length, int log2_phase_count,
int linear, double cutoff)
{
ReSampleContext *s;
if (input_channels > MAX_CHANNELS) {
av_log(NULL, AV_LOG_ERROR,
"Resampling with input channels greater than %d is unsupported.\n",
MAX_CHANNELS);
return NULL;
}
if (!(supported_resampling[input_channels-1] & (1<<(output_channels-1)))) {
int i;
av_log(NULL, AV_LOG_ERROR, "Unsupported audio resampling. Allowed "
"output channels for %d input channel%s", input_channels,
input_channels > 1 ? "s:" : ":");
for (i = 0; i < MAX_CHANNELS; i++)
if (supported_resampling[input_channels-1] & (1<<i))
av_log(NULL, AV_LOG_ERROR, " %d", i + 1);
av_log(NULL, AV_LOG_ERROR, "\n");
return NULL;
}
s = av_mallocz(sizeof(ReSampleContext));
if (!s) {
av_log(NULL, AV_LOG_ERROR, "Can't allocate memory for resample context.\n");
return NULL;
}
s->ratio = (float)output_rate / (float)input_rate;
s->input_channels = input_channels;
s->output_channels = output_channels;
s->filter_channels = s->input_channels;
if (s->output_channels < s->filter_channels)
s->filter_channels = s->output_channels;
s->sample_fmt[0] = sample_fmt_in;
s->sample_fmt[1] = sample_fmt_out;
s->sample_size[0] = av_get_bytes_per_sample(s->sample_fmt[0]);
s->sample_size[1] = av_get_bytes_per_sample(s->sample_fmt[1]);
if (s->sample_fmt[0] != AV_SAMPLE_FMT_S16) {
if (!(s->convert_ctx[0] = av_audio_convert_alloc(AV_SAMPLE_FMT_S16, 1,
s->sample_fmt[0], 1, NULL, 0))) {
av_log(s, AV_LOG_ERROR,
"Cannot convert %s sample format to s16 sample format\n",
av_get_sample_fmt_name(s->sample_fmt[0]));
av_free(s);
return NULL;
}
}
if (s->sample_fmt[1] != AV_SAMPLE_FMT_S16) {
if (!(s->convert_ctx[1] = av_audio_convert_alloc(s->sample_fmt[1], 1,
AV_SAMPLE_FMT_S16, 1, NULL, 0))) {
av_log(s, AV_LOG_ERROR,
"Cannot convert s16 sample format to %s sample format\n",
av_get_sample_fmt_name(s->sample_fmt[1]));
av_audio_convert_free(s->convert_ctx[0]);
av_free(s);
return NULL;
}
}
s->resample_context = av_resample_init(output_rate, input_rate,
filter_length, log2_phase_count,
linear, cutoff);
*(const AVClass**)s->resample_context = &audioresample_context_class;
return s;
}
/* resample audio. 'nb_samples' is the number of input samples */
/* XXX: optimize it ! */
int audio_resample(ReSampleContext *s, short *output, short *input, int nb_samples)
{
int i, nb_samples1;
short *bufin[MAX_CHANNELS];
short *bufout[MAX_CHANNELS];
short *buftmp2[MAX_CHANNELS], *buftmp3[MAX_CHANNELS];
short *output_bak = NULL;
int lenout;
if (s->sample_fmt[0] != AV_SAMPLE_FMT_S16) {
int istride[1] = { s->sample_size[0] };
int ostride[1] = { 2 };
const void *ibuf[1] = { input };
void *obuf[1];
unsigned input_size = nb_samples * s->input_channels * 2;
if (!s->buffer_size[0] || s->buffer_size[0] < input_size) {
av_free(s->buffer[0]);
s->buffer_size[0] = input_size;
s->buffer[0] = av_malloc(s->buffer_size[0]);
if (!s->buffer[0]) {
av_log(s->resample_context, AV_LOG_ERROR, "Could not allocate buffer\n");
return 0;
}
}
obuf[0] = s->buffer[0];
if (av_audio_convert(s->convert_ctx[0], obuf, ostride,
ibuf, istride, nb_samples * s->input_channels) < 0) {
av_log(s->resample_context, AV_LOG_ERROR,
"Audio sample format conversion failed\n");
return 0;
}
input = s->buffer[0];
}
lenout= 2*s->output_channels*nb_samples * s->ratio + 16;
if (s->sample_fmt[1] != AV_SAMPLE_FMT_S16) {
int out_size = lenout * av_get_bytes_per_sample(s->sample_fmt[1]) *
s->output_channels;
output_bak = output;
if (!s->buffer_size[1] || s->buffer_size[1] < out_size) {
av_free(s->buffer[1]);
s->buffer_size[1] = out_size;
s->buffer[1] = av_malloc(s->buffer_size[1]);
if (!s->buffer[1]) {
av_log(s->resample_context, AV_LOG_ERROR, "Could not allocate buffer\n");
return 0;
}
}
output = s->buffer[1];
}
/* XXX: move those malloc to resample init code */
for (i = 0; i < s->filter_channels; i++) {
bufin[i] = av_malloc_array((nb_samples + s->temp_len), sizeof(short));
bufout[i] = av_malloc_array(lenout, sizeof(short));
if (!bufin[i] || !bufout[i]) {
av_log(s->resample_context, AV_LOG_ERROR, "Could not allocate buffer\n");
nb_samples1 = 0;
goto fail;
}
memcpy(bufin[i], s->temp[i], s->temp_len * sizeof(short));
buftmp2[i] = bufin[i] + s->temp_len;
}
if (s->input_channels == 2 && s->output_channels == 1) {
buftmp3[0] = output;
stereo_to_mono(buftmp2[0], input, nb_samples);
} else if (s->output_channels >= 2 && s->input_channels == 1) {
buftmp3[0] = bufout[0];
memcpy(buftmp2[0], input, nb_samples * sizeof(short));
} else if (s->input_channels == 6 && s->output_channels ==2) {
buftmp3[0] = bufout[0];
buftmp3[1] = bufout[1];
surround_to_stereo(buftmp2, input, s->input_channels, nb_samples);
} else if (s->output_channels >= s->input_channels && s->input_channels >= 2) {
for (i = 0; i < s->input_channels; i++) {
buftmp3[i] = bufout[i];
}
deinterleave(buftmp2, input, s->input_channels, nb_samples);
} else {
buftmp3[0] = output;
memcpy(buftmp2[0], input, nb_samples * sizeof(short));
}
nb_samples += s->temp_len;
/* resample each channel */
nb_samples1 = 0; /* avoid warning */
for (i = 0; i < s->filter_channels; i++) {
int consumed;
int is_last = i + 1 == s->filter_channels;
nb_samples1 = av_resample(s->resample_context, buftmp3[i], bufin[i],
&consumed, nb_samples, lenout, is_last);
s->temp_len = nb_samples - consumed;
s->temp[i] = av_realloc_array(s->temp[i], s->temp_len, sizeof(short));
memcpy(s->temp[i], bufin[i] + consumed, s->temp_len * sizeof(short));
}
if (s->output_channels == 2 && s->input_channels == 1) {
mono_to_stereo(output, buftmp3[0], nb_samples1);
} else if (s->output_channels == 6 && s->input_channels == 2) {
ac3_5p1_mux(output, buftmp3[0], buftmp3[1], nb_samples1);
} else if ((s->output_channels == s->input_channels && s->input_channels >= 2) ||
(s->output_channels == 2 && s->input_channels == 6)) {
interleave(output, buftmp3, s->output_channels, nb_samples1);
}
if (s->sample_fmt[1] != AV_SAMPLE_FMT_S16) {
int istride[1] = { 2 };
int ostride[1] = { s->sample_size[1] };
const void *ibuf[1] = { output };
void *obuf[1] = { output_bak };
if (av_audio_convert(s->convert_ctx[1], obuf, ostride,
ibuf, istride, nb_samples1 * s->output_channels) < 0) {
av_log(s->resample_context, AV_LOG_ERROR,
"Audio sample format conversion failed\n");
return 0;
}
}
fail:
for (i = 0; i < s->filter_channels; i++) {
av_free(bufin[i]);
av_free(bufout[i]);
}
return nb_samples1;
}
void audio_resample_close(ReSampleContext *s)
{
int i;
av_resample_close(s->resample_context);
for (i = 0; i < s->filter_channels; i++)
av_freep(&s->temp[i]);
av_freep(&s->buffer[0]);
av_freep(&s->buffer[1]);
av_audio_convert_free(s->convert_ctx[0]);
av_audio_convert_free(s->convert_ctx[1]);
av_free(s);
}
FF_ENABLE_DEPRECATION_WARNINGS
#endif