Merge change 21948 into eclair
* changes: The PV AVC software decoder expects each fragment of a NAL unit to be handed to the decoder unframed.
This commit is contained in:
@@ -70,6 +70,7 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) {
|
||||
|
||||
long numIterationsLeft = gNumRepetitions;
|
||||
MediaSource::ReadOptions options;
|
||||
|
||||
while (numIterationsLeft-- > 0) {
|
||||
MediaBuffer *buffer;
|
||||
|
||||
|
||||
@@ -28,23 +28,23 @@
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
kKeyMIMEType = 'mime',
|
||||
kKeyWidth = 'widt',
|
||||
kKeyHeight = 'heig',
|
||||
kKeyChannelCount = '#chn',
|
||||
kKeySampleRate = 'srte',
|
||||
kKeyBitRate = 'brte',
|
||||
kKeyESDS = 'esds',
|
||||
kKeyAVCC = 'avcc',
|
||||
kKeyTimeUnits = '#tim',
|
||||
kKeyTimeScale = 'scal',
|
||||
kKeyNeedsNALFraming = 'NALf',
|
||||
kKeyIsSyncFrame = 'sync',
|
||||
kKeyDuration = 'dura',
|
||||
kKeyColorFormat = 'colf',
|
||||
kKeyPlatformPrivate = 'priv',
|
||||
kKeyDecoderComponent = 'decC',
|
||||
kKeyBufferID = 'bfID',
|
||||
kKeyMIMEType = 'mime',
|
||||
kKeyWidth = 'widt',
|
||||
kKeyHeight = 'heig',
|
||||
kKeyChannelCount = '#chn',
|
||||
kKeySampleRate = 'srte',
|
||||
kKeyBitRate = 'brte',
|
||||
kKeyESDS = 'esds',
|
||||
kKeyAVCC = 'avcc',
|
||||
kKeyTimeUnits = '#tim',
|
||||
kKeyTimeScale = 'scal',
|
||||
kKeyWantsNALFragments = 'NALf',
|
||||
kKeyIsSyncFrame = 'sync',
|
||||
kKeyDuration = 'dura',
|
||||
kKeyColorFormat = 'colf',
|
||||
kKeyPlatformPrivate = 'priv',
|
||||
kKeyDecoderComponent = 'decC',
|
||||
kKeyBufferID = 'bfID',
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
|
||||
enum Quirks {
|
||||
kNeedsFlushBeforeDisable = 1,
|
||||
kWantsRawNALFrames = 2,
|
||||
kWantsNALFragments = 2,
|
||||
kRequiresLoadedToIdleAfterAllocation = 4,
|
||||
kRequiresAllocateBufferOnInputPorts = 8,
|
||||
};
|
||||
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
};
|
||||
|
||||
enum Quirks {
|
||||
kWantsRawNALFrames = 1,
|
||||
kWantsNALFragments = 1,
|
||||
kDoesntReturnBuffersOnDisable = 2,
|
||||
kDoesntFlushOnExecutingToIdle = 4,
|
||||
kDoesntProperlyFlushAllPortsAtOnce = 8,
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -31,6 +28,7 @@
|
||||
#include <media/stagefright/MPEG4Extractor.h>
|
||||
#include <media/stagefright/MediaBuffer.h>
|
||||
#include <media/stagefright/MediaBufferGroup.h>
|
||||
#include <media/stagefright/MediaDebug.h>
|
||||
#include <media/stagefright/MediaSource.h>
|
||||
#include <media/stagefright/MetaData.h>
|
||||
#include <media/stagefright/SampleTable.h>
|
||||
@@ -70,10 +68,8 @@ private:
|
||||
MediaBufferGroup *mGroup;
|
||||
|
||||
MediaBuffer *mBuffer;
|
||||
size_t mBufferOffset;
|
||||
size_t mBufferSizeRemaining;
|
||||
|
||||
bool mNeedsNALFraming;
|
||||
bool mWantsNALFragments;
|
||||
|
||||
uint8_t *mSrcBuffer;
|
||||
|
||||
@@ -138,7 +134,7 @@ static const char *const FourCC2MIME(uint32_t fourcc) {
|
||||
return "video/avc";
|
||||
|
||||
default:
|
||||
assert(!"should not be here.");
|
||||
CHECK(!"should not be here.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -279,7 +275,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
assert(*offset == stop_offset);
|
||||
CHECK_EQ(*offset, stop_offset);
|
||||
|
||||
if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
|
||||
mHaveMetadata = true;
|
||||
@@ -291,7 +287,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
|
||||
case FOURCC('t', 'k', 'h', 'd'):
|
||||
{
|
||||
assert(chunk_data_size >= 4);
|
||||
CHECK(chunk_data_size >= 4);
|
||||
|
||||
uint8_t version;
|
||||
if (mDataSource->read_at(data_offset, &version, 1) < 1) {
|
||||
@@ -444,7 +440,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
}
|
||||
|
||||
uint8_t buffer[8];
|
||||
assert(chunk_data_size >= (off_t)sizeof(buffer));
|
||||
CHECK(chunk_data_size >= (off_t)sizeof(buffer));
|
||||
if (mDataSource->read_at(
|
||||
data_offset, buffer, 8) < 8) {
|
||||
return ERROR_IO;
|
||||
@@ -470,7 +466,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
assert(*offset == stop_offset);
|
||||
CHECK_EQ(*offset, stop_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -518,7 +514,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
assert(*offset == stop_offset);
|
||||
CHECK_EQ(*offset, stop_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -560,7 +556,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
assert(*offset == stop_offset);
|
||||
CHECK_EQ(*offset, stop_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -728,16 +724,14 @@ MPEG4Source::MPEG4Source(
|
||||
mStarted(false),
|
||||
mGroup(NULL),
|
||||
mBuffer(NULL),
|
||||
mBufferOffset(0),
|
||||
mBufferSizeRemaining(0),
|
||||
mNeedsNALFraming(false),
|
||||
mWantsNALFragments(false),
|
||||
mSrcBuffer(NULL) {
|
||||
const char *mime;
|
||||
bool success = mFormat->findCString(kKeyMIMEType, &mime);
|
||||
assert(success);
|
||||
CHECK(success);
|
||||
|
||||
success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
|
||||
assert(success);
|
||||
CHECK(success);
|
||||
|
||||
mIsAVC = !strcasecmp(mime, "video/avc");
|
||||
}
|
||||
@@ -749,21 +743,21 @@ MPEG4Source::~MPEG4Source() {
|
||||
}
|
||||
|
||||
status_t MPEG4Source::start(MetaData *params) {
|
||||
assert(!mStarted);
|
||||
CHECK(!mStarted);
|
||||
|
||||
int32_t val;
|
||||
if (mIsAVC && params && params->findInt32(kKeyNeedsNALFraming, &val)
|
||||
if (params && params->findInt32(kKeyWantsNALFragments, &val)
|
||||
&& val != 0) {
|
||||
mNeedsNALFraming = true;
|
||||
mWantsNALFragments = true;
|
||||
} else {
|
||||
mNeedsNALFraming = false;
|
||||
mWantsNALFragments = false;
|
||||
}
|
||||
|
||||
mGroup = new MediaBufferGroup;
|
||||
|
||||
size_t max_size;
|
||||
status_t err = mSampleTable->getMaxSampleSize(&max_size);
|
||||
assert(err == OK);
|
||||
CHECK_EQ(err, OK);
|
||||
|
||||
// Assume that a given buffer only contains at most 10 fragments,
|
||||
// each fragment originally prefixed with a 2 byte length will
|
||||
@@ -779,7 +773,7 @@ status_t MPEG4Source::start(MetaData *params) {
|
||||
}
|
||||
|
||||
status_t MPEG4Source::stop() {
|
||||
assert(mStarted);
|
||||
CHECK(mStarted);
|
||||
|
||||
if (mBuffer != NULL) {
|
||||
mBuffer->release();
|
||||
@@ -804,7 +798,7 @@ sp<MetaData> MPEG4Source::getFormat() {
|
||||
|
||||
status_t MPEG4Source::read(
|
||||
MediaBuffer **out, const ReadOptions *options) {
|
||||
assert(mStarted);
|
||||
CHECK(mStarted);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
@@ -830,38 +824,124 @@ status_t MPEG4Source::read(
|
||||
|
||||
off_t offset;
|
||||
size_t size;
|
||||
status_t err = mSampleTable->getSampleOffsetAndSize(
|
||||
mCurrentSampleIndex, &offset, &size);
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
uint32_t dts;
|
||||
err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
|
||||
bool newBuffer = false;
|
||||
if (mBuffer == NULL) {
|
||||
newBuffer = true;
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
status_t err = mSampleTable->getSampleOffsetAndSize(
|
||||
mCurrentSampleIndex, &offset, &size);
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mGroup->acquire_buffer(&mBuffer);
|
||||
if (err != OK) {
|
||||
CHECK_EQ(mBuffer, NULL);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = mGroup->acquire_buffer(&mBuffer);
|
||||
if (err != OK) {
|
||||
assert(mBuffer == NULL);
|
||||
return err;
|
||||
}
|
||||
if (!mIsAVC || mWantsNALFragments) {
|
||||
if (newBuffer) {
|
||||
ssize_t num_bytes_read =
|
||||
mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
|
||||
|
||||
if (num_bytes_read < (ssize_t)size) {
|
||||
mBuffer->release();
|
||||
mBuffer = NULL;
|
||||
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
mBuffer->set_range(0, size);
|
||||
mBuffer->meta_data()->clear();
|
||||
mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
|
||||
mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
|
||||
++mCurrentSampleIndex;
|
||||
}
|
||||
|
||||
if (!mIsAVC) {
|
||||
*out = mBuffer;
|
||||
mBuffer = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Each NAL unit is split up into its constituent fragments and
|
||||
// each one of them returned in its own buffer.
|
||||
|
||||
CHECK(mBuffer->range_length() >= 2);
|
||||
|
||||
const uint8_t *src =
|
||||
(const uint8_t *)mBuffer->data() + mBuffer->range_offset();
|
||||
|
||||
size_t nal_size = U16_AT(src);
|
||||
|
||||
CHECK(mBuffer->range_length() >= 2 + nal_size);
|
||||
|
||||
MediaBuffer *clone = mBuffer->clone();
|
||||
clone->set_range(mBuffer->range_offset() + 2, nal_size);
|
||||
|
||||
mBuffer->set_range(
|
||||
mBuffer->range_offset() + 2 + nal_size,
|
||||
mBuffer->range_length() - 2 - nal_size);
|
||||
|
||||
if (mBuffer->range_length() == 0) {
|
||||
mBuffer->release();
|
||||
mBuffer = NULL;
|
||||
}
|
||||
|
||||
*out = clone;
|
||||
|
||||
return OK;
|
||||
} else {
|
||||
// Whole NAL units are returned but each fragment is prefixed by
|
||||
// the start code (0x00 00 00 01).
|
||||
|
||||
if (!mIsAVC || !mNeedsNALFraming) {
|
||||
ssize_t num_bytes_read =
|
||||
mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
|
||||
mDataSource->read_at(offset, mSrcBuffer, size);
|
||||
|
||||
if (num_bytes_read < (ssize_t)size) {
|
||||
mBuffer->release();
|
||||
mBuffer = NULL;
|
||||
|
||||
return err;
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
mBuffer->set_range(0, size);
|
||||
uint8_t *dstData = (uint8_t *)mBuffer->data();
|
||||
size_t srcOffset = 0;
|
||||
size_t dstOffset = 0;
|
||||
while (srcOffset < size) {
|
||||
CHECK(srcOffset + 1 < size);
|
||||
size_t nalLength =
|
||||
(mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
|
||||
CHECK(srcOffset + 1 + nalLength < size);
|
||||
srcOffset += 2;
|
||||
|
||||
if (nalLength == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CHECK(dstOffset + 4 <= mBuffer->size());
|
||||
|
||||
dstData[dstOffset++] = 0;
|
||||
dstData[dstOffset++] = 0;
|
||||
dstData[dstOffset++] = 0;
|
||||
dstData[dstOffset++] = 1;
|
||||
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
|
||||
srcOffset += nalLength;
|
||||
dstOffset += nalLength;
|
||||
}
|
||||
|
||||
mBuffer->set_range(0, dstOffset);
|
||||
mBuffer->meta_data()->clear();
|
||||
mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
|
||||
mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
|
||||
@@ -872,52 +952,6 @@ status_t MPEG4Source::read(
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ssize_t num_bytes_read =
|
||||
mDataSource->read_at(offset, mSrcBuffer, size);
|
||||
|
||||
if (num_bytes_read < (ssize_t)size) {
|
||||
mBuffer->release();
|
||||
mBuffer = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t *dstData = (uint8_t *)mBuffer->data();
|
||||
size_t srcOffset = 0;
|
||||
size_t dstOffset = 0;
|
||||
while (srcOffset < size) {
|
||||
assert(srcOffset + 1 < size);
|
||||
size_t nalLength =
|
||||
(mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
|
||||
assert(srcOffset + 1 + nalLength < size);
|
||||
srcOffset += 2;
|
||||
|
||||
if (nalLength == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(dstOffset + 4 <= mBuffer->size());
|
||||
|
||||
dstData[dstOffset++] = 0;
|
||||
dstData[dstOffset++] = 0;
|
||||
dstData[dstOffset++] = 0;
|
||||
dstData[dstOffset++] = 1;
|
||||
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
|
||||
srcOffset += nalLength;
|
||||
dstOffset += nalLength;
|
||||
}
|
||||
|
||||
mBuffer->set_range(0, dstOffset);
|
||||
mBuffer->meta_data()->clear();
|
||||
mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
|
||||
mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
|
||||
++mCurrentSampleIndex;
|
||||
|
||||
*out = mBuffer;
|
||||
mBuffer = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool SniffMPEG4(
|
||||
|
||||
@@ -152,7 +152,7 @@ sp<OMXCodec> OMXCodec::Create(
|
||||
|
||||
uint32_t quirks = 0;
|
||||
if (!strcmp(componentName, "OMX.PV.avcdec")) {
|
||||
quirks |= kWantsRawNALFrames;
|
||||
quirks |= kWantsNALFragments;
|
||||
}
|
||||
if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
|
||||
quirks |= kNeedsFlushBeforeDisable;
|
||||
@@ -554,7 +554,7 @@ OMXCodec::OMXCodec(
|
||||
}
|
||||
|
||||
OMXCodec::~OMXCodec() {
|
||||
CHECK_EQ(mState, LOADED);
|
||||
CHECK(mState == LOADED || mState == ERROR);
|
||||
|
||||
status_t err = mOMX->observe_node(mNode, NULL);
|
||||
CHECK_EQ(err, OK);
|
||||
@@ -1149,8 +1149,8 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
|
||||
|
||||
size_t size = specific->mSize;
|
||||
|
||||
if (!strcasecmp(mMIME, "video/avc")
|
||||
&& !(mQuirks & kWantsRawNALFrames)) {
|
||||
if (!strcasecmp("video/avc", mMIME)
|
||||
&& !(mQuirks & kWantsNALFragments)) {
|
||||
static const uint8_t kNALStartCode[4] =
|
||||
{ 0x00, 0x00, 0x00, 0x01 };
|
||||
|
||||
@@ -1462,8 +1462,8 @@ status_t OMXCodec::start(MetaData *) {
|
||||
}
|
||||
|
||||
sp<MetaData> params = new MetaData;
|
||||
if (!strcasecmp(mMIME, "video/avc") && !(mQuirks & kWantsRawNALFrames)) {
|
||||
params->setInt32(kKeyNeedsNALFraming, true);
|
||||
if (mQuirks & kWantsNALFragments) {
|
||||
params->setInt32(kKeyWantsNALFragments, true);
|
||||
}
|
||||
status_t err = mSource->start(params.get());
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ sp<OMXDecoder> OMXDecoder::Create(
|
||||
|
||||
uint32_t quirks = 0;
|
||||
if (!strcmp(codec, "OMX.PV.avcdec")) {
|
||||
quirks |= kWantsRawNALFrames;
|
||||
quirks |= kWantsNALFragments;
|
||||
}
|
||||
if (!strcmp(codec, "OMX.TI.AAC.decode")
|
||||
|| !strcmp(codec, "OMX.TI.MP3.decode")) {
|
||||
@@ -274,8 +274,8 @@ status_t OMXDecoder::start(MetaData *) {
|
||||
// mDealer->dump("Decoder Dealer");
|
||||
|
||||
sp<MetaData> params = new MetaData;
|
||||
if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
|
||||
params->setInt32(kKeyNeedsNALFraming, true);
|
||||
if (mQuirks & kWantsNALFragments) {
|
||||
params->setInt32(kKeyWantsNALFragments, true);
|
||||
}
|
||||
|
||||
status_t err = mSource->start(params.get());
|
||||
@@ -1331,7 +1331,7 @@ void OMXDecoder::onRealEmptyBufferDone(IOMX::buffer_id buffer) {
|
||||
|
||||
size_t range_length = 0;
|
||||
|
||||
if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
|
||||
if (mIsAVC && !(mQuirks & kWantsNALFragments)) {
|
||||
assert((*mCodecSpecificDataIterator).size + 4 <= mem->size());
|
||||
|
||||
memcpy(mem->pointer(), kNALStartCode, 4);
|
||||
|
||||
Reference in New Issue
Block a user