From ee7ff20e69498ebd53dd9717a0f984188341a75e Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 7 May 2010 10:35:13 -0700 Subject: A new OggExtractor/VorbisDecoder combo to support approximate seeking. Change-Id: Id5d0c1c8b1adc62896bb5ed951f7b5cfda811e95 related-to-bug: 2654400 --- media/libstagefright/codecs/vorbis/Android.mk | 4 + media/libstagefright/codecs/vorbis/dec/Android.mk | 13 ++ .../codecs/vorbis/dec/VorbisDecoder.cpp | 246 +++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 media/libstagefright/codecs/vorbis/Android.mk create mode 100644 media/libstagefright/codecs/vorbis/dec/Android.mk create mode 100644 media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp (limited to 'media/libstagefright/codecs/vorbis') diff --git a/media/libstagefright/codecs/vorbis/Android.mk b/media/libstagefright/codecs/vorbis/Android.mk new file mode 100644 index 0000000..2e43120 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/Android.mk @@ -0,0 +1,4 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk new file mode 100644 index 0000000..5c768c8 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + VorbisDecoder.cpp \ + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + external/tremolo + +LOCAL_MODULE := libstagefright_vorbisdec + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp new file mode 100644 index 0000000..5485f25 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VorbisDecoder.h" + +#include +#include +#include +#include +#include + +extern "C" { + #include + + int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); +} + +namespace android { + +VorbisDecoder::VorbisDecoder(const sp &source) + : mSource(source), + mStarted(false), + mBufferGroup(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mState(NULL), + mVi(NULL) { + sp srcFormat = mSource->getFormat(); + CHECK(srcFormat->findInt32(kKeyChannelCount, &mNumChannels)); + CHECK(srcFormat->findInt32(kKeySampleRate, &mSampleRate)); +} + +VorbisDecoder::~VorbisDecoder() { + if (mStarted) { + stop(); + } +} + +static void makeBitReader( + const void *data, size_t size, + ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) { + buf->data = (uint8_t *)data; + buf->size = size; + buf->refcount = 1; + buf->ptr.owner = NULL; + + ref->buffer = buf; + ref->begin = 0; + ref->length = size; + ref->next = NULL; + + oggpack_readinit(bits, ref); +} + +status_t VorbisDecoder::start(MetaData *params) { + CHECK(!mStarted); + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer( + new MediaBuffer(kMaxNumSamplesPerBuffer * sizeof(int16_t))); + + mSource->start(); + + sp meta = mSource->getFormat(); + + mVi = new vorbis_info; + vorbis_info_init(mVi); + + /////////////////////////////////////////////////////////////////////////// + + uint32_t type; + const void *data; + size_t size; + CHECK(meta->findData(kKeyVorbisInfo, &type, &data, &size)); + + ogg_buffer buf; + ogg_reference ref; + oggpack_buffer bits; + makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits); + CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits)); + + /////////////////////////////////////////////////////////////////////////// + + CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size)); + + makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits); + CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits)); + + /////////////////////////////////////////////////////////////////////////// + + mState = new vorbis_dsp_state; + CHECK_EQ(0, vorbis_dsp_init(mState, mVi)); + + mAnchorTimeUs = 0; + mNumFramesOutput = 0; + mStarted = true; + + return OK; +} + +status_t VorbisDecoder::stop() { + CHECK(mStarted); + + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + + delete mBufferGroup; + mBufferGroup = NULL; + + mSource->stop(); + + mStarted = false; + + return OK; +} + +sp VorbisDecoder::getFormat() { + sp srcFormat = mSource->getFormat(); + + sp meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + meta->setInt32(kKeyChannelCount, mNumChannels); + meta->setInt32(kKeySampleRate, mSampleRate); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + meta->setInt64(kKeyDuration, durationUs); + } + + meta->setCString(kKeyDecoderComponent, "VorbisDecoder"); + + return meta; +} + +int VorbisDecoder::decodePacket(MediaBuffer *packet, MediaBuffer *out) { + ogg_buffer buf; + buf.data = (uint8_t *)packet->data() + packet->range_offset(); + buf.size = packet->range_length(); + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = packet->range_length(); + ref.next = NULL; + + ogg_packet pack; + pack.packet = &ref; + pack.bytes = packet->range_length(); + pack.b_o_s = 0; + pack.e_o_s = 0; + pack.granulepos = 0; + pack.packetno = 0; + + int err = vorbis_dsp_synthesis(mState, &pack, 1); + if (err != 0) { + LOGW("vorbis_dsp_synthesis returned %d", err); + return 0; + } + + int numFrames = vorbis_dsp_pcmout( + mState, (int16_t *)out->data(), kMaxNumSamplesPerBuffer); + + if (numFrames < 0) { + LOGE("vorbis_dsp_pcmout returned %d", numFrames); + return 0; + } + + out->set_range(0, numFrames * sizeof(int16_t) * mNumChannels); + + return numFrames; +} + +status_t VorbisDecoder::read( + MediaBuffer **out, const ReadOptions *options) { + status_t err; + + *out = NULL; + + int64_t seekTimeUs; + if (options && options->getSeekTo(&seekTimeUs)) { + CHECK(seekTimeUs >= 0); + + mNumFramesOutput = 0; + } else { + seekTimeUs = -1; + } + + MediaBuffer *inputBuffer; + err = mSource->read(&inputBuffer, options); + + if (err != OK) { + return ERROR_END_OF_STREAM; + } + + int64_t timeUs; + if (inputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { + mAnchorTimeUs = timeUs; + mNumFramesOutput = 0; + } else { + // We must have a new timestamp after seeking. + CHECK(seekTimeUs < 0); + } + + MediaBuffer *outputBuffer; + CHECK_EQ(mBufferGroup->acquire_buffer(&outputBuffer), OK); + + int numFrames = decodePacket(inputBuffer, outputBuffer); + + inputBuffer->release(); + inputBuffer = NULL; + + outputBuffer->meta_data()->setInt64( + kKeyTime, + mAnchorTimeUs + + (mNumFramesOutput * 1000000ll) / mSampleRate); + + mNumFramesOutput += numFrames; + + *out = outputBuffer; + + return OK; +} + +} // namespace android -- cgit v1.1