summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/codecs/avc/dec/AVCDecoder.cpp')
-rw-r--r--media/libstagefright/codecs/avc/dec/AVCDecoder.cpp439
1 files changed, 439 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
new file mode 100644
index 0000000..0fc9622
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -0,0 +1,439 @@
+/*
+ * 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 "AVCDecoder.h"
+
+#include "avcdec_api.h"
+#include "avcdec_int.h"
+
+#include <OMX_Component.h>
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static int32_t Malloc(void *userData, int32_t size, int32_t attrs) {
+ return reinterpret_cast<int32_t>(malloc(size));
+}
+
+static void Free(void *userData, int32_t ptr) {
+ free(reinterpret_cast<void *>(ptr));
+}
+
+AVCDecoder::AVCDecoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mStarted(false),
+ mHandle(new tagAVCHandle),
+ mInputBuffer(NULL),
+ mAnchorTimeUs(0),
+ mNumSamplesOutput(0) {
+ memset(mHandle, 0, sizeof(tagAVCHandle));
+ mHandle->AVCObject = NULL;
+ mHandle->userData = this;
+ mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper;
+ mHandle->CBAVC_FrameBind = BindFrameWrapper;
+ mHandle->CBAVC_FrameUnbind = UnbindFrame;
+ mHandle->CBAVC_Malloc = Malloc;
+ mHandle->CBAVC_Free = Free;
+
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ int32_t width, height;
+ CHECK(mSource->getFormat()->findInt32(kKeyWidth, &width));
+ CHECK(mSource->getFormat()->findInt32(kKeyHeight, &height));
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
+ mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
+ mFormat->setCString(kKeyDecoderComponent, "AVCDecoder");
+}
+
+AVCDecoder::~AVCDecoder() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mHandle;
+ mHandle = NULL;
+}
+
+status_t AVCDecoder::start(MetaData *) {
+ CHECK(!mStarted);
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (mSource->getFormat()->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ(ptr[0], 1); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+ }
+
+ sp<MetaData> params = new MetaData;
+ params->setInt32(kKeyWantsNALFragments, true);
+ mSource->start(params.get());
+
+ mAnchorTimeUs = 0;
+ mNumSamplesOutput = 0;
+ mStarted = true;
+
+ return OK;
+}
+
+void AVCDecoder::addCodecSpecificData(const uint8_t *data, size_t size) {
+ MediaBuffer *buffer = new MediaBuffer(size);
+ memcpy(buffer->data(), data, size);
+ buffer->set_range(0, size);
+
+ mCodecSpecificData.push(buffer);
+}
+
+status_t AVCDecoder::stop() {
+ CHECK(mStarted);
+
+ for (size_t i = 0; i < mCodecSpecificData.size(); ++i) {
+ (*mCodecSpecificData.editItemAt(i)).release();
+ }
+ mCodecSpecificData.clear();
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ mSource->stop();
+
+ releaseFrames();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> AVCDecoder::getFormat() {
+ return mFormat;
+}
+
+status_t AVCDecoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ if (mInputBuffer == NULL) {
+ LOGV("fetching new input buffer.");
+
+ if (!mCodecSpecificData.isEmpty()) {
+ mInputBuffer = mCodecSpecificData.editItemAt(0);
+ mCodecSpecificData.removeAt(0);
+ } else {
+ for (;;) {
+ status_t err = mSource->read(&mInputBuffer);
+ if (err != OK) {
+ return err;
+ }
+
+ if (mInputBuffer->range_length() > 0) {
+ break;
+ }
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+ }
+ }
+
+ const uint8_t *inPtr =
+ (const uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset();
+
+ int nalType;
+ int nalRefIdc;
+ AVCDec_Status res =
+ PVAVCDecGetNALType(
+ const_cast<uint8_t *>(inPtr), mInputBuffer->range_length(),
+ &nalType, &nalRefIdc);
+
+ if (res != AVCDEC_SUCCESS) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ return UNKNOWN_ERROR;
+ }
+
+ switch (nalType) {
+ case AVC_NALTYPE_SPS:
+ {
+ res = PVAVCDecSeqParamSet(
+ mHandle, const_cast<uint8_t *>(inPtr),
+ mInputBuffer->range_length());
+
+ if (res != AVCDEC_SUCCESS) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ return UNKNOWN_ERROR;
+ }
+
+ AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject;
+
+ int32_t width =
+ (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16;
+
+ int32_t height =
+ (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16;
+
+ int32_t crop_left, crop_right, crop_top, crop_bottom;
+ if (pDecVid->seqParams[0]->frame_cropping_flag)
+ {
+ crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset;
+ crop_right =
+ width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1);
+
+ if (pDecVid->seqParams[0]->frame_mbs_only_flag)
+ {
+ crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset;
+ crop_bottom =
+ height -
+ (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+ }
+ else
+ {
+ crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset;
+ crop_bottom =
+ height -
+ (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+ }
+ } else {
+ crop_bottom = height - 1;
+ crop_right = width - 1;
+ crop_top = crop_left = 0;
+ }
+
+ mFormat->setInt32(kKeyWidth, crop_right - crop_left + 1);
+ mFormat->setInt32(kKeyHeight, crop_bottom - crop_top + 1);
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ return INFO_FORMAT_CHANGED;
+ }
+
+ case AVC_NALTYPE_PPS:
+ {
+ res = PVAVCDecPicParamSet(
+ mHandle, const_cast<uint8_t *>(inPtr),
+ mInputBuffer->range_length());
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ if (res != AVCDEC_SUCCESS) {
+ return UNKNOWN_ERROR;
+ }
+
+ *out = new MediaBuffer(0);
+
+ return OK;
+ }
+
+ case AVC_NALTYPE_SLICE:
+ case AVC_NALTYPE_IDR:
+ {
+ res = PVAVCDecodeSlice(
+ mHandle, const_cast<uint8_t *>(inPtr),
+ mInputBuffer->range_length());
+
+ if (res == AVCDEC_PICTURE_OUTPUT_READY) {
+ int32_t index;
+ int32_t Release;
+ AVCFrameIO Output;
+ Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
+ CHECK_EQ(PVAVCDecGetOutput(
+ mHandle, &index, &Release, &Output),
+ AVCDEC_SUCCESS);
+
+ CHECK(index >= 0);
+ CHECK(index < (int32_t)mFrames.size());
+
+ *out = mFrames.editItemAt(index);
+ (*out)->set_range(0, (*out)->size());
+ (*out)->add_ref();
+
+ // Do _not_ release input buffer yet.
+
+ return OK;
+ }
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ if (res == AVCDEC_PICTURE_READY) {
+ *out = new MediaBuffer(0);
+
+ return OK;
+ } else {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ case AVC_NALTYPE_SEI:
+ {
+ res = PVAVCDecodeSlice(
+ mHandle, const_cast<uint8_t *>(inPtr),
+ mInputBuffer->range_length());
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ if (res != AVCDEC_SUCCESS) {
+ return UNKNOWN_ERROR;
+ }
+
+ *out = new MediaBuffer(0);
+
+ return OK;
+ }
+
+ default:
+ {
+ LOGE("Should not be here, unknown nalType %d", nalType);
+ CHECK(!"Should not be here");
+ break;
+ }
+ }
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ return UNKNOWN_ERROR;
+}
+
+// static
+int32_t AVCDecoder::ActivateSPSWrapper(
+ void *userData, unsigned int sizeInMbs, unsigned int numBuffers) {
+ return static_cast<AVCDecoder *>(userData)->activateSPS(sizeInMbs, numBuffers);
+}
+
+// static
+int32_t AVCDecoder::BindFrameWrapper(
+ void *userData, int32_t index, uint8_t **yuv) {
+ return static_cast<AVCDecoder *>(userData)->bindFrame(index, yuv);
+}
+
+// static
+void AVCDecoder::UnbindFrame(void *userData, int32_t index) {
+}
+
+int32_t AVCDecoder::activateSPS(
+ unsigned int sizeInMbs, unsigned int numBuffers) {
+ CHECK(mFrames.isEmpty());
+
+ size_t frameSize = (sizeInMbs << 7) * 3;
+ for (unsigned int i = 0; i < numBuffers; ++i) {
+ MediaBuffer *buffer = new MediaBuffer(frameSize);
+ buffer->setObserver(this);
+
+ mFrames.push(buffer);
+ }
+
+ return 1;
+}
+
+int32_t AVCDecoder::bindFrame(int32_t index, uint8_t **yuv) {
+ CHECK(index >= 0);
+ CHECK(index < (int32_t)mFrames.size());
+
+ CHECK(mInputBuffer != NULL);
+ int64_t timeUs;
+ CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ mFrames[index]->meta_data()->setInt64(kKeyTime, timeUs);
+
+ *yuv = (uint8_t *)mFrames[index]->data();
+
+ return 1;
+}
+
+void AVCDecoder::releaseFrames() {
+ for (size_t i = 0; i < mFrames.size(); ++i) {
+ MediaBuffer *buffer = mFrames.editItemAt(i);
+
+ buffer->setObserver(NULL);
+ buffer->release();
+ }
+ mFrames.clear();
+}
+
+void AVCDecoder::signalBufferReturned(MediaBuffer *buffer) {
+}
+
+} // namespace android