summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/codecs/mp3dec/MP3Decoder.cpp')
-rw-r--r--media/libstagefright/codecs/mp3dec/MP3Decoder.cpp586
1 files changed, 586 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
new file mode 100644
index 0000000..c24aca0
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2009 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 "MP3Decoder.h"
+
+#include "include/pvmp3decoder_api.h"
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+// Everything must match except for
+// protection, bitrate, padding, private bits, mode extension,
+// copyright bit, original bit and emphasis.
+// Yes ... there are things that must indeed match...
+static const uint32_t kMask = 0xfffe0cc0;
+
+static bool get_mp3_frame_size(
+ uint32_t header, size_t *frame_size,
+ int *out_sampling_rate = NULL, int *out_channels = NULL,
+ int *out_bitrate = NULL) {
+ *frame_size = 0;
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = 0;
+ }
+
+ if (out_channels) {
+ *out_channels = 0;
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = 0;
+ }
+
+ if ((header & 0xffe00000) != 0xffe00000) {
+ return false;
+ }
+
+ unsigned version = (header >> 19) & 3;
+
+ if (version == 0x01) {
+ return false;
+ }
+
+ unsigned layer = (header >> 17) & 3;
+
+ if (layer == 0x00) {
+ return false;
+ }
+
+ unsigned protection = (header >> 16) & 1;
+
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+
+ if (bitrate_index == 0 || bitrate_index == 0x0f) {
+ // Disallow "free" bitrate.
+ return false;
+ }
+
+ unsigned sampling_rate_index = (header >> 10) & 3;
+
+ if (sampling_rate_index == 3) {
+ return false;
+ }
+
+ static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) {
+ // layer I
+
+ static const int kBitrateV1[] = {
+ 32, 64, 96, 128, 160, 192, 224, 256,
+ 288, 320, 352, 384, 416, 448
+ };
+
+ static const int kBitrateV2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 144, 160, 176, 192, 224, 256
+ };
+
+ int bitrate =
+ (version == 3 /* V1 */)
+ ? kBitrateV1[bitrate_index - 1]
+ : kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+ } else {
+ // layer II or III
+
+ static const int kBitrateV1L2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 160, 192, 224, 256, 320, 384
+ };
+
+ static const int kBitrateV1L3[] = {
+ 32, 40, 48, 56, 64, 80, 96, 112,
+ 128, 160, 192, 224, 256, 320
+ };
+
+ static const int kBitrateV2[] = {
+ 8, 16, 24, 32, 40, 48, 56, 64,
+ 80, 96, 112, 128, 144, 160
+ };
+
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate = (layer == 2 /* L2 */)
+ ? kBitrateV1L2[bitrate_index - 1]
+ : kBitrateV1L3[bitrate_index - 1];
+ } else {
+ // V2 (or 2.5)
+
+ bitrate = kBitrateV2[bitrate_index - 1];
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else {
+ // V2 or V2.5
+ *frame_size = 72000 * bitrate / sampling_rate + padding;
+ }
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static bool resync(
+ uint8_t *data, uint32_t size, uint32_t match_header, off_t *out_pos) {
+
+ bool valid = false;
+ off_t pos = 0;
+ *out_pos = 0;
+ do {
+ if (pos + 4 > size) {
+ // Don't scan forever.
+ ALOGV("no dice, no valid sequence of frames found.");
+ break;
+ }
+
+ uint32_t header = U32_AT(data + pos);
+
+ if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
+ ++pos;
+ continue;
+ }
+
+ ALOGV("found possible frame at %ld (header = 0x%08x)", pos, header);
+
+ // We found what looks like a valid frame,
+ valid = true;
+ *out_pos = pos;
+ } while (!valid);
+
+ return valid;
+}
+
+
+MP3Decoder::MP3Decoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mNumChannels(0),
+ mStarted(false),
+ mBufferGroup(NULL),
+ mConfig(new tPVMP3DecoderExternal),
+ mDecoderBuf(NULL),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mInputBuffer(NULL),
+ mPartialBuffer(NULL),
+ mFixedHeader(0) {
+ init();
+}
+
+void MP3Decoder::init() {
+ sp<MetaData> srcFormat = mSource->getFormat();
+
+ int32_t sampleRate;
+ CHECK(srcFormat->findInt32(kKeyChannelCount, &mNumChannels));
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
+
+ mMeta = new MetaData;
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ mMeta->setInt32(kKeyChannelCount, mNumChannels);
+ mMeta->setInt32(kKeySampleRate, sampleRate);
+
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ mMeta->setInt64(kKeyDuration, durationUs);
+ }
+
+ mMeta->setCString(kKeyDecoderComponent, "MP3Decoder");
+}
+
+MP3Decoder::~MP3Decoder() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+status_t MP3Decoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(new MediaBuffer(4608 * 2));
+
+ mConfig->equalizerType = flat;
+ mConfig->crcEnabled = false;
+
+ uint32_t memRequirements = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+
+ mSource->start();
+
+ mAnchorTimeUs = 0;
+ mNumFramesOutput = 0;
+ mStarted = true;
+
+ return OK;
+}
+
+status_t MP3Decoder::stop() {
+ CHECK(mStarted);
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+
+ delete mBufferGroup;
+ mBufferGroup = NULL;
+
+ mSource->stop();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> MP3Decoder::getFormat() {
+ return mMeta;
+}
+
+status_t MP3Decoder::updatePartialFrame() {
+ status_t err = OK;
+ if (mPartialBuffer == NULL) {
+ return err;
+ }
+
+ size_t frameSize = 0;
+ uint32_t partialBufLen = mPartialBuffer->range_length();
+ uint32_t inputBufLen = mInputBuffer->range_length();
+ uint8_t frameHeader[4];
+ uint8_t *frmHdr;
+ uint32_t header;
+
+
+ // Look at the frame size and complete the partial frame
+ // Also check if a vaild header is found after the partial frame
+ if (partialBufLen < 4) { // check if partial frame has the 4 bytes header
+ if (inputBufLen < (4 - partialBufLen)) {
+ // input buffer does not have the frame header bytes
+ // bail out TODO
+ ALOGE("MP3Decoder::updatePartialFrame buffer to small header not found"
+ " partial buffer len %d, input buffer len %d",
+ partialBufLen, inputBufLen);
+ //mPartialBuffer->release();
+ //mPartialBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
+ // copy the header bytes to frameHeader
+ memcpy (frameHeader, mPartialBuffer->data(), partialBufLen);
+ memcpy (frameHeader + partialBufLen, mInputBuffer->data(), (4 - partialBufLen));
+ // get the first 4 bytes of the buffer
+ header = U32_AT((uint8_t *)frameHeader);
+ frmHdr = frameHeader;
+ } else {
+ frmHdr = (uint8_t *)mPartialBuffer->data();
+ }
+
+ // check if its a good frame, and the frame size
+ // get the first 4 bytes of the buffer
+ header = U32_AT(frmHdr);
+ bool curFrame = get_mp3_frame_size(header,&frameSize);
+ if (!curFrame) {
+ ALOGE("MP3Decoder::read - partial frame does not have a vaild header 0x%x",
+ header);
+ return UNKNOWN_ERROR;
+ }
+
+ // check if the following frame is good
+ uint32_t nextFrameOffset = frameSize - partialBufLen;
+ if ((nextFrameOffset + 4) <= inputBufLen) {
+ header = U32_AT((uint8_t *)mInputBuffer->data() + nextFrameOffset);
+ if ((header & 0xffe00000) != 0xffe00000) {
+ // next frame does not have a valid header,
+ // this may not be the next buffer, bail out.
+ ALOGE("MP3Decoder::read - next frame does not have a vaild header 0x%x",
+ header);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ // next frame header is out of range
+ // assume good header for now
+ ALOGE("MP3Decoder::read - assuming next frame is good");
+ }
+
+ // check if the input buffer has the remaining partial frame
+ if (frameSize > (partialBufLen + inputBufLen)) {
+ // input buffer does not have the remaining partial frame,
+ // discard data here as frame split in 3 buffers not supported
+ ALOGE("MP3Decoder::updatePartialFrame - input buffer does not have the complete frame."
+ " frame size %d, saved partial buffer len %d,"
+ " input buffer len %d", frameSize, partialBufLen, inputBufLen);
+ return UNKNOWN_ERROR;
+ }
+
+ // check if the mPartialBuffer can fit the remaining frame
+ if ((mPartialBuffer->size() - partialBufLen) < (frameSize - partialBufLen)) {
+ // mPartialBuffer is small to hold the reaming frame
+ //TODO
+ ALOGE("MP3Decoder::updatePartialFrame - mPartialBuffer is small, size %d, required &d",
+ (mPartialBuffer->size() - partialBufLen), (frameSize - partialBufLen));
+ return UNKNOWN_ERROR;
+ }
+
+ // done with error checks
+ // copy the partial frames to from a complete frame
+ // Copy the remaining frame from input buffer
+ uint32_t bytesRemaining = frameSize - mPartialBuffer->range_length();
+ memcpy ((uint8_t *)mPartialBuffer->data() + mPartialBuffer->range_length(),
+ (uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset(),
+ bytesRemaining);
+
+ // mark the bytes as consumed from input buffer
+ mInputBuffer->set_range(
+ mInputBuffer->range_offset() + bytesRemaining,
+ mInputBuffer->range_length() - bytesRemaining);
+
+ // set the range and length of mPartialBuffer
+ mPartialBuffer->set_range(0,
+ mPartialBuffer->range_length() + bytesRemaining);
+
+ ALOGE("MP3Decoder::updatePartialFrame - copied the partial frame %d, input buffer length %d",
+ bytesRemaining, mInputBuffer->range_length());
+
+ return err;
+}
+
+status_t MP3Decoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ status_t err;
+
+ *out = NULL;
+ bool usedPartialFrame = false;
+ bool seekSource = false;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ CHECK(seekTimeUs >= 0);
+
+ mNumFramesOutput = 0;
+ seekSource = true;
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ if (mPartialBuffer) {
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ }
+
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ } else {
+ seekTimeUs = -1;
+ }
+
+ if (mInputBuffer == NULL) {
+ err = mSource->read(&mInputBuffer, options);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if ((mFixedHeader == 0) && (mInputBuffer->range_length() > 4)) {
+ //save the first 4 bytes as fixed header for the reset of the file
+ mFixedHeader = U32_AT((uint8_t *)mInputBuffer->data());
+ }
+
+ if (seekSource == true) {
+ off_t syncOffset = 0;
+ bool valid = resync((uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset()
+ ,mInputBuffer->range_length(), mFixedHeader, &syncOffset);
+ if (valid) {
+ // consume these bytes, we might find a frame header in next buffer
+ mInputBuffer->set_range(
+ mInputBuffer->range_offset() + syncOffset,
+ mInputBuffer->range_length() - syncOffset);
+ ALOGV("mp3 decoder found a sync point after seek syncOffset %d", syncOffset);
+ } else {
+ ALOGV("NO SYNC POINT found, buffer length %d",mInputBuffer->range_length());
+ }
+ }
+
+ int64_t timeUs;
+ if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
+ mAnchorTimeUs = timeUs;
+ mNumFramesOutput = 0;
+ } else {
+ // We must have a new timestamp after seeking.
+ CHECK(seekTimeUs < 0);
+ }
+ // check for partial frame
+ if (mPartialBuffer != NULL) {
+ err = updatePartialFrame();
+ if (err != OK) {
+ // updating partial frame failed, discard the previously
+ // saved partial frame and continue
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ err = OK;
+ }
+ }
+ }
+
+ MediaBuffer *buffer;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+
+ if (mPartialBuffer != NULL) {
+ mConfig->pInputBuffer =
+ (uint8_t *)mPartialBuffer->data() + mPartialBuffer->range_offset();
+ mConfig->inputBufferCurrentLength = mPartialBuffer->range_length();
+ usedPartialFrame = true;
+ } else {
+ mConfig->pInputBuffer =
+ (uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset();
+ mConfig->inputBufferCurrentLength = mInputBuffer->range_length();
+ }
+
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+
+ mConfig->outputFrameSize = buffer->size() / sizeof(int16_t);
+ mConfig->pOutputBuffer = static_cast<int16_t *>(buffer->data());
+
+ ERROR_CODE decoderErr;
+ if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+ != NO_DECODING_ERROR) {
+ ALOGV("mp3 decoder returned error %d", decoderErr);
+
+ if ((decoderErr != NO_ENOUGH_MAIN_DATA_ERROR) &&
+ (decoderErr != SYNCH_LOST_ERROR)) {
+ buffer->release();
+ buffer = NULL;
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ if (mPartialBuffer) {
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ }
+ ALOGE("mp3 decoder returned UNKNOWN_ERROR");
+
+ return UNKNOWN_ERROR;
+ }
+
+ if ((mPartialBuffer == NULL) && (decoderErr == NO_ENOUGH_MAIN_DATA_ERROR)) {
+ // Might be a partial frame, save it
+ mPartialBuffer = new MediaBuffer(mInputBuffer->size());
+ memcpy ((uint8_t *)mPartialBuffer->data(),
+ mConfig->pInputBuffer, mConfig->inputBufferCurrentLength);
+ mPartialBuffer->set_range(0, mConfig->inputBufferCurrentLength);
+ // set output buffer to 0
+ mConfig->outputFrameSize = 0;
+ // consume the copied bytes from input
+ mConfig->inputBufferUsedLength = mConfig->inputBufferCurrentLength;
+ } else if(decoderErr == SYNCH_LOST_ERROR) {
+ // Try to find the mp3 frame header in the current buffer
+ off_t syncOffset = 0;
+ bool valid = resync(mConfig->pInputBuffer, mConfig->inputBufferCurrentLength,
+ mFixedHeader, &syncOffset);
+ if (!valid || !syncOffset) {
+ // consume these bytes, we might find a frame header in next buffer
+ syncOffset = mConfig->inputBufferCurrentLength;
+ }
+ // set output buffer to 0
+ mConfig->outputFrameSize = 0;
+ // consume the junk bytes from input buffer
+ mConfig->inputBufferUsedLength = syncOffset;
+ } else {
+ // This is recoverable, just ignore the current frame and
+ // play silence instead.
+ memset(buffer->data(), 0, mConfig->outputFrameSize * sizeof(int16_t));
+ mConfig->inputBufferUsedLength = mInputBuffer->range_length();
+ }
+ }
+
+ buffer->set_range(
+ 0, mConfig->outputFrameSize * sizeof(int16_t));
+
+ if ((mPartialBuffer != NULL) && usedPartialFrame) {
+ mPartialBuffer->set_range(
+ mPartialBuffer->range_offset() + mConfig->inputBufferUsedLength,
+ mPartialBuffer->range_length() - mConfig->inputBufferUsedLength);
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ } else {
+ mInputBuffer->set_range(
+ mInputBuffer->range_offset() + mConfig->inputBufferUsedLength,
+ mInputBuffer->range_length() - mConfig->inputBufferUsedLength);
+ }
+
+ if (mInputBuffer->range_length() == 0) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ buffer->meta_data()->setInt64(
+ kKeyTime,
+ mAnchorTimeUs
+ + (mNumFramesOutput * 1000000) / mConfig->samplingRate);
+
+ mNumFramesOutput += mConfig->outputFrameSize / mNumChannels;
+
+ *out = buffer;
+
+ return OK;
+}
+
+} // namespace android