1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-31 22:45:35 +00:00

Only use one stream for the ffmpeg decoder

This commit is contained in:
Chris Robinson 2012-12-15 22:13:19 -08:00
parent a3bd3a40ca
commit 9d86890d9d
2 changed files with 80 additions and 92 deletions

View file

@ -72,39 +72,26 @@ int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
/* Used by getAV*Data to search for more compressed data, and buffer it in the /* Used by getAV*Data to search for more compressed data, and buffer it in the
* correct stream. It won't buffer data for streams that the app doesn't have a * correct stream. It won't buffer data for streams that the app doesn't have a
* handle for. */ * handle for. */
bool FFmpeg_Decoder::getNextPacket(int streamidx) bool FFmpeg_Decoder::getNextPacket()
{ {
PacketList *packet; if(!mStream.get())
return false;
packet = (PacketList*)av_malloc(sizeof(*packet)); PacketList *packet = (PacketList*)av_malloc(sizeof(*packet));
packet->next = NULL; packet->next = NULL;
next_packet:
while(av_read_frame(mFormatCtx, &packet->pkt) >= 0) while(av_read_frame(mFormatCtx, &packet->pkt) >= 0)
{ {
std::vector<MyStream*>::iterator iter = mStreams.begin(); /* Check if the packet belongs to this stream */
if(mStream->mStreamIdx == packet->pkt.stream_index)
/* Check each stream the user has a handle for, looking for the one
* this packet belongs to */
while(iter != mStreams.end())
{ {
if((*iter)->mStreamIdx == packet->pkt.stream_index) PacketList **last;
{
PacketList **last;
last = &(*iter)->mPackets; last = &mStream->mPackets;
while(*last != NULL) while(*last != NULL)
last = &(*last)->next; last = &(*last)->next;
*last = packet; *last = packet;
if((*iter)->mStreamIdx == streamidx) return true;
return true;
packet = (PacketList*)av_malloc(sizeof(*packet));
packet->next = NULL;
goto next_packet;
}
iter++;
} }
/* Free the packet and look for another */ /* Free the packet and look for another */
av_free_packet(&packet->pkt); av_free_packet(&packet->pkt);
@ -138,7 +125,7 @@ void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length)
mDecodedDataSize = 0; mDecodedDataSize = 0;
next_packet: next_packet:
if(!mPackets && !mParent->getNextPacket(mStreamIdx)) if(!mPackets && !mParent->getNextPacket())
return NULL; return NULL;
/* Decode some data, and check for errors */ /* Decode some data, and check for errors */
@ -173,8 +160,7 @@ next_packet:
/* Move the unread data to the front and clear the end bits */ /* Move the unread data to the front and clear the end bits */
int remaining = mPackets->pkt.size - len; int remaining = mPackets->pkt.size - len;
memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining); memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining);
memset(&mPackets->pkt.data[remaining], 0, mPackets->pkt.size - remaining); av_shrink_packet(&mPackets->pkt, remaining);
mPackets->pkt.size -= len;
} }
else else
{ {
@ -261,35 +247,38 @@ void FFmpeg_Decoder::open(const std::string &fname)
if(avformat_find_stream_info(mFormatCtx, NULL) < 0) if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
fail("Failed to find stream info in "+fname); fail("Failed to find stream info in "+fname);
int audio_idx = -1;
for(size_t j = 0;j < mFormatCtx->nb_streams;j++) for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
{ {
if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{ {
std::auto_ptr<MyStream> stream(new MyStream); audio_idx = j;
stream->mCodecCtx = mFormatCtx->streams[j]->codec;
stream->mStreamIdx = j;
stream->mPackets = NULL;
AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id);
if(!codec)
{
std::stringstream ss("No codec found for id ");
ss << stream->mCodecCtx->codec_id;
fail(ss.str());
}
if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name));
stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
stream->mDecodedDataSize = 0;
stream->mParent = this;
mStreams.push_back(stream.release());
break; break;
} }
} }
if(mStreams.empty()) if(audio_idx == -1)
fail("No audio streams in "+fname); fail("No audio streams in "+fname);
std::auto_ptr<MyStream> stream(new MyStream);
stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec;
stream->mStreamIdx = audio_idx;
stream->mPackets = NULL;
AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id);
if(!codec)
{
std::stringstream ss("No codec found for id ");
ss << stream->mCodecCtx->codec_id;
fail(ss.str());
}
if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name));
stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
stream->mDecodedDataSize = 0;
stream->mParent = this;
mStream = stream;
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -301,23 +290,19 @@ void FFmpeg_Decoder::open(const std::string &fname)
void FFmpeg_Decoder::close() void FFmpeg_Decoder::close()
{ {
while(!mStreams.empty()) if(mStream.get())
{ {
MyStream *stream = mStreams.front(); mStream->clearPackets();
avcodec_close(mStream->mCodecCtx);
stream->clearPackets(); av_free(mStream->mDecodedData);
avcodec_close(stream->mCodecCtx);
av_free(stream->mDecodedData);
delete stream;
mStreams.erase(mStreams.begin());
} }
mStream.reset();
if(mFormatCtx) if(mFormatCtx)
{ {
AVIOContext* context = mFormatCtx->pb; AVIOContext* context = mFormatCtx->pb;
av_free(context);
mFormatCtx->pb = NULL;
avformat_close_input(&mFormatCtx); avformat_close_input(&mFormatCtx);
av_free(context);
} }
mFormatCtx = NULL; mFormatCtx = NULL;
@ -331,81 +316,83 @@ std::string FFmpeg_Decoder::getName()
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
if(mStreams.empty()) if(!mStream.get())
fail("No audio stream info"); fail("No audio stream info");
MyStream *stream = mStreams[0]; if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8)
if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8)
*type = SampleType_UInt8; *type = SampleType_UInt8;
else if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) else if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16)
*type = SampleType_Int16; *type = SampleType_Int16;
else else
fail(std::string("Unsupported sample format: ")+ fail(std::string("Unsupported sample format: ")+
av_get_sample_fmt_name(stream->mCodecCtx->sample_fmt)); av_get_sample_fmt_name(mStream->mCodecCtx->sample_fmt));
if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
*chans = ChannelConfig_Mono; *chans = ChannelConfig_Mono;
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO)
*chans = ChannelConfig_Stereo; *chans = ChannelConfig_Stereo;
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD)
*chans = ChannelConfig_Quad; *chans = ChannelConfig_Quad;
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1)
*chans = ChannelConfig_5point1; *chans = ChannelConfig_5point1;
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1)
*chans = ChannelConfig_7point1; *chans = ChannelConfig_7point1;
else if(stream->mCodecCtx->channel_layout == 0) else if(mStream->mCodecCtx->channel_layout == 0)
{ {
/* Unknown channel layout. Try to guess. */ /* Unknown channel layout. Try to guess. */
if(stream->mCodecCtx->channels == 1) if(mStream->mCodecCtx->channels == 1)
*chans = ChannelConfig_Mono; *chans = ChannelConfig_Mono;
else if(stream->mCodecCtx->channels == 2) else if(mStream->mCodecCtx->channels == 2)
*chans = ChannelConfig_Stereo; *chans = ChannelConfig_Stereo;
else else
{ {
std::stringstream sstr("Unsupported raw channel count: "); std::stringstream sstr("Unsupported raw channel count: ");
sstr << stream->mCodecCtx->channels; sstr << mStream->mCodecCtx->channels;
fail(sstr.str()); fail(sstr.str());
} }
} }
else else
{ {
char str[1024]; char str[1024];
av_get_channel_layout_string(str, sizeof(str), stream->mCodecCtx->channels, av_get_channel_layout_string(str, sizeof(str), mStream->mCodecCtx->channels,
stream->mCodecCtx->channel_layout); mStream->mCodecCtx->channel_layout);
fail(std::string("Unsupported channel layout: ")+str); fail(std::string("Unsupported channel layout: ")+str);
} }
*samplerate = stream->mCodecCtx->sample_rate; *samplerate = mStream->mCodecCtx->sample_rate;
} }
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
{ {
if(mStreams.empty()) if(!mStream.get())
fail("No audio streams"); fail("No audio stream");
MyStream *stream = mStreams.front(); size_t got = mStream->readAVAudioData(buffer, bytes);
size_t got = stream->readAVAudioData(buffer, bytes); mSamplesRead += got / mStream->mCodecCtx->channels /
mSamplesRead += got / av_samples_get_buffer_size(NULL, stream->mCodecCtx->channels, 1, av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt);
stream->mCodecCtx->sample_fmt, 1);
return got; return got;
} }
void FFmpeg_Decoder::readAll(std::vector<char> &output) void FFmpeg_Decoder::readAll(std::vector<char> &output)
{ {
if(mStreams.empty()) if(!mStream.get())
fail("No audio streams"); fail("No audio stream");
MyStream *stream = mStreams.front();
char *inbuf; char *inbuf;
size_t got; size_t got;
while((inbuf=(char*)mStream->getAVAudioData(&got)) != NULL && got > 0)
while((inbuf=(char*)stream->getAVAudioData(&got)) != NULL && got > 0) {
output.insert(output.end(), inbuf, inbuf+got); output.insert(output.end(), inbuf, inbuf+got);
mSamplesRead += got / mStream->mCodecCtx->channels /
av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt);
}
} }
void FFmpeg_Decoder::rewind() void FFmpeg_Decoder::rewind()
{ {
av_seek_frame(mFormatCtx, -1, 0, 0); av_seek_frame(mFormatCtx, -1, 0, 0);
std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets)); if(mStream.get())
mStream->clearPackets();
mSamplesRead = 0; mSamplesRead = 0;
} }

View file

@ -21,13 +21,14 @@ namespace MWSound
{ {
class FFmpeg_Decoder : public Sound_Decoder class FFmpeg_Decoder : public Sound_Decoder
{ {
struct MyStream;
AVFormatContext *mFormatCtx; AVFormatContext *mFormatCtx;
struct MyStream; std::auto_ptr<MyStream> mStream;
std::vector<MyStream*> mStreams;
size_t mSamplesRead; size_t mSamplesRead;
bool getNextPacket(int streamidx); bool getNextPacket();
Ogre::DataStreamPtr mDataStream; Ogre::DataStreamPtr mDataStream;
static int readPacket(void *user_data, uint8_t *buf, int buf_size); static int readPacket(void *user_data, uint8_t *buf, int buf_size);