summaryrefslogtreecommitdiff
path: root/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/libsimplewebm/OpusVorbisDecoder.cpp')
-rw-r--r--thirdparty/libsimplewebm/OpusVorbisDecoder.cpp224
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);
+}