diff options
Diffstat (limited to 'thirdparty/libsimplewebm/OpusVorbisDecoder.cpp')
-rw-r--r-- | thirdparty/libsimplewebm/OpusVorbisDecoder.cpp | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp b/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp new file mode 100644 index 0000000000..d7869f599b --- /dev/null +++ b/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp @@ -0,0 +1,224 @@ +/* + MIT License + + Copyright (c) 2016 Błażej Szczygieł + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "OpusVorbisDecoder.hpp" + +#include <vorbis/codec.h> +#include <opus/opus.h> + +#include <string.h> + +struct VorbisDecoder +{ + vorbis_info info; + vorbis_dsp_state dspState; + vorbis_block block; + ogg_packet op; + + bool hasDSPState, hasBlock; +}; + +/**/ + +OpusVorbisDecoder::OpusVorbisDecoder(const WebMDemuxer &demuxer) : + m_vorbis(NULL), m_opus(NULL), + m_numSamples(0), + m_channels(demuxer.getChannels()) +{ + switch (demuxer.getAudioCodec()) + { + case WebMDemuxer::AUDIO_VORBIS: + if (openVorbis(demuxer)) + return; + break; + case WebMDemuxer::AUDIO_OPUS: + if (openOpus(demuxer)) + return; + break; + default: + return; + } + close(); +} +OpusVorbisDecoder::~OpusVorbisDecoder() +{ + close(); +} + +bool OpusVorbisDecoder::isOpen() const +{ + return (m_vorbis || m_opus); +} + +bool OpusVorbisDecoder::getPCMS16(WebMFrame &frame, short *buffer, int &numOutSamples) +{ + if (m_vorbis) + { + m_vorbis->op.packet = frame.buffer; + m_vorbis->op.bytes = frame.bufferSize; + + if (vorbis_synthesis(&m_vorbis->block, &m_vorbis->op)) + return false; + if (vorbis_synthesis_blockin(&m_vorbis->dspState, &m_vorbis->block)) + return false; + + const int maxSamples = getBufferSamples(); + int samplesCount, count = 0; + float **pcm; + while ((samplesCount = vorbis_synthesis_pcmout(&m_vorbis->dspState, &pcm))) + { + const int toConvert = samplesCount <= maxSamples ? samplesCount : maxSamples; + for (int c = 0; c < m_channels; ++c) + { + float *samples = pcm[c]; + for (int i = 0, j = c; i < toConvert; ++i, j += m_channels) + { + int sample = samples[i] * 32767.0f; + if (sample > 32767) + sample = 32767; + else if (sample < -32768) + sample = -32768; + buffer[count + j] = sample; + } + } + vorbis_synthesis_read(&m_vorbis->dspState, toConvert); + count += toConvert; + } + + numOutSamples = count; + return true; + } + else if (m_opus) + { + const int samples = opus_decode(m_opus, frame.buffer, frame.bufferSize, buffer, m_numSamples, 0); + if (samples >= 0) + { + numOutSamples = samples; + return true; + } + } + return false; +} + +bool OpusVorbisDecoder::openVorbis(const WebMDemuxer &demuxer) +{ + size_t extradataSize = 0; + const unsigned char *extradata = demuxer.getAudioExtradata(extradataSize); + + if (extradataSize < 3 || !extradata || extradata[0] != 2) + return false; + + size_t headerSize[3] = {0}; + size_t offset = 1; + + /* Calculate three headers sizes */ + for (int i = 0; i < 2; ++i) + { + for (;;) + { + if (offset >= extradataSize) + return false; + headerSize[i] += extradata[offset]; + if (extradata[offset++] < 0xFF) + break; + } + } + headerSize[2] = extradataSize - (headerSize[0] + headerSize[1] + offset); + + if (headerSize[0] + headerSize[1] + headerSize[2] + offset != extradataSize) + return false; + + ogg_packet op[3]; + memset(op, 0, sizeof op); + + op[0].packet = (unsigned char *)extradata + offset; + op[0].bytes = headerSize[0]; + op[0].b_o_s = 1; + + op[1].packet = (unsigned char *)extradata + offset + headerSize[0]; + op[1].bytes = headerSize[1]; + + op[2].packet = (unsigned char *)extradata + offset + headerSize[0] + headerSize[1]; + op[2].bytes = headerSize[2]; + + m_vorbis = new VorbisDecoder; + m_vorbis->hasDSPState = m_vorbis->hasBlock = false; + vorbis_info_init(&m_vorbis->info); + + /* Upload three Vorbis headers into libvorbis */ + vorbis_comment vc; + vorbis_comment_init(&vc); + for (int i = 0; i < 3; ++i) + { + if (vorbis_synthesis_headerin(&m_vorbis->info, &vc, &op[i])) + { + vorbis_comment_clear(&vc); + return false; + } + } + vorbis_comment_clear(&vc); + + if (vorbis_synthesis_init(&m_vorbis->dspState, &m_vorbis->info)) + return false; + m_vorbis->hasDSPState = true; + + if (m_vorbis->info.channels != m_channels || m_vorbis->info.rate != demuxer.getSampleRate()) + return false; + + if (vorbis_block_init(&m_vorbis->dspState, &m_vorbis->block)) + return false; + m_vorbis->hasBlock = true; + + memset(&m_vorbis->op, 0, sizeof m_vorbis->op); + + m_numSamples = 4096 / m_channels; + + return true; +} +bool OpusVorbisDecoder::openOpus(const WebMDemuxer &demuxer) +{ + int opusErr = 0; + m_opus = opus_decoder_create(demuxer.getSampleRate(), m_channels, &opusErr); + if (!opusErr) + { + m_numSamples = demuxer.getSampleRate() * 0.06 + 0.5; //Maximum frame size (for 60 ms frame) + return true; + } + return false; +} + +void OpusVorbisDecoder::close() +{ + if (m_vorbis) + { + if (m_vorbis->hasBlock) + vorbis_block_clear(&m_vorbis->block); + if (m_vorbis->hasDSPState) + vorbis_dsp_clear(&m_vorbis->dspState); + vorbis_info_clear(&m_vorbis->info); + delete m_vorbis; + } + if (m_opus) + opus_decoder_destroy(m_opus); +} |