1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-30 08:57:00 +02:00

Merge pull request #4703 from IvanSavenko/swresample

Use swresample (part of FFmpeg) to de-planarize audio into format that can be consumed by SDL
This commit is contained in:
Ivan Savenko 2024-10-06 16:03:15 +03:00 committed by GitHub
commit ac5efaaf75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 23 deletions

View File

@ -486,11 +486,7 @@ if(NOT FORCE_BUNDLED_MINIZIP)
endif()
if (ENABLE_CLIENT)
set(FFMPEG_COMPONENTS avutil swscale avformat avcodec)
if(APPLE_IOS AND NOT USING_CONAN)
list(APPEND FFMPEG_COMPONENTS swresample)
endif()
find_package(ffmpeg COMPONENTS ${FFMPEG_COMPONENTS})
find_package(ffmpeg COMPONENTS avutil swscale avformat avcodec swresample)
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)

View File

@ -33,7 +33,9 @@ extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}
// Define a set of functions to read data
@ -501,8 +503,35 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
int numChannels = codecpar->ch_layout.nb_channels;
#endif
samples.reserve(44100 * 5); // arbitrary 5-second buffer
samples.reserve(44100 * 5); // arbitrary 5-second buffer to reduce reallocations
if (formatProperties.isPlanar && numChannels > 1)
{
// Format is 'planar', which is not supported by wav / SDL
// Use swresample part of ffmpeg to deplanarize audio into format supported by wav / SDL
auto sourceFormat = static_cast<AVSampleFormat>(codecpar->format);
auto targetFormat = av_get_alt_sample_fmt(sourceFormat, false);
SwrContext * swr_ctx = swr_alloc();
#if (LIBAVUTIL_VERSION_MAJOR < 58)
av_opt_set_channel_layout(swr_ctx, "in_chlayout", codecpar->channel_layout, 0);
av_opt_set_channel_layout(swr_ctx, "out_chlayout", codecpar->channel_layout, 0);
#else
av_opt_set_chlayout(swr_ctx, "in_chlayout", &codecpar->ch_layout, 0);
av_opt_set_chlayout(swr_ctx, "out_chlayout", &codecpar->ch_layout, 0);
#endif
av_opt_set_int(swr_ctx, "in_sample_rate", codecpar->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", sourceFormat, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", codecpar->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", targetFormat, 0);
int initResult = swr_init(swr_ctx);
if (initResult < 0)
throwFFmpegError(initResult);
std::vector<uint8_t> frameSamplesBuffer;
for (;;)
{
decodeNextFrame();
@ -511,22 +540,34 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
if (!frame)
break;
int samplesToRead = frame->nb_samples * numChannels;
int bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
size_t samplesToRead = frame->nb_samples * numChannels;
size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
frameSamplesBuffer.resize(std::max(frameSamplesBuffer.size(), bytesToRead));
uint8_t * frameSamplesPtr = frameSamplesBuffer.data();
if (formatProperties.isPlanar && numChannels > 1)
{
// Workaround for lack of resampler
// Currently, ffmpeg on conan systems is built without sws resampler
// Because of that, and because wav format does not supports 'planar' formats from ffmpeg
// we need to de-planarize it and convert to "normal" (non-planar / interleaved) stream
samples.reserve(samples.size() + bytesToRead);
for (int sm = 0; sm < frame->nb_samples; ++sm)
for (int ch = 0; ch < numChannels; ++ch)
samples.insert(samples.end(), frame->data[ch] + sm * formatProperties.sampleSizeBytes, frame->data[ch] + (sm+1) * formatProperties.sampleSizeBytes );
int result = swr_convert(swr_ctx, &frameSamplesPtr, frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
if (result < 0)
throwFFmpegError(result);
size_t samplesToCopy = result * numChannels;
size_t bytesToCopy = samplesToCopy * formatProperties.sampleSizeBytes;
samples.insert(samples.end(), frameSamplesBuffer.begin(), frameSamplesBuffer.begin() + bytesToCopy);
}
swr_free(&swr_ctx);
}
else
{
for (;;)
{
decodeNextFrame();
const AVFrame * frame = getCurrentFrame();
if (!frame)
break;
size_t samplesToRead = frame->nb_samples * numChannels;
size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
samples.insert(samples.end(), frame->data[0], frame->data[0] + bytesToRead);
}
}