Don't throw exceptions from the audio decoder

pull/359/head
Chris Robinson 7 years ago
parent d2b3f902c7
commit aecf74e7bb

@ -3,7 +3,7 @@
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <sstream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -11,11 +11,6 @@
namespace MWSound namespace MWSound
{ {
void FFmpeg_Decoder::fail(const std::string &msg)
{
throw std::runtime_error("FFmpeg exception: "+msg);
}
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
{ {
try try
@ -33,7 +28,8 @@ int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
int FFmpeg_Decoder::writePacket(void *, uint8_t *, int) int FFmpeg_Decoder::writePacket(void *, uint8_t *, int)
{ {
throw std::runtime_error("can't write to read-only stream"); std::cerr<< "can't write to read-only stream" <<std::endl;
return -1;
} }
int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
@ -180,40 +176,40 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)
return dec; return dec;
} }
void FFmpeg_Decoder::open(const std::string &fname) bool FFmpeg_Decoder::open(const std::string &fname)
{ {
close(); close();
mDataStream = mResourceMgr->get(fname); try
{
mDataStream = mResourceMgr->get(fname);
if((mFormatCtx=avformat_alloc_context()) == NULL) if((mFormatCtx=avformat_alloc_context()) == NULL)
fail("Failed to allocate context"); throw std::runtime_error("Failed to allocate context");
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
{
// "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
{ {
if (mFormatCtx->pb != NULL) // "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
{ {
if (mFormatCtx->pb->buffer != NULL) if (mFormatCtx->pb != NULL)
{ {
av_free(mFormatCtx->pb->buffer); if (mFormatCtx->pb->buffer != NULL)
mFormatCtx->pb->buffer = NULL; {
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
} }
av_free(mFormatCtx->pb); avformat_free_context(mFormatCtx);
mFormatCtx->pb = NULL;
} }
avformat_free_context(mFormatCtx); mFormatCtx = NULL;
throw std::runtime_error("Failed to allocate input stream");
} }
mFormatCtx = NULL;
fail("Failed to allocate input stream");
}
try
{
if(avformat_find_stream_info(mFormatCtx, NULL) < 0) if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
fail("Failed to find stream info in "+fname); throw std::runtime_error("Failed to find stream info in "+fname);
for(size_t j = 0;j < mFormatCtx->nb_streams;j++) for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
{ {
@ -224,23 +220,45 @@ void FFmpeg_Decoder::open(const std::string &fname)
} }
} }
if(!mStream) if(!mStream)
fail("No audio streams in "+fname); throw std::runtime_error("No audio streams in "+fname);
(*mStream)->codec->request_sample_fmt = (*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)
{ {
std::stringstream ss("No codec found for id "); std::string ss = "No codec found for id " +
ss << (*mStream)->codec->codec_id; std::to_string((*mStream)->codec->codec_id);
fail(ss.str()); throw std::runtime_error(ss);
} }
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)); throw std::runtime_error(std::string("Failed to open audio codec ") +
codec->long_name);
mFrame = av_frame_alloc(); mFrame = av_frame_alloc();
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT ||
(*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support
else 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
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
mOutputChannelLayout = (*mStream)->codec->channel_layout;
if(mOutputChannelLayout == 0)
mOutputChannelLayout = av_get_default_channel_layout((*mStream)->codec->channels);
return true;
} }
catch(std::exception&) catch(std::exception &e)
{
std::cerr<< "Could not open audio file: "<<e.what() <<std::endl;
}
if(mFormatCtx)
{ {
if(mStream) if(mStream)
avcodec_close((*mStream)->codec); avcodec_close((*mStream)->codec);
@ -248,15 +266,15 @@ void FFmpeg_Decoder::open(const std::string &fname)
if (mFormatCtx->pb->buffer != NULL) if (mFormatCtx->pb->buffer != NULL)
{ {
av_free(mFormatCtx->pb->buffer); av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL; mFormatCtx->pb->buffer = NULL;
} }
av_free(mFormatCtx->pb); av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL; mFormatCtx->pb = NULL;
avformat_close_input(&mFormatCtx); avformat_close_input(&mFormatCtx);
throw;
} }
return false;
} }
void FFmpeg_Decoder::close() void FFmpeg_Decoder::close()
@ -274,18 +292,18 @@ void FFmpeg_Decoder::close()
{ {
if (mFormatCtx->pb != NULL) if (mFormatCtx->pb != NULL)
{ {
// mFormatCtx->pb->buffer must be freed by hand, // mFormatCtx->pb->buffer must be freed by hand,
// if not, valgrind will show memleak, see: // if not, valgrind will show memleak, see:
// //
// https://trac.ffmpeg.org/ticket/1357 // https://trac.ffmpeg.org/ticket/1357
// //
if (mFormatCtx->pb->buffer != NULL) if (mFormatCtx->pb->buffer != NULL)
{ {
av_free(mFormatCtx->pb->buffer); av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL; mFormatCtx->pb->buffer = NULL;
} }
av_free(mFormatCtx->pb); av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL; mFormatCtx->pb = NULL;
} }
avformat_close_input(&mFormatCtx); avformat_close_input(&mFormatCtx);
} }
@ -298,19 +316,13 @@ std::string FFmpeg_Decoder::getName()
return mFormatCtx->filename; return mFormatCtx->filename;
} }
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) bool FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
if(!mStream) if(!mStream)
fail("No audio stream info"); {
std::cerr<< "No audio stream info" <<std::endl;
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT || (*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) return false;
mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support }
else 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
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
if(mOutputSampleFormat == AV_SAMPLE_FMT_U8) if(mOutputSampleFormat == AV_SAMPLE_FMT_U8)
*type = SampleType_UInt8; *type = SampleType_UInt8;
@ -319,19 +331,6 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT) else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT)
*type = SampleType_Float32; *type = SampleType_Float32;
int64_t ch_layout = (*mStream)->codec->channel_layout;
if(ch_layout == 0)
ch_layout = av_get_default_channel_layout((*mStream)->codec->channels);
mOutputChannelLayout = ch_layout;
if (ch_layout == AV_CH_LAYOUT_5POINT1 || ch_layout == AV_CH_LAYOUT_7POINT1
|| ch_layout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
else if (ch_layout != AV_CH_LAYOUT_MONO
&& ch_layout != AV_CH_LAYOUT_STEREO)
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
if(mOutputChannelLayout == AV_CH_LAYOUT_MONO) if(mOutputChannelLayout == AV_CH_LAYOUT_MONO)
*chans = ChannelConfig_Mono; *chans = ChannelConfig_Mono;
else if(mOutputChannelLayout == AV_CH_LAYOUT_STEREO) else if(mOutputChannelLayout == AV_CH_LAYOUT_STEREO)
@ -347,13 +346,27 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
char str[1024]; char str[1024];
av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels, av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels,
(*mStream)->codec->channel_layout); (*mStream)->codec->channel_layout);
fail(std::string("Unsupported channel layout: ")+str); std::cerr<< "Unsupported channel layout: "<<str <<std::endl;
if((*mStream)->codec->channels == 1)
{
mOutputChannelLayout = AV_CH_LAYOUT_MONO;
*chans = ChannelConfig_Mono;
}
else
{
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
*chans = ChannelConfig_Stereo;
}
} }
*samplerate = (*mStream)->codec->sample_rate; *samplerate = (*mStream)->codec->sample_rate;
int64_t ch_layout = (*mStream)->codec->channel_layout;
if(ch_layout == 0)
ch_layout = av_get_default_channel_layout((*mStream)->codec->channels);
if(mOutputSampleFormat != (*mStream)->codec->sample_fmt if(mOutputSampleFormat != (*mStream)->codec->sample_fmt ||
|| mOutputChannelLayout != ch_layout) mOutputChannelLayout != ch_layout)
{ {
mSwr = swr_alloc_set_opts(mSwr, // SwrContext mSwr = swr_alloc_set_opts(mSwr, // SwrContext
mOutputChannelLayout, // output ch layout mOutputChannelLayout, // output ch layout
@ -365,24 +378,37 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
0, // logging level offset 0, // logging level offset
NULL); // log context NULL); // log context
if(!mSwr) if(!mSwr)
fail(std::string("Couldn't allocate SwrContext")); {
std::cerr<< "Couldn't allocate SwrContext" <<std::endl;
return false;
}
if(swr_init(mSwr) < 0) if(swr_init(mSwr) < 0)
fail(std::string("Couldn't initialize SwrContext")); {
std::cerr<< "Couldn't initialize SwrContext" <<std::endl;
return false;
}
} }
return true;
} }
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
{ {
if(!mStream) if(!mStream)
fail("No audio stream"); {
std::cerr<< "No audio stream" <<std::endl;
return 0;
}
return readAVAudioData(buffer, bytes); return readAVAudioData(buffer, bytes);
} }
void FFmpeg_Decoder::readAll(std::vector<char> &output) void FFmpeg_Decoder::readAll(std::vector<char> &output)
{ {
if(!mStream) if(!mStream)
fail("No audio stream"); {
std::cerr<< "No audio stream" <<std::endl;
return;
}
while(getAVAudioData()) while(getAVAudioData())
{ {

@ -35,7 +35,7 @@ extern "C"
namespace MWSound namespace MWSound
{ {
class FFmpeg_Decoder : public Sound_Decoder class FFmpeg_Decoder final : public Sound_Decoder
{ {
AVFormatContext *mFormatCtx; AVFormatContext *mFormatCtx;
AVStream **mStream; AVStream **mStream;
@ -66,17 +66,15 @@ namespace MWSound
bool getAVAudioData(); bool getAVAudioData();
size_t readAVAudioData(void *data, size_t length); size_t readAVAudioData(void *data, size_t length);
virtual void open(const std::string &fname); bool open(const std::string &fname) override;
virtual void close(); void close() override;
virtual std::string getName(); std::string getName() override;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); bool getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
virtual size_t read(char *buffer, size_t bytes); size_t read(char *buffer, size_t bytes) override;
virtual void readAll(std::vector<char> &output); void readAll(std::vector<char> &output) override;
virtual size_t getSampleOffset(); size_t getSampleOffset() override;
void fail(const std::string &msg);
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs);

@ -1,5 +1,7 @@
#include "movieaudiofactory.hpp" #include "movieaudiofactory.hpp"
#include <iostream>
#include <extern/osg-ffmpeg-videoplayer/audiodecoder.hpp> #include <extern/osg-ffmpeg-videoplayer/audiodecoder.hpp>
#include <extern/osg-ffmpeg-videoplayer/videostate.hpp> #include <extern/osg-ffmpeg-videoplayer/videostate.hpp>
@ -13,7 +15,7 @@ namespace MWSound
{ {
class MovieAudioDecoder; class MovieAudioDecoder;
class MWSoundDecoderBridge : public Sound_Decoder class MWSoundDecoderBridge final : public Sound_Decoder
{ {
public: public:
MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder)
@ -25,12 +27,12 @@ namespace MWSound
private: private:
MWSound::MovieAudioDecoder* mDecoder; MWSound::MovieAudioDecoder* mDecoder;
virtual void open(const std::string &fname); bool open(const std::string &fname) override;
virtual void close(); void close() override;
virtual std::string getName(); std::string getName() override;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); bool getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
virtual size_t read(char *buffer, size_t bytes); size_t read(char *buffer, size_t bytes) override;
virtual size_t getSampleOffset(); size_t getSampleOffset() override;
}; };
class MovieAudioDecoder : public Video::MovieAudioDecoder class MovieAudioDecoder : public Video::MovieAudioDecoder
@ -96,9 +98,9 @@ namespace MWSound
}; };
void MWSoundDecoderBridge::open(const std::string &fname) bool MWSoundDecoderBridge::open(const std::string &fname)
{ {
throw std::runtime_error("unimplemented"); return false;
} }
void MWSoundDecoderBridge::close() {} void MWSoundDecoderBridge::close() {}
@ -107,7 +109,7 @@ namespace MWSound
return mDecoder->getStreamName(); return mDecoder->getStreamName();
} }
void MWSoundDecoderBridge::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) bool MWSoundDecoderBridge::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
*samplerate = mDecoder->getOutputSampleRate(); *samplerate = mDecoder->getOutputSampleRate();
@ -124,9 +126,8 @@ namespace MWSound
*chans = ChannelConfig_Quad; *chans = ChannelConfig_Quad;
else else
{ {
std::stringstream error; std::cerr<< "Unsupported channel layout: "<<outputChannelLayout <<std::endl;
error << "Unsupported channel layout: " << outputChannelLayout; return false;
throw std::runtime_error(error.str());
} }
AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat(); AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat();
@ -140,8 +141,11 @@ namespace MWSound
{ {
char str[1024]; char str[1024];
av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat); av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat);
throw std::runtime_error(std::string("Unsupported sample format: ") + str); std::cerr<< "Unsupported sample format: "<<str <<std::endl;
return false;
} }
return true;
} }
size_t MWSoundDecoderBridge::read(char *buffer, size_t bytes) size_t MWSoundDecoderBridge::read(char *buffer, size_t bytes)

@ -411,7 +411,8 @@ bool OpenAL_SoundStream::init(bool getLoudnessData)
ChannelConfig chans; ChannelConfig chans;
SampleType type; SampleType type;
mDecoder->getInfo(&mSampleRate, &chans, &type); if(!mDecoder->getInfo(&mSampleRate, &chans, &type))
return false;
mFormat = getALFormat(chans, type); mFormat = getALFormat(chans, type);
if(!mFormat) return false; if(!mFormat) return false;
@ -948,14 +949,18 @@ std::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname
DecoderPtr decoder = mManager.getDecoder(); DecoderPtr decoder = mManager.getDecoder();
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
if(decoder->mResourceMgr->exists(fname)) if(decoder->mResourceMgr->exists(fname))
decoder->open(fname); {
if(!decoder->open(fname))
return std::make_pair(nullptr, 0);
}
else else
{ {
std::string file = fname; std::string file = fname;
std::string::size_type pos = file.rfind('.'); std::string::size_type pos = file.rfind('.');
if(pos != std::string::npos) if(pos != std::string::npos)
file = file.substr(0, pos)+".mp3"; file = file.substr(0, pos)+".mp3";
decoder->open(file); if(!decoder->open(file))
return std::make_pair(nullptr, 0);
} }
std::vector<char> data; std::vector<char> data;
@ -964,7 +969,8 @@ std::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname
ALenum format; ALenum format;
int srate; int srate;
decoder->getInfo(&srate, &chans, &type); if(!decoder->getInfo(&srate, &chans, &type))
return std::make_pair(nullptr, 0);
format = getALFormat(chans, type); format = getALFormat(chans, type);
if(!format) return std::make_pair(nullptr, 0); if(!format) return std::make_pair(nullptr, 0);

@ -34,11 +34,11 @@ namespace MWSound
{ {
const VFS::Manager* mResourceMgr; const VFS::Manager* mResourceMgr;
virtual void open(const std::string &fname) = 0; virtual bool open(const std::string &fname) = 0;
virtual void close() = 0; virtual void close() = 0;
virtual std::string getName() = 0; virtual std::string getName() = 0;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; virtual bool getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;
virtual size_t read(char *buffer, size_t bytes) = 0; virtual size_t read(char *buffer, size_t bytes) = 0;
virtual void readAll(std::vector<char> &output); virtual void readAll(std::vector<char> &output);

Loading…
Cancel
Save