diff options
Diffstat (limited to 'media/libstagefright')
-rw-r--r-- | media/libstagefright/AMRWriter.cpp | 26 | ||||
-rw-r--r-- | media/libstagefright/AudioSource.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/CameraSource.cpp | 145 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Writer.cpp | 700 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 129 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXNodeInstance.cpp | 6 |
6 files changed, 748 insertions, 262 deletions
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index bf4424b..73ea56d 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -53,8 +53,6 @@ status_t AMRWriter::initCheck() const { } status_t AMRWriter::addSource(const sp<MediaSource> &source) { - Mutex::Autolock autoLock(mLock); - if (mInitCheck != OK) { return mInitCheck; } @@ -95,8 +93,6 @@ status_t AMRWriter::addSource(const sp<MediaSource> &source) { } status_t AMRWriter::start() { - Mutex::Autolock autoLock(mLock); - if (mInitCheck != OK) { return mInitCheck; } @@ -127,16 +123,12 @@ status_t AMRWriter::start() { } void AMRWriter::stop() { - { - Mutex::Autolock autoLock(mLock); - - if (!mStarted) { - return; - } - - mDone = true; + if (!mStarted) { + return; } + mDone = true; + void *dummy; pthread_join(mThread, &dummy); @@ -153,13 +145,7 @@ void *AMRWriter::ThreadWrapper(void *me) { } void AMRWriter::threadFunc() { - for (;;) { - Mutex::Autolock autoLock(mLock); - - if (mDone) { - break; - } - + while (!mDone) { MediaBuffer *buffer; status_t err = mSource->read(&buffer); @@ -184,12 +170,10 @@ void AMRWriter::threadFunc() { buffer = NULL; } - Mutex::Autolock autoLock(mLock); mReachedEOS = true; } bool AMRWriter::reachedEOS() { - Mutex::Autolock autoLock(mLock); return mReachedEOS; } diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index edabaf9..abd8abc 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "AudioSource" +#include <utils/Log.h> + #include <media/stagefright/AudioSource.h> #include <media/AudioRecord.h> diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 075b1e3..cd26e6b 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include <sys/time.h> +//#define LOG_NDEBUG 0 +#define LOG_TAG "CameraSource" +#include <utils/Log.h> #include <OMX_Component.h> -#include <binder/IServiceManager.h> -#include <cutils/properties.h> // for property_get #include <media/stagefright/CameraSource.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> @@ -27,46 +27,10 @@ #include <media/stagefright/MetaData.h> #include <camera/Camera.h> #include <camera/CameraParameters.h> -#include <ui/GraphicBuffer.h> -#include <ui/Overlay.h> -#include <surfaceflinger/ISurface.h> #include <utils/String8.h> namespace android { -static int64_t getNowUs() { - struct timeval tv; - gettimeofday(&tv, NULL); - - return (int64_t)tv.tv_usec + tv.tv_sec * 1000000; -} - -struct DummySurface : public BnSurface { - DummySurface() {} - - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) { - return NULL; - } - - virtual status_t registerBuffers(const BufferHeap &buffers) { - return OK; - } - - virtual void postBuffer(ssize_t offset) {} - virtual void unregisterBuffers() {} - - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format, int32_t orientation) { - return NULL; - } - -protected: - virtual ~DummySurface() {} - - DummySurface(const DummySurface &); - DummySurface &operator=(const DummySurface &); -}; - struct CameraSourceListener : public CameraListener { CameraSourceListener(const sp<CameraSource> &source); @@ -100,22 +64,20 @@ void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) { LOGV("postData(%d, ptr:%p, size:%d)", msgType, dataPtr->pointer(), dataPtr->size()); - - sp<CameraSource> source = mSource.promote(); - if (source.get() != NULL) { - source->dataCallback(msgType, dataPtr); - } } void CameraSourceListener::postDataTimestamp( nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) { - LOGV("postDataTimestamp(%lld, %d, ptr:%p, size:%d)", - timestamp, msgType, dataPtr->pointer(), dataPtr->size()); + + sp<CameraSource> source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr); + } } // static CameraSource *CameraSource::Create() { - sp<Camera> camera = Camera::connect(); + sp<Camera> camera = Camera::connect(0); if (camera.get() == NULL) { return NULL; @@ -125,9 +87,7 @@ CameraSource *CameraSource::Create() { } // static -CameraSource *CameraSource::CreateFromICamera(const sp<ICamera> &icamera) { - sp<Camera> camera = Camera::create(icamera); - +CameraSource *CameraSource::CreateFromCamera(const sp<Camera> &camera) { if (camera.get() == NULL) { return NULL; } @@ -140,15 +100,11 @@ CameraSource::CameraSource(const sp<Camera> &camera) mWidth(0), mHeight(0), mFirstFrameTimeUs(0), - mNumFrames(0), + mLastFrameTimestampUs(0), + mNumFramesReceived(0), + mNumFramesEncoded(0), + mNumFramesDropped(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()); @@ -162,26 +118,12 @@ CameraSource::~CameraSource() { } } -void CameraSource::setPreviewSurface(const sp<ISurface> &surface) { - mPreviewSurface = surface; -} - status_t CameraSource::start(MetaData *) { + LOGV("start"); CHECK(!mStarted); mCamera->setListener(new CameraSourceListener(this)); - - status_t err = - mCamera->setPreviewDisplay( - mPreviewSurface != NULL ? mPreviewSurface : new DummySurface); - CHECK_EQ(err, OK); - - mCamera->setPreviewCallbackFlags( - FRAME_CALLBACK_FLAG_ENABLE_MASK - | FRAME_CALLBACK_FLAG_COPY_OUT_MASK); - - err = mCamera->startPreview(); - CHECK_EQ(err, OK); + CHECK_EQ(OK, mCamera->startRecording()); mStarted = true; @@ -189,15 +131,32 @@ status_t CameraSource::start(MetaData *) { } status_t CameraSource::stop() { - CHECK(mStarted); - - mCamera->stopPreview(); - + LOGV("stop"); + Mutex::Autolock autoLock(mLock); mStarted = false; + mFrameAvailableCondition.signal(); + mCamera->setListener(NULL); + mCamera->stopRecording(); + + releaseQueuedFrames(); + LOGI("Frames received/encoded/dropped: %d/%d/%d, timestamp (us) last/first: %lld/%lld", + mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, + mLastFrameTimestampUs, mFirstFrameTimeUs); + CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); return OK; } +void CameraSource::releaseQueuedFrames() { + List<sp<IMemory> >::iterator it; + while (!mFrames.empty()) { + it = mFrames.begin(); + mCamera->releaseRecordingFrame(*it); + mFrames.erase(it); + ++mNumFramesDropped; + } +} + sp<MetaData> CameraSource::getFormat() { sp<MetaData> meta = new MetaData; meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); @@ -210,7 +169,7 @@ sp<MetaData> CameraSource::getFormat() { status_t CameraSource::read( MediaBuffer **buffer, const ReadOptions *options) { - CHECK(mStarted); + LOGV("read"); *buffer = NULL; @@ -224,20 +183,24 @@ status_t CameraSource::read( { Mutex::Autolock autoLock(mLock); - while (mFrames.empty()) { + while (mStarted && mFrames.empty()) { mFrameAvailableCondition.wait(mLock); } - + if (!mStarted) { + return OK; + } frame = *mFrames.begin(); mFrames.erase(mFrames.begin()); frameTime = *mFrameTimes.begin(); mFrameTimes.erase(mFrameTimes.begin()); + ++mNumFramesEncoded; } *buffer = new MediaBuffer(frame->size()); memcpy((*buffer)->data(), frame->pointer(), frame->size()); (*buffer)->set_range(0, frame->size()); + mCamera->releaseRecordingFrame(frame); (*buffer)->meta_data()->clear(); (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); @@ -245,17 +208,25 @@ status_t CameraSource::read( return OK; } -void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) { +void CameraSource::dataCallbackTimestamp(int64_t timestampUs, + int32_t msgType, const sp<IMemory> &data) { + LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); + mLastFrameTimestampUs = timestampUs; Mutex::Autolock autoLock(mLock); + if (!mStarted) { + mCamera->releaseRecordingFrame(data); + ++mNumFramesReceived; + ++mNumFramesDropped; + return; + } - int64_t nowUs = getNowUs(); - if (mNumFrames == 0) { - mFirstFrameTimeUs = nowUs; + if (mNumFramesReceived == 0) { + mFirstFrameTimeUs = timestampUs; } - ++mNumFrames; + ++mNumFramesReceived; mFrames.push_back(data); - mFrameTimes.push_back(nowUs - mFirstFrameTimeUs); + mFrameTimes.push_back(timestampUs - mFirstFrameTimeUs); mFrameAvailableCondition.signal(); } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 2cf0ddf..19cccf7 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "MPEG4Writer" +#include <utils/Log.h> + #include <arpa/inet.h> #include <ctype.h> @@ -24,6 +28,7 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/Utils.h> @@ -52,34 +57,73 @@ private: struct SampleInfo { size_t size; - off_t offset; int64_t timestamp; }; - List<SampleInfo> mSampleInfos; + List<SampleInfo> mSampleInfos; + bool mSamplesHaveSameSize; + + List<MediaBuffer *> mChunkSamples; + List<off_t> mChunkOffsets; + + struct StscTableEntry { + + StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id) + : firstChunk(chunk), + samplesPerChunk(samples), + sampleDescriptionId(id) {} + + uint32_t firstChunk; + uint32_t samplesPerChunk; + uint32_t sampleDescriptionId; + }; + List<StscTableEntry> mStscTableEntries; + + List<int32_t> mStssTableEntries; + + struct SttsTableEntry { + + SttsTableEntry(uint32_t count, uint32_t duration) + : sampleCount(count), sampleDuration(duration) {} + + uint32_t sampleCount; + uint32_t sampleDuration; + }; + List<SttsTableEntry> mSttsTableEntries; void *mCodecSpecificData; size_t mCodecSpecificDataSize; + bool mGotAllCodecSpecificData; bool mReachedEOS; static void *ThreadWrapper(void *me); void threadEntry(); + status_t makeAVCCodecSpecificData( + const uint8_t *data, size_t size); + void writeOneChunk(bool isAvc); + Track(const Track &); Track &operator=(const Track &); }; +#define USE_NALLEN_FOUR 1 + MPEG4Writer::MPEG4Writer(const char *filename) : mFile(fopen(filename, "wb")), mOffset(0), - mMdatOffset(0) { + mMdatOffset(0), + mEstimatedMoovBoxSize(0), + mInterleaveDurationUs(500000) { CHECK(mFile != NULL); } MPEG4Writer::MPEG4Writer(int fd) : mFile(fdopen(fd, "wb")), mOffset(0), - mMdatOffset(0) { + mMdatOffset(0), + mEstimatedMoovBoxSize(0), + mInterleaveDurationUs(500000) { CHECK(mFile != NULL); } @@ -105,15 +149,33 @@ status_t MPEG4Writer::start() { return UNKNOWN_ERROR; } + mStreamableFile = true; + mWriteMoovBoxToMemory = false; + mMoovBoxBuffer = NULL; + mMoovBoxBufferOffset = 0; + beginBox("ftyp"); writeFourcc("isom"); writeInt32(0); writeFourcc("isom"); endBox(); - mMdatOffset = mOffset; - write("\x00\x00\x00\x01mdat????????", 16); + mFreeBoxOffset = mOffset; + if (mEstimatedMoovBoxSize == 0) { + // XXX: Estimate the moov box size + // based on max file size or duration limit + mEstimatedMoovBoxSize = 0x0F00; + } + CHECK(mEstimatedMoovBoxSize >= 8); + fseeko(mFile, mFreeBoxOffset, SEEK_SET); + writeInt32(mEstimatedMoovBoxSize); + write("free", 4); + + mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize; + mOffset = mMdatOffset; + fseeko(mFile, mMdatOffset, SEEK_SET); + write("\x00\x00\x00\x01mdat????????", 16); for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { status_t err = (*it)->start(); @@ -147,14 +209,20 @@ void MPEG4Writer::stop() { } } + // Fix up the size of the 'mdat' chunk. - fseek(mFile, mMdatOffset + 8, SEEK_SET); + fseeko(mFile, mMdatOffset + 8, SEEK_SET); int64_t size = mOffset - mMdatOffset; size = hton64(size); fwrite(&size, 1, 8, mFile); - fseek(mFile, mOffset, SEEK_SET); + fseeko(mFile, mOffset, SEEK_SET); time_t now = time(NULL); + const off_t moovOffset = mOffset; + mWriteMoovBoxToMemory = true; + mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); + mMoovBoxBufferOffset = 0; + CHECK(mMoovBoxBuffer != NULL); beginBox("moov"); @@ -194,15 +262,48 @@ void MPEG4Writer::stop() { } endBox(); // moov + mWriteMoovBoxToMemory = false; + if (mStreamableFile) { + CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize); + + // Moov box + fseeko(mFile, mFreeBoxOffset, SEEK_SET); + mOffset = mFreeBoxOffset; + write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile); + + // Free box + mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset; + fseeko(mFile, mFreeBoxOffset, SEEK_SET); + writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset); + write("free", 4); + + // Free temp memory + free(mMoovBoxBuffer); + mMoovBoxBuffer = NULL; + mMoovBoxBufferOffset = 0; + } + CHECK(mBoxes.empty()); + fflush(mFile); fclose(mFile); mFile = NULL; } -off_t MPEG4Writer::addSample(MediaBuffer *buffer) { - Mutex::Autolock autoLock(mLock); +status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) { + mInterleaveDurationUs = durationUs; + return OK; +} + +void MPEG4Writer::lock() { + mLock.lock(); +} + +void MPEG4Writer::unlock() { + mLock.unlock(); +} +off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) { off_t old_offset = mOffset; fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), @@ -213,31 +314,92 @@ off_t MPEG4Writer::addSample(MediaBuffer *buffer) { return old_offset; } -off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) { - Mutex::Autolock autoLock(mLock); +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_l(MediaBuffer *buffer) { + 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; } +size_t MPEG4Writer::write( + const void *ptr, size_t size, size_t nmemb, FILE *stream) { + + const size_t bytes = size * nmemb; + if (mWriteMoovBoxToMemory) { + if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) { + for (List<off_t>::iterator it = mBoxes.begin(); + it != mBoxes.end(); ++it) { + (*it) += mOffset; + } + fseeko(mFile, mOffset, SEEK_SET); + fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream); + fwrite(ptr, size, nmemb, stream); + mOffset += (bytes + mMoovBoxBufferOffset); + free(mMoovBoxBuffer); + mMoovBoxBuffer = NULL; + mMoovBoxBufferOffset = 0; + mWriteMoovBoxToMemory = false; + mStreamableFile = false; + } else { + memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes); + mMoovBoxBufferOffset += bytes; + } + } else { + fwrite(ptr, size, nmemb, stream); + mOffset += bytes; + } + return bytes; +} + void MPEG4Writer::beginBox(const char *fourcc) { CHECK_EQ(strlen(fourcc), 4); - mBoxes.push_back(mOffset); + mBoxes.push_back(mWriteMoovBoxToMemory? + mMoovBoxBufferOffset: mOffset); writeInt32(0); writeFourcc(fourcc); @@ -249,51 +411,48 @@ void MPEG4Writer::endBox() { off_t offset = *--mBoxes.end(); mBoxes.erase(--mBoxes.end()); - fseek(mFile, offset, SEEK_SET); - writeInt32(mOffset - offset); - mOffset -= 4; - fseek(mFile, mOffset, SEEK_SET); + if (mWriteMoovBoxToMemory) { + int32_t x = htonl(mMoovBoxBufferOffset - offset); + memcpy(mMoovBoxBuffer + offset, &x, 4); + } else { + fseeko(mFile, offset, SEEK_SET); + writeInt32(mOffset - offset); + mOffset -= 4; + fseeko(mFile, mOffset, SEEK_SET); + } } void MPEG4Writer::writeInt8(int8_t x) { - fwrite(&x, 1, 1, mFile); - ++mOffset; + write(&x, 1, 1, mFile); } void MPEG4Writer::writeInt16(int16_t x) { x = htons(x); - fwrite(&x, 1, 2, mFile); - mOffset += 2; + write(&x, 1, 2, mFile); } void MPEG4Writer::writeInt32(int32_t x) { x = htonl(x); - fwrite(&x, 1, 4, mFile); - mOffset += 4; + write(&x, 1, 4, mFile); } void MPEG4Writer::writeInt64(int64_t x) { x = hton64(x); - fwrite(&x, 1, 8, mFile); - mOffset += 8; + write(&x, 1, 8, mFile); } void MPEG4Writer::writeCString(const char *s) { size_t n = strlen(s); - - fwrite(s, 1, n + 1, mFile); - mOffset += n + 1; + write(s, 1, n + 1, mFile); } void MPEG4Writer::writeFourcc(const char *s) { CHECK_EQ(strlen(s), 4); - fwrite(s, 1, 4, mFile); - mOffset += 4; + write(s, 1, 4, mFile); } void MPEG4Writer::write(const void *data, size_t size) { - fwrite(data, 1, size, mFile); - mOffset += size; + write(data, 1, size, mFile); } bool MPEG4Writer::reachedEOS() { @@ -318,8 +477,10 @@ MPEG4Writer::Track::Track( mSource(source), mDone(false), mMaxTimeStampUs(0), + mSamplesHaveSameSize(true), mCodecSpecificData(NULL), mCodecSpecificDataSize(0), + mGotAllCodecSpecificData(false), mReachedEOS(false) { } @@ -380,73 +541,162 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) { return NULL; } +#include <ctype.h> +static void hexdump(const void *_data, size_t size) { + const uint8_t *data = (const uint8_t *)_data; + size_t offset = 0; + while (offset < size) { + printf("0x%04x ", offset); + + size_t n = size - offset; + if (n > 16) { + n = 16; + } + + for (size_t i = 0; i < 16; ++i) { + if (i == 8) { + printf(" "); + } + + if (offset + i < size) { + printf("%02x ", data[offset + i]); + } else { + printf(" "); + } + } + + printf(" "); + + for (size_t i = 0; i < n; ++i) { + if (isprint(data[offset + i])) { + printf("%c", data[offset + i]); + } else { + printf("."); + } + } + + printf("\n"); + + offset += 16; + } +} + + +status_t MPEG4Writer::Track::makeAVCCodecSpecificData( + const uint8_t *data, size_t size) { + // hexdump(data, size); + + if (mCodecSpecificData != NULL) { + LOGE("Already have codec specific data"); + return ERROR_MALFORMED; + } + + if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { + LOGE("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) { + LOGE("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<MetaData> meta = mSource->getFormat(); const char *mime; meta->findCString(kKeyMIMEType, &mime); - bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4); + bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || + !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); int32_t count = 0; + const int64_t interleaveDurationUs = mOwner->interleaveDuration(); + int64_t chunkTimestampUs = 0; + int32_t nChunks = 0; + int32_t nZeroLengthFrames = 0; + int64_t lastTimestamp = 0; // Timestamp of the previous sample + int64_t lastDuration = 0; // Time spacing between the previous two samples + int32_t sampleCount = 1; // Sample count in the current stts table entry + uint32_t previousSampleSize = 0; // Size of the previous sample MediaBuffer *buffer; while (!mDone && mSource->read(&buffer) == OK) { if (buffer->range_length() == 0) { buffer->release(); buffer = NULL; - + ++nZeroLengthFrames; continue; } ++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; - 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); - break; - } + int32_t isCodecConfig; + if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig) + && isCodecConfig) { + CHECK(!mGotAllCodecSpecificData); + + if (is_avc) { + status_t err = makeAVCCodecSpecificData( + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); + CHECK_EQ(OK, err); + } else if (is_mpeg4) { + mCodecSpecificDataSize = buffer->range_length(); + mCodecSpecificData = malloc(mCodecSpecificDataSize); + memcpy(mCodecSpecificData, + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); } buffer->release(); buffer = NULL; + mGotAllCodecSpecificData = true; continue; - } + } else if (!mGotAllCodecSpecificData && + 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,14 +724,67 @@ void MPEG4Writer::Track::threadEntry() { memcpy(mCodecSpecificData, data, offset); buffer->set_range(buffer->range_offset() + offset, size - offset); - } - off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer) - : mOwner->addSample(buffer); + if (size == offset) { + buffer->release(); + buffer = NULL; + + continue; + } + + mGotAllCodecSpecificData = true; + } else if (!mGotAllCodecSpecificData && 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; + CHECK_EQ(OK, err); + + mGotAllCodecSpecificData = true; + } + + continue; + } SampleInfo info; - info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length(); - info.offset = offset; + info.size = is_avc +#if USE_NALLEN_FOUR + ? buffer->range_length() + 4 +#else + ? buffer->range_length() + 2 +#endif + : buffer->range_length(); + + bool is_audio = !strncasecmp(mime, "audio/", 6); int64_t timestampUs; CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); @@ -492,14 +795,109 @@ void MPEG4Writer::Track::threadEntry() { // Our timestamp is in ms. info.timestamp = (timestampUs + 500) / 1000; - mSampleInfos.push_back(info); + if (mSampleInfos.size() > 2) { + if (lastDuration != info.timestamp - lastTimestamp) { + SttsTableEntry sttsEntry(sampleCount, lastDuration); + mSttsTableEntries.push_back(sttsEntry); + sampleCount = 1; + } else { + ++sampleCount; + } + } + if (mSamplesHaveSameSize) { + if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) { + mSamplesHaveSameSize = false; + } + previousSampleSize = info.size; + } + lastDuration = info.timestamp - lastTimestamp; + lastTimestamp = info.timestamp; + +//////////////////////////////////////////////////////////////////////////////// + // Make a deep copy of the MediaBuffer less Metadata + MediaBuffer *copy = new MediaBuffer(buffer->range_length()); + memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length()); + copy->set_range(0, buffer->range_length()); + + mChunkSamples.push_back(copy); + if (interleaveDurationUs == 0) { + StscTableEntry stscEntry(++nChunks, 1, 1); + mStscTableEntries.push_back(stscEntry); + writeOneChunk(is_avc); + } else { + if (chunkTimestampUs == 0) { + chunkTimestampUs = timestampUs; + } else { + if (timestampUs - chunkTimestampUs > interleaveDurationUs) { + ++nChunks; + if (nChunks == 1 || // First chunk + (--(mStscTableEntries.end()))->samplesPerChunk != + mChunkSamples.size()) { + StscTableEntry stscEntry(nChunks, + mChunkSamples.size(), 1); + mStscTableEntries.push_back(stscEntry); + } + writeOneChunk(is_avc); + chunkTimestampUs = timestampUs; + } + } + } + + int32_t isSync = false; + if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync) && + isSync != 0) { + mStssTableEntries.push_back(mSampleInfos.size()); + } buffer->release(); buffer = NULL; } + CHECK(!mSampleInfos.empty()); + + // Last chunk + if (!mChunkSamples.empty()) { + ++nChunks; + StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); + mStscTableEntries.push_back(stscEntry); + writeOneChunk(is_avc); + } + + // 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. + if (mSampleInfos.size() == 1) { + lastDuration = 0; // A single sample's duration + } else { + ++sampleCount; // Count for the last sample + } + SttsTableEntry sttsEntry(sampleCount, lastDuration); + mSttsTableEntries.push_back(sttsEntry); mReachedEOS = true; + LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames", + count, nZeroLengthFrames, mSampleInfos.size()); +} + +void MPEG4Writer::Track::writeOneChunk(bool isAvc) { + mOwner->lock(); + for (List<MediaBuffer *>::iterator it = mChunkSamples.begin(); + it != mChunkSamples.end(); ++it) { + off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it) + : mOwner->addSample_l(*it); + if (it == mChunkSamples.begin()) { + mChunkOffsets.push_back(offset); + } + } + mOwner->unlock(); + while (!mChunkSamples.empty()) { + List<MediaBuffer *>::iterator it = mChunkSamples.begin(); + (*it)->release(); + (*it) = NULL; + mChunkSamples.erase(it); + } + mChunkSamples.clear(); } int64_t MPEG4Writer::Track::getDurationUs() const { @@ -550,8 +948,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { success = success && mMeta->findInt32(kKeyHeight, &height); CHECK(success); - mOwner->writeInt32(width); - mOwner->writeInt32(height); + mOwner->writeInt32(width << 16); // 32-bit fixed-point value + mOwner->writeInt32(height << 16); // 32-bit fixed-point value } mOwner->endBox(); // tkhd @@ -569,26 +967,15 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox("hdlr"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(0); // predefined - mOwner->writeFourcc(is_audio ? "soun" : "vide"); + mOwner->writeInt32(0); // component type: should be mhlr + mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved - mOwner->writeCString(""); // name + mOwner->writeCString("SoundHandler"); // name mOwner->endBox(); mOwner->beginBox("minf"); - - mOwner->beginBox("dinf"); - mOwner->beginBox("dref"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(1); - mOwner->beginBox("url "); - mOwner->writeInt32(1); // version=0, flags=1 - mOwner->endBox(); // url - mOwner->endBox(); // dref - mOwner->endBox(); // dinf - if (is_audio) { mOwner->beginBox("smhd"); mOwner->writeInt32(0); // version=0, flags=0 @@ -604,7 +991,18 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->writeInt16(0); mOwner->endBox(); } - mOwner->endBox(); // minf + + mOwner->beginBox("dinf"); + mOwner->beginBox("dref"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(1); + mOwner->beginBox("url "); + mOwner->writeInt32(1); // version=0, flags=1 + mOwner->endBox(); // url + mOwner->endBox(); // dref + mOwner->endBox(); // dinf + + mOwner->endBox(); // minf mOwner->beginBox("stbl"); @@ -617,6 +1015,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { fourcc = "samr"; } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { fourcc = "sawb"; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { + fourcc = "mp4a"; } else { LOGE("Unknown mime type '%s'.", mime); CHECK(!"should not be here, unknown mime type."); @@ -625,10 +1025,12 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox(fourcc); // audio format mOwner->writeInt32(0); // reserved mOwner->writeInt16(0); // reserved - mOwner->writeInt16(0); // data ref index + mOwner->writeInt16(0x1); // data ref index mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved - mOwner->writeInt16(2); // channel count + int32_t nChannels; + CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels)); + mOwner->writeInt16(nChannels); // channel count mOwner->writeInt16(16); // sample size mOwner->writeInt16(0); // predefined mOwner->writeInt16(0); // reserved @@ -638,6 +1040,38 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { CHECK(success); mOwner->writeInt32(samplerate << 16); + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { + mOwner->beginBox("esds"); + + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt8(0x03); // ES_DescrTag + mOwner->writeInt8(23 + mCodecSpecificDataSize); + mOwner->writeInt16(0x0000);// ES_ID + mOwner->writeInt8(0x00); + + mOwner->writeInt8(0x04); // DecoderConfigDescrTag + mOwner->writeInt8(15 + mCodecSpecificDataSize); + mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 + mOwner->writeInt8(0x15); // streamType AudioStream + + mOwner->writeInt16(0x03); // XXX + mOwner->writeInt8(0x00); // buffer size 24-bit + mOwner->writeInt32(96000); // max bit rate + mOwner->writeInt32(96000); // avg bit rate + + mOwner->writeInt8(0x05); // DecoderSpecificInfoTag + mOwner->writeInt8(mCodecSpecificDataSize); + mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); + + static const uint8_t kData2[] = { + 0x06, // SLConfigDescriptorTag + 0x01, + 0x02 + }; + mOwner->write(kData2, sizeof(kData2)); + + mOwner->endBox(); // esds + } mOwner->endBox(); } else { if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { @@ -698,7 +1132,7 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { 0x00, 0x03, 0xe8, 0x00 }; mOwner->write(kData, sizeof(kData)); - + mOwner->writeInt8(0x05); // DecoderSpecificInfoTag mOwner->writeInt8(mCodecSpecificDataSize); @@ -733,49 +1167,59 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox("stts"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size() - 1); - - List<SampleInfo>::iterator it = mSampleInfos.begin(); - int64_t last = (*it).timestamp; - ++it; - while (it != mSampleInfos.end()) { - mOwner->writeInt32(1); - mOwner->writeInt32((*it).timestamp - last); - - last = (*it).timestamp; - - ++it; + mOwner->writeInt32(mSttsTableEntries.size()); + for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); + it != mSttsTableEntries.end(); ++it) { + mOwner->writeInt32(it->sampleCount); + mOwner->writeInt32(it->sampleDuration); } mOwner->endBox(); // stts + if (!is_audio) { + mOwner->beginBox("stss"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames + for (List<int32_t>::iterator it = mStssTableEntries.begin(); + it != mStssTableEntries.end(); ++it) { + mOwner->writeInt32(*it); + } + mOwner->endBox(); // stss + } + mOwner->beginBox("stsz"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(0); // default sample size + if (mSamplesHaveSameSize) { + List<SampleInfo>::iterator it = mSampleInfos.begin(); + mOwner->writeInt32(it->size); // default sample size + } else { + mOwner->writeInt32(0); + } mOwner->writeInt32(mSampleInfos.size()); - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it) { - mOwner->writeInt32((*it).size); + if (!mSamplesHaveSameSize) { + for (List<SampleInfo>::iterator it = mSampleInfos.begin(); + it != mSampleInfos.end(); ++it) { + mOwner->writeInt32((*it).size); + } } mOwner->endBox(); // stsz mOwner->beginBox("stsc"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size()); - int32_t n = 1; - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it, ++n) { - mOwner->writeInt32(n); - mOwner->writeInt32(1); - mOwner->writeInt32(1); + mOwner->writeInt32(mStscTableEntries.size()); + for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); + it != mStscTableEntries.end(); ++it) { + mOwner->writeInt32(it->firstChunk); + mOwner->writeInt32(it->samplesPerChunk); + mOwner->writeInt32(it->sampleDescriptionId); } mOwner->endBox(); // stsc mOwner->beginBox("co64"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size()); - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it, ++n) { - mOwner->writeInt64((*it).offset); + mOwner->writeInt32(mChunkOffsets.size()); + for (List<off_t>::iterator it = mChunkOffsets.begin(); + it != mChunkOffsets.end(); ++it) { + mOwner->writeInt64((*it)); } mOwner->endBox(); // co64 diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 69da7ef..ce4dd32 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -141,6 +141,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" }, }; @@ -523,6 +524,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { setAACFormat(numChannels, sampleRate); } + if (!strncasecmp(mMIME, "video/", 6)) { int32_t width, height; bool success = meta->findInt32(kKeyWidth, &width); @@ -570,7 +572,8 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { } if (!strcmp(mComponentName, "OMX.TI.AMR.encode") - || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")) { + || !strcmp(mComponentName, "OMX.TI.WBAMR.encode") + || !strcmp(mComponentName, "OMX.TI.AAC.encode")) { setMinBufferSize(kPortIndexOutput, 8192); // XXX } @@ -683,6 +686,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; @@ -713,61 +717,62 @@ void OMXCodec::setVideoInputFormat( colorFormat = OMX_COLOR_FormatYCbYCr; } - CHECK_EQ(setVideoPortFormatType( - kPortIndexInput, OMX_VIDEO_CodingUnused, - colorFormat), OK); - CHECK_EQ(setVideoPortFormatType( - kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused), - OK); + status_t err; OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - def.nPortIndex = kPortIndexOutput; - OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; - status_t err = mOMX->getParameter( - mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + //////////////////////// Input port ///////////////////////// + CHECK_EQ(setVideoPortFormatType( + kPortIndexInput, OMX_VIDEO_CodingUnused, + colorFormat), OK); + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); + + def.nBufferSize = getFrameSize(colorFormat, width, height); + CHECK_EQ(def.eDomain, OMX_PortDomainVideo); video_def->nFrameWidth = width; video_def->nFrameHeight = height; + video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; + video_def->eColorFormat = colorFormat; - video_def->eCompressionFormat = compressionFormat; - video_def->eColorFormat = OMX_COLOR_FormatUnused; + video_def->xFramerate = (24 << 16); // Q16 format err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); - //////////////////////////////////////////////////////////////////////////// - + //////////////////////// Output port ///////////////////////// + CHECK_EQ(setVideoPortFormatType( + kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused), + OK); InitOMXParams(&def); - def.nPortIndex = kPortIndexInput; + def.nPortIndex = kPortIndexOutput; err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); - CHECK_EQ(err, OK); - - def.nBufferSize = getFrameSize(colorFormat, width, height); - CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize); + CHECK_EQ(err, OK); CHECK_EQ(def.eDomain, OMX_PortDomainVideo); video_def->nFrameWidth = width; video_def->nFrameHeight = height; - video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; - video_def->eColorFormat = colorFormat; - video_def->xFramerate = 24 << 16; // XXX crucial! + video_def->eCompressionFormat = compressionFormat; + video_def->eColorFormat = OMX_COLOR_FormatUnused; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); + /////////////////// Codec-specific //////////////////////// switch (compressionFormat) { case OMX_VIDEO_CodingMPEG4: { @@ -915,7 +920,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, @@ -2128,11 +2133,24 @@ void OMXCodec::setState(State newState) { void OMXCodec::setRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) { + + // port definition + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = portIndex; + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + CHECK_EQ(err, OK); + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, + &def, sizeof(def)), OK); + + // pcm param OMX_AUDIO_PARAM_PCMMODETYPE pcmParams; InitOMXParams(&pcmParams); pcmParams.nPortIndex = portIndex; - status_t err = mOMX->getParameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); CHECK_EQ(err, OK); @@ -2172,6 +2190,8 @@ void OMXCodec::setAMRFormat(bool isWAMR) { CHECK_EQ(err, OK); def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + // XXX: Select bandmode based on bit rate def.eAMRBandMode = isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0; @@ -2192,8 +2212,60 @@ void OMXCodec::setAMRFormat(bool isWAMR) { } void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate) { + CHECK(numChannels == 1 || numChannels == 2); if (mIsEncoder) { + //////////////// input port //////////////////// setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); + + //////////////// output port //////////////////// + // format + OMX_AUDIO_PARAM_PORTFORMATTYPE format; + format.nPortIndex = kPortIndexOutput; + format.nIndex = 0; + status_t err = OMX_ErrorNone; + while (OMX_ErrorNone == err) { + CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat, + &format, sizeof(format)), OK); + if (format.eEncoding == OMX_AUDIO_CodingAAC) { + break; + } + format.nIndex++; + } + CHECK_EQ(OK, err); + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat, + &format, sizeof(format)), OK); + + // port definition + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexOutput; + CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, + &def, sizeof(def)), OK); + def.format.audio.bFlagErrorConcealment = OMX_TRUE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, + &def, sizeof(def)), OK); + + // profile + OMX_AUDIO_PARAM_AACPROFILETYPE profile; + InitOMXParams(&profile); + profile.nPortIndex = kPortIndexOutput; + CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac, + &profile, sizeof(profile)), OK); + profile.nChannels = numChannels; + profile.eChannelMode = (numChannels == 1? + OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo); + profile.nSampleRate = sampleRate; + profile.nBitRate = 96000; // XXX + profile.nAudioBandWidth = 0; + profile.nFrameLength = 0; + profile.nAACtools = OMX_AUDIO_AACToolAll; + profile.nAACERtools = OMX_AUDIO_AACERNone; + profile.eAACProfile = OMX_AUDIO_AACObjectLC; + profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioAac, + &profile, sizeof(profile)), OK); + } else { OMX_AUDIO_PARAM_AACPROFILETYPE profile; InitOMXParams(&profile); @@ -2962,6 +3034,11 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { } else if (audio_def->eEncoding == OMX_AUDIO_CodingAAC) { mOutputFormat->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + int32_t numChannels, sampleRate; + inputFormat->findInt32(kKeyChannelCount, &numChannels); + inputFormat->findInt32(kKeySampleRate, &sampleRate); + mOutputFormat->setInt32(kKeyChannelCount, numChannels); + mOutputFormat->setInt32(kKeySampleRate, sampleRate); } else { CHECK(!"Should not be here. Unknown audio encoding."); } 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); |