mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-29 22:36:43 +00:00
Add libswresample and libavresample support for sounds.
This commit is contained in:
parent
862c5fc8f6
commit
48a36442c6
3 changed files with 191 additions and 73 deletions
|
@ -53,68 +53,66 @@ extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// WARNING: avcodec versions up to 54.54.100 potentially crashes on Windows 64bit.
|
// WARNING: avcodec versions up to 54.54.100 potentially crashes on Windows 64bit.
|
||||||
}
|
|
||||||
|
|
||||||
// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See:
|
// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See:
|
||||||
// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d
|
// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d
|
||||||
// http://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872
|
// http://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872
|
||||||
extern "C" {
|
#ifdef HAVE_LIBSWRESAMPLE
|
||||||
#ifdef HAVE_LIBSWRESAMPLE
|
#include <libswresample/swresample.h>
|
||||||
#include <libswresample/swresample.h>
|
#else
|
||||||
#else
|
#include <libavresample/avresample.h>
|
||||||
/* nasty hack for systems without libswresample */
|
#include <libavutil/opt.h>
|
||||||
#include <libavresample/avresample.h>
|
/* FIXME: remove this section once libswresample is available on all platforms */
|
||||||
#include <libavutil/opt.h>
|
|
||||||
|
|
||||||
int swr_convert(
|
int swr_convert(
|
||||||
AVAudioResampleContext *avr,
|
AVAudioResampleContext *avr,
|
||||||
uint8_t** output,
|
uint8_t** output,
|
||||||
int out_samples,
|
int out_samples,
|
||||||
const uint8_t** input,
|
const uint8_t** input,
|
||||||
int in_samples)
|
int in_samples)
|
||||||
{
|
|
||||||
// FIXME: potential performance hit
|
|
||||||
int out_plane_size = 0;
|
|
||||||
int in_plane_size = 0;
|
|
||||||
return avresample_convert(avr, output, out_plane_size, out_samples,
|
|
||||||
(uint8_t **)input, in_plane_size, in_samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
AVAudioResampleContext * swr_alloc_set_opts(
|
|
||||||
AVAudioResampleContext *avr,
|
|
||||||
int64_t out_ch_layout,
|
|
||||||
AVSampleFormat out_fmt,
|
|
||||||
int out_rate,
|
|
||||||
int64_t in_ch_layout,
|
|
||||||
AVSampleFormat in_fmt,
|
|
||||||
int in_rate,
|
|
||||||
int o,
|
|
||||||
void* l)
|
|
||||||
{
|
|
||||||
avr = avresample_alloc_context();
|
|
||||||
if(!avr)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((av_opt_set_int(avr, "out_channel_layout", out_ch_layout, 0) < 0) ||
|
|
||||||
(av_opt_set_int(avr, "out_sample_fmt", out_fmt, 0) < 0) ||
|
|
||||||
(av_opt_set_int(avr, "out_sample_rate", out_rate, 0) < 0) ||
|
|
||||||
(av_opt_set_int(avr, "in_channel_layout", in_ch_layout, 0) < 0) ||
|
|
||||||
(av_opt_set_int(avr, "in_sample_fmt", in_fmt, 0) < 0) ||
|
|
||||||
(av_opt_set_int(avr, "in_sample_rate", in_rate, 0) < 0))
|
|
||||||
{
|
{
|
||||||
return 0;
|
// FIXME: potential performance hit
|
||||||
|
int out_plane_size = 0;
|
||||||
|
int in_plane_size = 0;
|
||||||
|
return avresample_convert(avr, output, out_plane_size, out_samples,
|
||||||
|
(uint8_t **)input, in_plane_size, in_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(avresample_open(avr) < 0)
|
AVAudioResampleContext * swr_alloc_set_opts(
|
||||||
return 0;
|
AVAudioResampleContext *avr,
|
||||||
else
|
int64_t out_ch_layout,
|
||||||
return avr;
|
AVSampleFormat out_fmt,
|
||||||
}
|
int out_rate,
|
||||||
|
int64_t in_ch_layout,
|
||||||
|
AVSampleFormat in_fmt,
|
||||||
|
int in_rate,
|
||||||
|
int o,
|
||||||
|
void* l)
|
||||||
|
{
|
||||||
|
avr = avresample_alloc_context();
|
||||||
|
if(!avr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
void swr_free(AVAudioResampleContext **avr) { avresample_free(avr); }
|
if ((av_opt_set_int(avr, "out_channel_layout", out_ch_layout, 0) < 0) ||
|
||||||
int swr_init(AVAudioResampleContext *avr) { return 1; }
|
(av_opt_set_int(avr, "out_sample_fmt", out_fmt, 0) < 0) ||
|
||||||
#define SwrContext AVAudioResampleContext
|
(av_opt_set_int(avr, "out_sample_rate", out_rate, 0) < 0) ||
|
||||||
#endif
|
(av_opt_set_int(avr, "in_channel_layout", in_ch_layout, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "in_sample_fmt", in_fmt, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "in_sample_rate", in_rate, 0) < 0))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(avresample_open(avr) < 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return avr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swr_free(AVAudioResampleContext **avr) { avresample_free(avr); }
|
||||||
|
int swr_init(AVAudioResampleContext *avr) { return 1; }
|
||||||
|
#define SwrContext AVAudioResampleContext
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
|
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
|
||||||
|
|
|
@ -5,6 +5,58 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#ifndef HAVE_LIBSWRESAMPLE
|
||||||
|
/* FIXME: remove this section once libswresample is available on all platforms */
|
||||||
|
|
||||||
|
int swr_convert(
|
||||||
|
AVAudioResampleContext *avr,
|
||||||
|
uint8_t** output,
|
||||||
|
int out_samples,
|
||||||
|
const uint8_t** input,
|
||||||
|
int in_samples)
|
||||||
|
{
|
||||||
|
// FIXME: potential performance hit
|
||||||
|
int out_plane_size = 0;
|
||||||
|
int in_plane_size = 0;
|
||||||
|
return avresample_convert(avr, output, out_plane_size, out_samples,
|
||||||
|
(uint8_t **)input, in_plane_size, in_samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
AVAudioResampleContext * swr_alloc_set_opts(
|
||||||
|
AVAudioResampleContext *avr,
|
||||||
|
int64_t out_ch_layout,
|
||||||
|
AVSampleFormat out_fmt,
|
||||||
|
int out_rate,
|
||||||
|
int64_t in_ch_layout,
|
||||||
|
AVSampleFormat in_fmt,
|
||||||
|
int in_rate,
|
||||||
|
int o,
|
||||||
|
void* l)
|
||||||
|
{
|
||||||
|
avr = avresample_alloc_context();
|
||||||
|
if(!avr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((av_opt_set_int(avr, "out_channel_layout", out_ch_layout, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "out_sample_fmt", out_fmt, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "out_sample_rate", out_rate, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "in_channel_layout", in_ch_layout, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "in_sample_fmt", in_fmt, 0) < 0) ||
|
||||||
|
(av_opt_set_int(avr, "in_sample_rate", in_rate, 0) < 0))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(avresample_open(avr) < 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return avr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swr_free(AVAudioResampleContext **avr) { avresample_free(avr); }
|
||||||
|
int swr_init(AVAudioResampleContext *avr) { return 1; }
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -95,6 +147,29 @@ bool FFmpeg_Decoder::getAVAudioData()
|
||||||
memmove(mPacket.data, &mPacket.data[len], remaining);
|
memmove(mPacket.data, &mPacket.data[len], remaining);
|
||||||
av_shrink_packet(&mPacket, remaining);
|
av_shrink_packet(&mPacket, remaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mSwr)
|
||||||
|
{
|
||||||
|
if(!mDataBuf || mDataBufLen < mFrame->nb_samples)
|
||||||
|
{
|
||||||
|
av_freep(&mDataBuf);
|
||||||
|
if(av_samples_alloc(&mDataBuf, NULL, (*mStream)->codec->channels,
|
||||||
|
mFrame->nb_samples, mOutputSampleFormat, 0) < 0)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
mDataBufLen = mFrame->nb_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(swr_convert(mSwr, (uint8_t**)&mDataBuf, mFrame->nb_samples,
|
||||||
|
(const uint8_t**)mFrame->extended_data, mFrame->nb_samples) < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mFrameData = &mDataBuf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mFrameData = &mFrame->data[0];
|
||||||
|
|
||||||
} while(got_frame == 0 || mFrame->nb_samples == 0);
|
} while(got_frame == 0 || mFrame->nb_samples == 0);
|
||||||
mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate;
|
mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate;
|
||||||
|
|
||||||
|
@ -122,7 +197,7 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)
|
||||||
size_t rem = std::min<size_t>(length-dec, mFrameSize-mFramePos);
|
size_t rem = std::min<size_t>(length-dec, mFrameSize-mFramePos);
|
||||||
|
|
||||||
/* Copy the data to the app's buffer and increment */
|
/* Copy the data to the app's buffer and increment */
|
||||||
memcpy(data, mFrame->data[0]+mFramePos, rem);
|
memcpy(data, mFrameData[0]+mFramePos, rem);
|
||||||
data = (char*)data + rem;
|
data = (char*)data + rem;
|
||||||
dec += rem;
|
dec += rem;
|
||||||
mFramePos += rem;
|
mFramePos += rem;
|
||||||
|
@ -132,19 +207,6 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)
|
||||||
return dec;
|
return dec;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AVSampleFormat ffmpegNonPlanarSampleFormat (AVSampleFormat format)
|
|
||||||
{
|
|
||||||
switch (format)
|
|
||||||
{
|
|
||||||
case AV_SAMPLE_FMT_U8P: return AV_SAMPLE_FMT_U8;
|
|
||||||
case AV_SAMPLE_FMT_S16P: return AV_SAMPLE_FMT_S16;
|
|
||||||
case AV_SAMPLE_FMT_S32P: return AV_SAMPLE_FMT_S32;
|
|
||||||
case AV_SAMPLE_FMT_FLTP: return AV_SAMPLE_FMT_FLT;
|
|
||||||
case AV_SAMPLE_FMT_DBLP: return AV_SAMPLE_FMT_DBL;
|
|
||||||
default:return format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FFmpeg_Decoder::open(const std::string &fname)
|
void FFmpeg_Decoder::open(const std::string &fname)
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
|
@ -191,7 +253,7 @@ void FFmpeg_Decoder::open(const std::string &fname)
|
||||||
if(!mStream)
|
if(!mStream)
|
||||||
fail("No audio streams in "+fname);
|
fail("No audio streams in "+fname);
|
||||||
|
|
||||||
(*mStream)->codec->request_sample_fmt = ffmpegNonPlanarSampleFormat ((*mStream)->codec->sample_fmt);
|
(*mStream)->codec->request_sample_fmt = (*mStream)->codec->sample_fmt;
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id);
|
AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id);
|
||||||
if(!codec)
|
if(!codec)
|
||||||
|
@ -203,7 +265,7 @@ void FFmpeg_Decoder::open(const std::string &fname)
|
||||||
if(avcodec_open2((*mStream)->codec, codec, NULL) < 0)
|
if(avcodec_open2((*mStream)->codec, codec, NULL) < 0)
|
||||||
fail("Failed to open audio codec " + std::string(codec->long_name));
|
fail("Failed to open audio codec " + std::string(codec->long_name));
|
||||||
|
|
||||||
mFrame = avcodec_alloc_frame();
|
mFrame = av_frame_alloc();
|
||||||
}
|
}
|
||||||
catch(std::exception&)
|
catch(std::exception&)
|
||||||
{
|
{
|
||||||
|
@ -228,6 +290,8 @@ void FFmpeg_Decoder::close()
|
||||||
|
|
||||||
av_free_packet(&mPacket);
|
av_free_packet(&mPacket);
|
||||||
av_freep(&mFrame);
|
av_freep(&mFrame);
|
||||||
|
swr_free(&mSwr);
|
||||||
|
av_freep(&mDataBuf);
|
||||||
|
|
||||||
if(mFormatCtx)
|
if(mFormatCtx)
|
||||||
{
|
{
|
||||||
|
@ -268,6 +332,12 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
|
||||||
*type = SampleType_Int16;
|
*type = SampleType_Int16;
|
||||||
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
|
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
|
||||||
*type = SampleType_Float32;
|
*type = SampleType_Float32;
|
||||||
|
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
|
||||||
|
*type = SampleType_UInt8;
|
||||||
|
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
|
||||||
|
*type = SampleType_Int16;
|
||||||
|
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
|
||||||
|
*type = SampleType_Float32;
|
||||||
else
|
else
|
||||||
fail(std::string("Unsupported sample format: ")+
|
fail(std::string("Unsupported sample format: ")+
|
||||||
av_get_sample_fmt_name((*mStream)->codec->sample_fmt));
|
av_get_sample_fmt_name((*mStream)->codec->sample_fmt));
|
||||||
|
@ -305,6 +375,30 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
|
||||||
}
|
}
|
||||||
|
|
||||||
*samplerate = (*mStream)->codec->sample_rate;
|
*samplerate = (*mStream)->codec->sample_rate;
|
||||||
|
|
||||||
|
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
|
||||||
|
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
|
||||||
|
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
|
||||||
|
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
|
||||||
|
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
|
||||||
|
mOutputSampleFormat = AV_SAMPLE_FMT_FLT;
|
||||||
|
|
||||||
|
if(mOutputSampleFormat != AV_SAMPLE_FMT_NONE)
|
||||||
|
{
|
||||||
|
mSwr = swr_alloc_set_opts(mSwr, // SwrContext
|
||||||
|
(*mStream)->codec->channel_layout, // output ch layout
|
||||||
|
mOutputSampleFormat, // output sample format
|
||||||
|
(*mStream)->codec->sample_rate, // output sample rate
|
||||||
|
(*mStream)->codec->channel_layout, // input ch layout
|
||||||
|
(*mStream)->codec->sample_fmt, // input sample format
|
||||||
|
(*mStream)->codec->sample_rate, // input sample rate
|
||||||
|
0, // logging level offset
|
||||||
|
NULL); // log context
|
||||||
|
if(!mSwr)
|
||||||
|
fail(std::string("Couldn't allocate SwrContext"));
|
||||||
|
if(swr_init(mSwr) < 0)
|
||||||
|
fail(std::string("Couldn't initialize SwrContext"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
|
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
|
||||||
|
@ -323,7 +417,7 @@ void FFmpeg_Decoder::readAll(std::vector<char> &output)
|
||||||
{
|
{
|
||||||
size_t got = mFrame->nb_samples * (*mStream)->codec->channels *
|
size_t got = mFrame->nb_samples * (*mStream)->codec->channels *
|
||||||
av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
|
av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
|
||||||
const char *inbuf = reinterpret_cast<char*>(mFrame->data[0]);
|
const char *inbuf = reinterpret_cast<char*>(mFrameData[0]);
|
||||||
output.insert(output.end(), inbuf, inbuf+got);
|
output.insert(output.end(), inbuf, inbuf+got);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,6 +446,11 @@ FFmpeg_Decoder::FFmpeg_Decoder()
|
||||||
, mFrameSize(0)
|
, mFrameSize(0)
|
||||||
, mFramePos(0)
|
, mFramePos(0)
|
||||||
, mNextPts(0.0)
|
, mNextPts(0.0)
|
||||||
|
, mSwr(0)
|
||||||
|
, mOutputSampleFormat(AV_SAMPLE_FMT_NONE)
|
||||||
|
, mDataBuf(NULL)
|
||||||
|
, mFrameData(NULL)
|
||||||
|
, mDataBufLen(0)
|
||||||
{
|
{
|
||||||
memset(&mPacket, 0, sizeof(mPacket));
|
memset(&mPacket, 0, sizeof(mPacket));
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,21 @@ extern "C"
|
||||||
LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO)
|
LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO)
|
||||||
#include <libavutil/channel_layout.h>
|
#include <libavutil/channel_layout.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
|
||||||
|
#define av_frame_alloc avcodec_alloc_frame
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See:
|
||||||
|
// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d
|
||||||
|
// http://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872
|
||||||
|
#ifdef HAVE_LIBSWRESAMPLE
|
||||||
|
#include <libswresample/swresample.h>
|
||||||
|
#else
|
||||||
|
#include <libavresample/avresample.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
#define SwrContext AVAudioResampleContext
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -40,6 +55,12 @@ namespace MWSound
|
||||||
|
|
||||||
double mNextPts;
|
double mNextPts;
|
||||||
|
|
||||||
|
SwrContext *mSwr;
|
||||||
|
enum AVSampleFormat mOutputSampleFormat;
|
||||||
|
uint8_t *mDataBuf;
|
||||||
|
uint8_t **mFrameData;
|
||||||
|
int mDataBufLen;
|
||||||
|
|
||||||
bool getNextPacket();
|
bool getNextPacket();
|
||||||
|
|
||||||
Ogre::DataStreamPtr mDataStream;
|
Ogre::DataStreamPtr mDataStream;
|
||||||
|
|
Loading…
Reference in a new issue