From 71c27d991ad9f07cc7e28545bf6cd2b133668cd5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 19 Mar 2010 11:43:15 -0700 Subject: Various fixes to enable recording on passion and nexus1. Change-Id: I75a461c9882e2449082ad754ee7b231c1ceec039 --- media/libstagefright/CameraSource.cpp | 7 - media/libstagefright/MPEG4Writer.cpp | 225 ++++++++++++++++++++++----- media/libstagefright/OMXCodec.cpp | 14 +- media/libstagefright/omx/OMXNodeInstance.cpp | 6 + 4 files changed, 203 insertions(+), 49 deletions(-) (limited to 'media') diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 075b1e3..f57ddc1 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -142,13 +142,6 @@ CameraSource::CameraSource(const sp &camera) mFirstFrameTimeUs(0), mNumFrames(0), mStarted(false) { - char value[PROPERTY_VALUE_MAX]; - if (property_get("ro.hardware", value, NULL) && !strcmp(value, "sholes")) { - // The hardware encoder(s) do not support yuv420, but only YCbYCr, - // fortunately the camera also supports this, so we needn't transcode. - mCamera->setParameters(String8("preview-format=yuv422i-yuyv")); - } - String8 s = mCamera->getParameters(); printf("params: \"%s\"\n", s.string()); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 2cf0ddf..e0e2b93 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -65,10 +66,15 @@ private: static void *ThreadWrapper(void *me); void threadEntry(); + status_t makeAVCCodecSpecificData( + const uint8_t *data, size_t size); + Track(const Track &); Track &operator=(const Track &); }; +#define USE_NALLEN_FOUR 1 + MPEG4Writer::MPEG4Writer(const char *filename) : mFile(fopen(filename, "wb")), mOffset(0), @@ -213,23 +219,55 @@ off_t MPEG4Writer::addSample(MediaBuffer *buffer) { return old_offset; } +static void StripStartcode(MediaBuffer *buffer) { + if (buffer->range_length() < 4) { + return; + } + + const uint8_t *ptr = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) { + buffer->set_range( + buffer->range_offset() + 4, buffer->range_length() - 4); + } +} + off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) { Mutex::Autolock autoLock(mLock); + StripStartcode(buffer); + off_t old_offset = mOffset; size_t length = buffer->range_length(); + +#if USE_NALLEN_FOUR + uint8_t x = length >> 24; + fwrite(&x, 1, 1, mFile); + x = (length >> 16) & 0xff; + fwrite(&x, 1, 1, mFile); + x = (length >> 8) & 0xff; + fwrite(&x, 1, 1, mFile); + x = length & 0xff; + fwrite(&x, 1, 1, mFile); +#else CHECK(length < 65536); uint8_t x = length >> 8; fwrite(&x, 1, 1, mFile); x = length & 0xff; fwrite(&x, 1, 1, mFile); +#endif fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 1, length, mFile); +#if USE_NALLEN_FOUR + mOffset += length + 4; +#else mOffset += length + 2; +#endif return old_offset; } @@ -380,6 +418,60 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) { return NULL; } +status_t MPEG4Writer::Track::makeAVCCodecSpecificData( + const uint8_t *data, size_t size) { + if (mCodecSpecificData != NULL) { + return ERROR_MALFORMED; + } + + if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { + // Must start with a start-code. + return ERROR_MALFORMED; + } + + size_t picParamOffset = 4; + while (picParamOffset + 3 < size + && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) { + ++picParamOffset; + } + + if (picParamOffset + 3 >= size) { + // Could not find start-code for pictureParameterSet. + return ERROR_MALFORMED; + } + + size_t seqParamSetLength = picParamOffset - 4; + size_t picParamSetLength = size - picParamOffset - 4; + + mCodecSpecificDataSize = + 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2; + + mCodecSpecificData = malloc(mCodecSpecificDataSize); + uint8_t *header = (uint8_t *)mCodecSpecificData; + header[0] = 1; + header[1] = 0x42; // profile + header[2] = 0x80; + header[3] = 0x1e; // level + +#if USE_NALLEN_FOUR + header[4] = 0xfc | 3; // length size == 4 bytes +#else + header[4] = 0xfc | 1; // length size == 2 bytes +#endif + + header[5] = 0xe0 | 1; + header[6] = seqParamSetLength >> 8; + header[7] = seqParamSetLength & 0xff; + memcpy(&header[8], &data[4], seqParamSetLength); + header += 8 + seqParamSetLength; + header[0] = 1; + header[1] = picParamSetLength >> 8; + header[2] = picParamSetLength & 0xff; + memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength); + + return OK; +} + void MPEG4Writer::Track::threadEntry() { sp meta = mSource->getFormat(); const char *mime; @@ -399,54 +491,40 @@ void MPEG4Writer::Track::threadEntry() { ++count; - if (is_avc && count < 3) { - size_t size = buffer->range_length(); - - switch (count) { - case 1: - { - CHECK_EQ(mCodecSpecificData, NULL); - mCodecSpecificData = malloc(size + 8); - uint8_t *header = (uint8_t *)mCodecSpecificData; - header[0] = 1; - header[1] = 0x42; // profile - header[2] = 0x80; - header[3] = 0x1e; // level - header[4] = 0xfc | 3; - header[5] = 0xe0 | 1; - header[6] = size >> 8; - header[7] = size & 0xff; - memcpy(&header[8], - (const uint8_t *)buffer->data() + buffer->range_offset(), - size); - - mCodecSpecificDataSize = size + 8; + int32_t isCodecConfig; + if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig) + && isCodecConfig) { + if (is_avc) { + status_t err = makeAVCCodecSpecificData( + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); + + if (err != OK) { + LOGE("failed to parse avc codec specific data."); break; } - - case 2: - { - size_t offset = mCodecSpecificDataSize; - mCodecSpecificDataSize += size + 3; - mCodecSpecificData = realloc(mCodecSpecificData, mCodecSpecificDataSize); - uint8_t *header = (uint8_t *)mCodecSpecificData; - header[offset] = 1; - header[offset + 1] = size >> 8; - header[offset + 2] = size & 0xff; - memcpy(&header[offset + 3], - (const uint8_t *)buffer->data() + buffer->range_offset(), - size); + } else if (is_mpeg4) { + if (mCodecSpecificData != NULL) { break; } + + mCodecSpecificDataSize = buffer->range_length(); + mCodecSpecificData = malloc(mCodecSpecificDataSize); + memcpy(mCodecSpecificData, + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); } buffer->release(); buffer = NULL; continue; - } + } else if (count == 1 && is_mpeg4 && mCodecSpecificData == NULL) { + // The TI mpeg4 encoder does not properly set the + // codec-specific-data flag. - if (mCodecSpecificData == NULL && is_mpeg4) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -474,13 +552,70 @@ void MPEG4Writer::Track::threadEntry() { memcpy(mCodecSpecificData, data, offset); buffer->set_range(buffer->range_offset() + offset, size - offset); + + if (size == offset) { + buffer->release(); + buffer = NULL; + + continue; + } + } else if (is_avc && count < 3) { + // The TI video encoder does not flag codec specific data + // as such and also splits up SPS and PPS across two buffers. + + const uint8_t *data = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + size_t size = buffer->range_length(); + + CHECK(count == 2 || mCodecSpecificData == NULL); + + size_t offset = mCodecSpecificDataSize; + mCodecSpecificDataSize += size + 4; + mCodecSpecificData = + realloc(mCodecSpecificData, mCodecSpecificDataSize); + + memcpy((uint8_t *)mCodecSpecificData + offset, + "\x00\x00\x00\x01", 4); + + memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size); + + buffer->release(); + buffer = NULL; + + if (count == 2) { + void *tmp = mCodecSpecificData; + size = mCodecSpecificDataSize; + mCodecSpecificData = NULL; + mCodecSpecificDataSize = 0; + + status_t err = makeAVCCodecSpecificData( + (const uint8_t *)tmp, size); + + free(tmp); + tmp = NULL; + + if (err != OK) { + LOGE("failed to parse avc codec specific data."); + break; + } + } + + continue; } off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer) : mOwner->addSample(buffer); SampleInfo info; - info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length(); + info.size = is_avc +#if USE_NALLEN_FOUR + ? buffer->range_length() + 4 +#else + ? buffer->range_length() + 2 +#endif + : buffer->range_length(); + info.offset = offset; int64_t timestampUs; @@ -733,19 +868,29 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox("stts"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size() - 1); + mOwner->writeInt32(mSampleInfos.size()); List::iterator it = mSampleInfos.begin(); int64_t last = (*it).timestamp; + int64_t lastDuration = 1; + ++it; while (it != mSampleInfos.end()) { mOwner->writeInt32(1); - mOwner->writeInt32((*it).timestamp - last); + lastDuration = (*it).timestamp - last; + mOwner->writeInt32(lastDuration); last = (*it).timestamp; ++it; } + + // We don't really know how long the last frame lasts, since + // there is no frame time after it, just repeat the previous + // frame's duration. + mOwner->writeInt32(1); + mOwner->writeInt32(lastDuration); + mOwner->endBox(); // stts mOwner->beginBox("stsz"); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 6037088..41ce239 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -137,6 +137,7 @@ static const CodecInfo kEncoderInfo[] = { { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" }, }; @@ -679,6 +680,7 @@ static size_t getFrameSize( case OMX_COLOR_FormatCbYCrY: return width * height * 2; + case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatYUV420SemiPlanar: return (width * height * 3) / 2; @@ -706,7 +708,7 @@ void OMXCodec::setVideoInputFormat( OMX_COLOR_FORMATTYPE colorFormat = OMX_COLOR_FormatYUV420SemiPlanar; if (!strcasecmp("OMX.TI.Video.encoder", mComponentName)) { - colorFormat = OMX_COLOR_FormatYCbYCr; + colorFormat = OMX_COLOR_FormatYUV420Planar; } CHECK_EQ(setVideoPortFormatType( @@ -764,6 +766,14 @@ void OMXCodec::setVideoInputFormat( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + CHECK_EQ(err, OK); + + err = mOMX->setParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + CHECK_EQ(err, OK); + switch (compressionFormat) { case OMX_VIDEO_CodingMPEG4: { @@ -911,7 +921,7 @@ status_t OMXCodec::setupAVCEncoderParameters() { CHECK_EQ(err, OK); bitrateType.eControlRate = OMX_Video_ControlRateVariable; - bitrateType.nTargetBitrate = 1000000; + bitrateType.nTargetBitrate = 3000000; err = mOMX->setParameter( mNode, OMX_IndexParamVideoBitrate, diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index c1a010c..5db516e 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -264,6 +264,8 @@ status_t OMXNodeInstance::useBuffer( return UNKNOWN_ERROR; } + CHECK_EQ(header->pAppPrivate, buffer_meta); + *buffer = header; addActiveBuffer(portIndex, *buffer); @@ -294,6 +296,8 @@ status_t OMXNodeInstance::allocateBuffer( return UNKNOWN_ERROR; } + CHECK_EQ(header->pAppPrivate, buffer_meta); + *buffer = header; *buffer_data = header->pBuffer; @@ -325,6 +329,8 @@ status_t OMXNodeInstance::allocateBufferWithBackup( return UNKNOWN_ERROR; } + CHECK_EQ(header->pAppPrivate, buffer_meta); + *buffer = header; addActiveBuffer(portIndex, *buffer); -- cgit v1.1