diff options
Diffstat (limited to 'media/libstagefright')
38 files changed, 1329 insertions, 769 deletions
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index e9d10cd..ef21c9a 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -498,6 +498,10 @@ void ACodec::initiateShutdown(bool keepComponentAllocated) { sp<AMessage> msg = new AMessage(kWhatShutdown, id()); msg->setInt32("keepComponentAllocated", keepComponentAllocated); msg->post(); + if (!keepComponentAllocated) { + // ensure shutdown completes in 3 seconds + (new AMessage(kWhatReleaseCodecInstance, id()))->post(3000000); + } } void ACodec::signalRequestIDRFrame() { @@ -1153,7 +1157,7 @@ status_t ACodec::configureCodec( } sp<AMessage> inputFormat = new AMessage(); - sp<AMessage> outputFormat = new AMessage(); + sp<AMessage> outputFormat = mNotify->dup(); // will use this for kWhatOutputFormatChanged mIsEncoder = encoder; @@ -1263,6 +1267,24 @@ status_t ACodec::configureCodec( static_cast<NativeWindowWrapper *>(obj.get())); sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow(); + // START of temporary support for automatic FRC - THIS WILL BE REMOVED + int32_t autoFrc; + if (msg->findInt32("auto-frc", &autoFrc)) { + bool enabled = autoFrc; + OMX_CONFIG_BOOLEANTYPE config; + InitOMXParams(&config); + config.bEnabled = (OMX_BOOL)enabled; + status_t temp = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigAutoFramerateConversion, + &config, sizeof(config)); + if (temp == OK) { + outputFormat->setInt32("auto-frc", enabled); + } else if (enabled) { + ALOGI("codec does not support requested auto-frc (err %d)", temp); + } + } + // END of temporary support for automatic FRC + int32_t tunneled; if (msg->findInt32("feature-tunneled-playback", &tunneled) && tunneled != 0) { @@ -1520,6 +1542,8 @@ status_t ACodec::configureCodec( err = setMinBufferSize(kPortIndexInput, 8192); // XXX } + mBaseOutputFormat = outputFormat; + CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK); CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK); mInputFormat = inputFormat; @@ -2216,7 +2240,11 @@ status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f); video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; - video_def->eColorFormat = colorFormat; + // this is redundant as it was already set up in setVideoPortFormatType + // FIXME for now skip this only for flexible YUV formats + if (colorFormat != OMX_COLOR_FormatYUV420Flexible) { + video_def->eColorFormat = colorFormat; + } err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); @@ -3505,7 +3533,7 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { } void ACodec::sendFormatChange(const sp<AMessage> &reply) { - sp<AMessage> notify = mNotify->dup(); + sp<AMessage> notify = mBaseOutputFormat->dup(); notify->setInt32("what", kWhatOutputFormatChanged); CHECK_EQ(getPortFormat(kPortIndexOutput, notify), (status_t)OK); @@ -3794,6 +3822,19 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { break; } + case ACodec::kWhatReleaseCodecInstance: + { + ALOGI("[%s] forcing the release of codec", + mCodec->mComponentName.c_str()); + status_t err = mCodec->mOMX->freeNode(mCodec->mNode); + ALOGE_IF("[%s] failed to release codec instance: err=%d", + mCodec->mComponentName.c_str(), err); + sp<AMessage> notify = mCodec->mNotify->dup(); + notify->setInt32("what", CodecBase::kWhatShutdownCompleted); + notify->post(); + break; + } + default: return false; } @@ -4453,6 +4494,13 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) { break; } + case ACodec::kWhatReleaseCodecInstance: + { + // nothing to do, as we have already signaled shutdown + handled = true; + break; + } + default: return BaseState::onMessageReceived(msg); } @@ -4599,6 +4647,7 @@ void ACodec::LoadedState::stateEntered() { mCodec->mRepeatFrameDelayUs = -1ll; mCodec->mInputFormat.clear(); mCodec->mOutputFormat.clear(); + mCodec->mBaseOutputFormat.clear(); if (mCodec->mShutdownInProgress) { bool keepComponentAllocated = mCodec->mKeepComponentAllocated; diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index ab8ac79..007c090 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -75,7 +75,7 @@ static const size_t kHighWaterMarkBytes = 200000; // maximum time in paused state when offloading audio decompression. When elapsed, the AudioPlayer // is destroyed to allow the audio DSP to power down. -static int64_t kOffloadPauseMaxUs = 60000000ll; +static int64_t kOffloadPauseMaxUs = 10000000ll; struct AwesomeEvent : public TimedEventQueue::Event { @@ -878,6 +878,16 @@ void AwesomePlayer::onStreamDone() { return; } + if (mFlags & AUTO_LOOPING) { + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + if (streamType == AUDIO_STREAM_NOTIFICATION) { + ALOGW("disabling auto-loop for notification"); + modifyFlags(AUTO_LOOPING, CLEAR); + } + } if ((mFlags & LOOPING) || ((mFlags & AUTO_LOOPING) && (mAudioSink == NULL || mAudioSink->realtime()))) { diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 853e8fc..ad12bdd 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -130,6 +130,7 @@ static int32_t getColorFormat(const char* colorFormat) { "CameraSource::getColorFormat", colorFormat); CHECK(!"Unknown color format"); + return -1; } CameraSource *CameraSource::Create(const String16 &clientName) { @@ -677,63 +678,80 @@ void CameraSource::stopCameraRecording() { void CameraSource::releaseCamera() { ALOGV("releaseCamera"); - if (mCamera != 0) { + sp<Camera> camera; + bool coldCamera = false; + { + Mutex::Autolock autoLock(mLock); + // get a local ref and clear ref to mCamera now + camera = mCamera; + mCamera.clear(); + coldCamera = (mCameraFlags & FLAGS_HOT_CAMERA) == 0; + } + + if (camera != 0) { int64_t token = IPCThreadState::self()->clearCallingIdentity(); - if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { + if (coldCamera) { ALOGV("Camera was cold when we started, stopping preview"); - mCamera->stopPreview(); - mCamera->disconnect(); + camera->stopPreview(); + camera->disconnect(); } - mCamera->unlock(); - mCamera.clear(); - mCamera = 0; + camera->unlock(); IPCThreadState::self()->restoreCallingIdentity(token); } - if (mCameraRecordingProxy != 0) { - IInterface::asBinder(mCameraRecordingProxy)->unlinkToDeath(mDeathNotifier); - mCameraRecordingProxy.clear(); + + { + Mutex::Autolock autoLock(mLock); + if (mCameraRecordingProxy != 0) { + IInterface::asBinder(mCameraRecordingProxy)->unlinkToDeath(mDeathNotifier); + mCameraRecordingProxy.clear(); + } + mCameraFlags = 0; } - mCameraFlags = 0; } status_t CameraSource::reset() { ALOGD("reset: E"); - Mutex::Autolock autoLock(mLock); - mStarted = false; - mFrameAvailableCondition.signal(); - int64_t token; - bool isTokenValid = false; - if (mCamera != 0) { - token = IPCThreadState::self()->clearCallingIdentity(); - isTokenValid = true; - } - releaseQueuedFrames(); - while (!mFramesBeingEncoded.empty()) { - if (NO_ERROR != - mFrameCompleteCondition.waitRelative(mLock, - mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { - ALOGW("Timed out waiting for outstanding frames being encoded: %zu", - mFramesBeingEncoded.size()); + { + Mutex::Autolock autoLock(mLock); + mStarted = false; + mFrameAvailableCondition.signal(); + + int64_t token; + bool isTokenValid = false; + if (mCamera != 0) { + token = IPCThreadState::self()->clearCallingIdentity(); + isTokenValid = true; + } + releaseQueuedFrames(); + while (!mFramesBeingEncoded.empty()) { + if (NO_ERROR != + mFrameCompleteCondition.waitRelative(mLock, + mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { + ALOGW("Timed out waiting for outstanding frames being encoded: %zu", + mFramesBeingEncoded.size()); + } + } + stopCameraRecording(); + if (isTokenValid) { + IPCThreadState::self()->restoreCallingIdentity(token); } - } - stopCameraRecording(); - releaseCamera(); - if (isTokenValid) { - IPCThreadState::self()->restoreCallingIdentity(token); - } - if (mCollectStats) { - ALOGI("Frames received/encoded/dropped: %d/%d/%d in %" PRId64 " us", - mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, - mLastFrameTimestampUs - mFirstFrameTimeUs); - } + if (mCollectStats) { + ALOGI("Frames received/encoded/dropped: %d/%d/%d in %" PRId64 " us", + mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, + mLastFrameTimestampUs - mFirstFrameTimeUs); + } - if (mNumGlitches > 0) { - ALOGW("%d long delays between neighboring video frames", mNumGlitches); + if (mNumGlitches > 0) { + ALOGW("%d long delays between neighboring video frames", mNumGlitches); + } + + CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); } - CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); + releaseCamera(); + ALOGD("reset: X"); return OK; } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index f6525ae..9f20b1d 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -41,6 +41,11 @@ #include "include/ESDS.h" + +#ifndef __predict_false +#define __predict_false(exp) __builtin_expect((exp) != 0, 0) +#endif + #define WARN_UNLESS(condition, message, ...) \ ( (__predict_false(condition)) ? false : ({ \ ALOGW("Condition %s failed " message, #condition, ##__VA_ARGS__); \ diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 5f55484..e1c8a41 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -738,6 +738,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { err, actionCode, mState); if (err == DEAD_OBJECT) { mFlags |= kFlagSawMediaServerDie; + mFlags &= ~kFlagIsComponentAllocated; } bool sendErrorResponse = true; @@ -863,6 +864,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { { CHECK_EQ(mState, INITIALIZING); setState(INITIALIZED); + mFlags |= kFlagIsComponentAllocated; CHECK(msg->findString("componentName", &mComponentName)); @@ -1009,6 +1011,16 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { mFlags |= kFlagOutputFormatChanged; postActivityNotificationIfPossible(); } + + // Notify mCrypto of video resolution changes + if (mCrypto != NULL) { + int32_t height, width; + if (mOutputFormat->findInt32("height", &height) && + mOutputFormat->findInt32("width", &width)) { + mCrypto->notifyResolution(width, height); + } + } + break; } @@ -1136,6 +1148,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { setState(UNINITIALIZED); mComponentName.clear(); } + mFlags &= ~kFlagIsComponentAllocated; (new AMessage)->postReply(mReplyID); break; @@ -1313,8 +1326,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->senderAwaitsResponse(&replyID)); if (mState == FLUSHED) { + setState(STARTED); mCodec->signalResume(); PostReplyWithError(replyID, OK); + break; } else if (mState != CONFIGURED) { PostReplyWithError(replyID, INVALID_OPERATION); break; @@ -1336,9 +1351,13 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - if (mState != INITIALIZED + if (!((mFlags & kFlagIsComponentAllocated) && targetState == UNINITIALIZED) // See 1 + && mState != INITIALIZED && mState != CONFIGURED && !isExecuting()) { - // We may be in "UNINITIALIZED" state already without the + // 1) Permit release to shut down the component if allocated. + // + // 2) We may be in "UNINITIALIZED" state already and + // also shutdown the encoder/decoder without the // client being aware of this if media server died while // we were being stopped. The client would assume that // after stop() returned, it would be safe to call release() diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index b9466da..c26e909 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -422,19 +422,11 @@ status_t MediaCodecSource::initEncoder() { } } - err = mEncoder->start(); - - if (err != OK) { - return err; - } - - err = mEncoder->getInputBuffers(&mEncoderInputBuffers); + mEncoderActivityNotify = new AMessage( + kWhatEncoderActivity, mReflector->id()); + mEncoder->setCallback(mEncoderActivityNotify); - if (err != OK) { - return err; - } - - err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + err = mEncoder->start(); if (err != OK) { return err; @@ -461,14 +453,6 @@ void MediaCodecSource::releaseEncoder() { mbuf->release(); } } - - for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) { - sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i); - accessUnit->setMediaBufferBase(NULL); - } - - mEncoderInputBuffers.clear(); - mEncoderOutputBuffers.clear(); } status_t MediaCodecSource::postSynchronouslyAndReturnError( @@ -539,20 +523,6 @@ void MediaCodecSource::resume(int64_t skipFramesBeforeUs) { } } -void MediaCodecSource::scheduleDoMoreWork() { - if (mDoMoreWorkPending) { - return; - } - - mDoMoreWorkPending = true; - - if (mEncoderActivityNotify == NULL) { - mEncoderActivityNotify = new AMessage( - kWhatEncoderActivity, mReflector->id()); - } - mEncoder->requestActivityNotification(mEncoderActivityNotify); -} - status_t MediaCodecSource::feedEncoderInputBuffers() { while (!mInputBufferQueue.empty() && !mAvailEncoderInputIndices.empty()) { @@ -587,16 +557,22 @@ status_t MediaCodecSource::feedEncoderInputBuffers() { #endif // DEBUG_DRIFT_TIME } + sp<ABuffer> inbuf; + status_t err = mEncoder->getInputBuffer(bufferIndex, &inbuf); + if (err != OK || inbuf == NULL) { + mbuf->release(); + signalEOS(); + break; + } + size = mbuf->size(); - memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(), - mbuf->data(), size); + memcpy(inbuf->data(), mbuf->data(), size); if (mIsVideo) { // video encoder will release MediaBuffer when done // with underlying data. - mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase( - mbuf); + inbuf->setMediaBufferBase(mbuf); } else { mbuf->release(); } @@ -615,112 +591,6 @@ status_t MediaCodecSource::feedEncoderInputBuffers() { return OK; } -status_t MediaCodecSource::doMoreWork(int32_t numInput, int32_t numOutput) { - status_t err = OK; - - if (!(mFlags & FLAG_USE_SURFACE_INPUT)) { - while (numInput-- > 0) { - size_t bufferIndex; - err = mEncoder->dequeueInputBuffer(&bufferIndex); - - if (err != OK) { - break; - } - - mAvailEncoderInputIndices.push_back(bufferIndex); - } - - feedEncoderInputBuffers(); - } - - while (numOutput-- > 0) { - size_t bufferIndex; - size_t offset; - size_t size; - int64_t timeUs; - uint32_t flags; - err = mEncoder->dequeueOutputBuffer( - &bufferIndex, &offset, &size, &timeUs, &flags); - - if (err != OK) { - if (err == INFO_FORMAT_CHANGED) { - continue; - } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { - mEncoder->getOutputBuffers(&mEncoderOutputBuffers); - continue; - } - - if (err == -EAGAIN) { - err = OK; - } - break; - } - if (!(flags & MediaCodec::BUFFER_FLAG_EOS)) { - sp<ABuffer> outbuf = mEncoderOutputBuffers.itemAt(bufferIndex); - - MediaBuffer *mbuf = new MediaBuffer(outbuf->size()); - memcpy(mbuf->data(), outbuf->data(), outbuf->size()); - - if (!(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) { - if (mIsVideo) { - int64_t decodingTimeUs; - if (mFlags & FLAG_USE_SURFACE_INPUT) { - // GraphicBufferSource is supposed to discard samples - // queued before start, and offset timeUs by start time - CHECK_GE(timeUs, 0ll); - // TODO: - // Decoding time for surface source is unavailable, - // use presentation time for now. May need to move - // this logic into MediaCodec. - decodingTimeUs = timeUs; - } else { - CHECK(!mDecodingTimeQueue.empty()); - decodingTimeUs = *(mDecodingTimeQueue.begin()); - mDecodingTimeQueue.erase(mDecodingTimeQueue.begin()); - } - mbuf->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs); - - ALOGV("[video] time %" PRId64 " us (%.2f secs), dts/pts diff %" PRId64, - timeUs, timeUs / 1E6, decodingTimeUs - timeUs); - } else { - int64_t driftTimeUs = 0; -#if DEBUG_DRIFT_TIME - CHECK(!mDriftTimeQueue.empty()); - driftTimeUs = *(mDriftTimeQueue.begin()); - mDriftTimeQueue.erase(mDriftTimeQueue.begin()); - mbuf->meta_data()->setInt64(kKeyDriftTime, driftTimeUs); -#endif // DEBUG_DRIFT_TIME - ALOGV("[audio] time %" PRId64 " us (%.2f secs), drift %" PRId64, - timeUs, timeUs / 1E6, driftTimeUs); - } - mbuf->meta_data()->setInt64(kKeyTime, timeUs); - } else { - mbuf->meta_data()->setInt32(kKeyIsCodecConfig, true); - } - if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) { - mbuf->meta_data()->setInt32(kKeyIsSyncFrame, true); - } - mbuf->setObserver(this); - mbuf->add_ref(); - - { - Mutex::Autolock autoLock(mOutputBufferLock); - mOutputBufferQueue.push_back(mbuf); - mOutputBufferCond.signal(); - } - } - - mEncoder->releaseOutputBuffer(bufferIndex); - - if (flags & MediaCodec::BUFFER_FLAG_EOS) { - err = ERROR_END_OF_STREAM; - break; - } - } - - return err; -} - status_t MediaCodecSource::onStart(MetaData *params) { if (mStopping) { ALOGE("Failed to start while we're stopping"); @@ -748,7 +618,6 @@ status_t MediaCodecSource::onStart(MetaData *params) { startTimeUs = -1ll; } resume(startTimeUs); - scheduleDoMoreWork(); } else { CHECK(mPuller != NULL); sp<AMessage> notify = new AMessage( @@ -792,37 +661,110 @@ void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) { mInputBufferQueue.push_back(mbuf); feedEncoderInputBuffers(); - scheduleDoMoreWork(); break; } case kWhatEncoderActivity: { - mDoMoreWorkPending = false; - if (mEncoder == NULL) { break; } - int32_t numInput, numOutput; + int32_t cbID; + CHECK(msg->findInt32("callbackID", &cbID)); + if (cbID == MediaCodec::CB_INPUT_AVAILABLE) { + int32_t index; + CHECK(msg->findInt32("index", &index)); + + mAvailEncoderInputIndices.push_back(index); + feedEncoderInputBuffers(); + } else if (cbID == MediaCodec::CB_OUTPUT_AVAILABLE) { + int32_t index; + size_t offset; + size_t size; + int64_t timeUs; + int32_t flags; + native_handle_t* handle = NULL; + + CHECK(msg->findInt32("index", &index)); + CHECK(msg->findSize("offset", &offset)); + CHECK(msg->findSize("size", &size)); + CHECK(msg->findInt64("timeUs", &timeUs)); + CHECK(msg->findInt32("flags", &flags)); + + if (flags & MediaCodec::BUFFER_FLAG_EOS) { + mEncoder->releaseOutputBuffer(index); + signalEOS(); + break; + } - if (!msg->findInt32("input-buffers", &numInput)) { - numInput = INT32_MAX; - } - if (!msg->findInt32("output-buffers", &numOutput)) { - numOutput = INT32_MAX; - } + sp<ABuffer> outbuf; + status_t err = mEncoder->getOutputBuffer(index, &outbuf); + if (err != OK || outbuf == NULL) { + signalEOS(); + break; + } - status_t err = doMoreWork(numInput, numOutput); + MediaBuffer *mbuf = new MediaBuffer(outbuf->size()); + memcpy(mbuf->data(), outbuf->data(), outbuf->size()); - if (err == OK) { - scheduleDoMoreWork(); - } else { - // reached EOS, or error - signalEOS(err); - } + if (!(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) { + if (mIsVideo) { + int64_t decodingTimeUs; + if (mFlags & FLAG_USE_SURFACE_INPUT) { + // GraphicBufferSource is supposed to discard samples + // queued before start, and offset timeUs by start time + CHECK_GE(timeUs, 0ll); + // TODO: + // Decoding time for surface source is unavailable, + // use presentation time for now. May need to move + // this logic into MediaCodec. + decodingTimeUs = timeUs; + } else { + CHECK(!mDecodingTimeQueue.empty()); + decodingTimeUs = *(mDecodingTimeQueue.begin()); + mDecodingTimeQueue.erase(mDecodingTimeQueue.begin()); + } + mbuf->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs); - break; + ALOGV("[video] time %" PRId64 " us (%.2f secs), dts/pts diff %" PRId64, + timeUs, timeUs / 1E6, decodingTimeUs - timeUs); + } else { + int64_t driftTimeUs = 0; +#if DEBUG_DRIFT_TIME + CHECK(!mDriftTimeQueue.empty()); + driftTimeUs = *(mDriftTimeQueue.begin()); + mDriftTimeQueue.erase(mDriftTimeQueue.begin()); + mbuf->meta_data()->setInt64(kKeyDriftTime, driftTimeUs); +#endif // DEBUG_DRIFT_TIME + ALOGV("[audio] time %" PRId64 " us (%.2f secs), drift %" PRId64, + timeUs, timeUs / 1E6, driftTimeUs); + } + mbuf->meta_data()->setInt64(kKeyTime, timeUs); + } else { + mbuf->meta_data()->setInt32(kKeyIsCodecConfig, true); + } + if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) { + mbuf->meta_data()->setInt32(kKeyIsSyncFrame, true); + } + mbuf->setObserver(this); + mbuf->add_ref(); + + { + Mutex::Autolock autoLock(mOutputBufferLock); + mOutputBufferQueue.push_back(mbuf); + mOutputBufferCond.signal(); + } + + mEncoder->releaseOutputBuffer(index); + } else if (cbID == MediaCodec::CB_ERROR) { + status_t err; + CHECK(msg->findInt32("err", &err)); + ALOGE("Encoder (%s) reported error : 0x%x", + mIsVideo ? "video" : "audio", err); + signalEOS(); + } + break; } case kWhatStart: { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 9c03c9a..69f4989 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -47,10 +47,11 @@ #include <media/stagefright/SkipCutBuffer.h> #include <utils/Vector.h> -#include <OMX_Audio.h> #include <OMX_AudioExt.h> #include <OMX_Component.h> #include <OMX_IndexExt.h> +#include <OMX_VideoExt.h> +#include <OMX_AsString.h> #include "include/avc_utils.h" @@ -817,6 +818,7 @@ static size_t getFrameSize( CHECK(!"Should not be here. Unsupported color format."); break; } + return 0; } status_t OMXCodec::findTargetColorFormat( @@ -4075,220 +4077,6 @@ void OMXCodec::signalBufferReturned(MediaBuffer *buffer) { CHECK(!"should not be here."); } -static const char *imageCompressionFormatString(OMX_IMAGE_CODINGTYPE type) { - static const char *kNames[] = { - "OMX_IMAGE_CodingUnused", - "OMX_IMAGE_CodingAutoDetect", - "OMX_IMAGE_CodingJPEG", - "OMX_IMAGE_CodingJPEG2K", - "OMX_IMAGE_CodingEXIF", - "OMX_IMAGE_CodingTIFF", - "OMX_IMAGE_CodingGIF", - "OMX_IMAGE_CodingPNG", - "OMX_IMAGE_CodingLZW", - "OMX_IMAGE_CodingBMP", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *colorFormatString(OMX_COLOR_FORMATTYPE type) { - static const char *kNames[] = { - "OMX_COLOR_FormatUnused", - "OMX_COLOR_FormatMonochrome", - "OMX_COLOR_Format8bitRGB332", - "OMX_COLOR_Format12bitRGB444", - "OMX_COLOR_Format16bitARGB4444", - "OMX_COLOR_Format16bitARGB1555", - "OMX_COLOR_Format16bitRGB565", - "OMX_COLOR_Format16bitBGR565", - "OMX_COLOR_Format18bitRGB666", - "OMX_COLOR_Format18bitARGB1665", - "OMX_COLOR_Format19bitARGB1666", - "OMX_COLOR_Format24bitRGB888", - "OMX_COLOR_Format24bitBGR888", - "OMX_COLOR_Format24bitARGB1887", - "OMX_COLOR_Format25bitARGB1888", - "OMX_COLOR_Format32bitBGRA8888", - "OMX_COLOR_Format32bitARGB8888", - "OMX_COLOR_FormatYUV411Planar", - "OMX_COLOR_FormatYUV411PackedPlanar", - "OMX_COLOR_FormatYUV420Planar", - "OMX_COLOR_FormatYUV420PackedPlanar", - "OMX_COLOR_FormatYUV420SemiPlanar", - "OMX_COLOR_FormatYUV422Planar", - "OMX_COLOR_FormatYUV422PackedPlanar", - "OMX_COLOR_FormatYUV422SemiPlanar", - "OMX_COLOR_FormatYCbYCr", - "OMX_COLOR_FormatYCrYCb", - "OMX_COLOR_FormatCbYCrY", - "OMX_COLOR_FormatCrYCbY", - "OMX_COLOR_FormatYUV444Interleaved", - "OMX_COLOR_FormatRawBayer8bit", - "OMX_COLOR_FormatRawBayer10bit", - "OMX_COLOR_FormatRawBayer8bitcompressed", - "OMX_COLOR_FormatL2", - "OMX_COLOR_FormatL4", - "OMX_COLOR_FormatL8", - "OMX_COLOR_FormatL16", - "OMX_COLOR_FormatL24", - "OMX_COLOR_FormatL32", - "OMX_COLOR_FormatYUV420PackedSemiPlanar", - "OMX_COLOR_FormatYUV422PackedSemiPlanar", - "OMX_COLOR_Format18BitBGR666", - "OMX_COLOR_Format24BitARGB6666", - "OMX_COLOR_Format24BitABGR6666", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) { - return "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar"; - } else if (type == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) { - return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar"; - } else if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *videoCompressionFormatString(OMX_VIDEO_CODINGTYPE type) { - static const char *kNames[] = { - "OMX_VIDEO_CodingUnused", - "OMX_VIDEO_CodingAutoDetect", - "OMX_VIDEO_CodingMPEG2", - "OMX_VIDEO_CodingH263", - "OMX_VIDEO_CodingMPEG4", - "OMX_VIDEO_CodingWMV", - "OMX_VIDEO_CodingRV", - "OMX_VIDEO_CodingAVC", - "OMX_VIDEO_CodingMJPEG", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *audioCodingTypeString(OMX_AUDIO_CODINGTYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_CodingUnused", - "OMX_AUDIO_CodingAutoDetect", - "OMX_AUDIO_CodingPCM", - "OMX_AUDIO_CodingADPCM", - "OMX_AUDIO_CodingAMR", - "OMX_AUDIO_CodingGSMFR", - "OMX_AUDIO_CodingGSMEFR", - "OMX_AUDIO_CodingGSMHR", - "OMX_AUDIO_CodingPDCFR", - "OMX_AUDIO_CodingPDCEFR", - "OMX_AUDIO_CodingPDCHR", - "OMX_AUDIO_CodingTDMAFR", - "OMX_AUDIO_CodingTDMAEFR", - "OMX_AUDIO_CodingQCELP8", - "OMX_AUDIO_CodingQCELP13", - "OMX_AUDIO_CodingEVRC", - "OMX_AUDIO_CodingSMV", - "OMX_AUDIO_CodingG711", - "OMX_AUDIO_CodingG723", - "OMX_AUDIO_CodingG726", - "OMX_AUDIO_CodingG729", - "OMX_AUDIO_CodingAAC", - "OMX_AUDIO_CodingMP3", - "OMX_AUDIO_CodingSBC", - "OMX_AUDIO_CodingVORBIS", - "OMX_AUDIO_CodingOPUS", - "OMX_AUDIO_CodingWMA", - "OMX_AUDIO_CodingRA", - "OMX_AUDIO_CodingMIDI", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *audioPCMModeString(OMX_AUDIO_PCMMODETYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_PCMModeLinear", - "OMX_AUDIO_PCMModeALaw", - "OMX_AUDIO_PCMModeMULaw", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *amrBandModeString(OMX_AUDIO_AMRBANDMODETYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_AMRBandModeUnused", - "OMX_AUDIO_AMRBandModeNB0", - "OMX_AUDIO_AMRBandModeNB1", - "OMX_AUDIO_AMRBandModeNB2", - "OMX_AUDIO_AMRBandModeNB3", - "OMX_AUDIO_AMRBandModeNB4", - "OMX_AUDIO_AMRBandModeNB5", - "OMX_AUDIO_AMRBandModeNB6", - "OMX_AUDIO_AMRBandModeNB7", - "OMX_AUDIO_AMRBandModeWB0", - "OMX_AUDIO_AMRBandModeWB1", - "OMX_AUDIO_AMRBandModeWB2", - "OMX_AUDIO_AMRBandModeWB3", - "OMX_AUDIO_AMRBandModeWB4", - "OMX_AUDIO_AMRBandModeWB5", - "OMX_AUDIO_AMRBandModeWB6", - "OMX_AUDIO_AMRBandModeWB7", - "OMX_AUDIO_AMRBandModeWB8", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *amrFrameFormatString(OMX_AUDIO_AMRFRAMEFORMATTYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_AMRFrameFormatConformance", - "OMX_AUDIO_AMRFrameFormatIF1", - "OMX_AUDIO_AMRFrameFormatIF2", - "OMX_AUDIO_AMRFrameFormatFSF", - "OMX_AUDIO_AMRFrameFormatRTPPayload", - "OMX_AUDIO_AMRFrameFormatITU", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -4319,10 +4107,10 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf(" nStride = %" PRIu32 "\n", imageDef->nStride); printf(" eCompressionFormat = %s\n", - imageCompressionFormatString(imageDef->eCompressionFormat)); + asString(imageDef->eCompressionFormat)); printf(" eColorFormat = %s\n", - colorFormatString(imageDef->eColorFormat)); + asString(imageDef->eColorFormat)); break; } @@ -4338,10 +4126,10 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf(" nStride = %" PRIu32 "\n", videoDef->nStride); printf(" eCompressionFormat = %s\n", - videoCompressionFormatString(videoDef->eCompressionFormat)); + asString(videoDef->eCompressionFormat)); printf(" eColorFormat = %s\n", - colorFormatString(videoDef->eColorFormat)); + asString(videoDef->eColorFormat)); break; } @@ -4353,7 +4141,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf("\n"); printf(" // Audio\n"); printf(" eEncoding = %s\n", - audioCodingTypeString(audioDef->eEncoding)); + asString(audioDef->eEncoding)); if (audioDef->eEncoding == OMX_AUDIO_CodingPCM) { OMX_AUDIO_PARAM_PCMMODETYPE params; @@ -4373,7 +4161,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { params.eNumData == OMX_NumericalDataSigned ? "signed" : "unsigned"); - printf(" ePCMMode = %s\n", audioPCMModeString(params.ePCMMode)); + printf(" ePCMMode = %s\n", asString(params.ePCMMode)); } else if (audioDef->eEncoding == OMX_AUDIO_CodingAMR) { OMX_AUDIO_PARAM_AMRTYPE amr; InitOMXParams(&amr); @@ -4385,9 +4173,9 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf(" nChannels = %" PRIu32 "\n", amr.nChannels); printf(" eAMRBandMode = %s\n", - amrBandModeString(amr.eAMRBandMode)); + asString(amr.eAMRBandMode)); printf(" eAMRFrameFormat = %s\n", - amrFrameFormatString(amr.eAMRFrameFormat)); + asString(amr.eAMRFrameFormat)); } break; @@ -4696,12 +4484,7 @@ status_t QueryCodec( const char *componentName, const char *mime, bool isEncoder, CodecCapabilities *caps) { - if (strncmp(componentName, "OMX.", 4)) { - // Not an OpenMax component but a software codec. - caps->mFlags = 0; - caps->mComponentName = componentName; - return OK; - } + bool isVideo = !strncasecmp(mime, "video/", 6); sp<OMXCodecObserver> observer = new OMXCodecObserver; IOMX::node_id node; @@ -4716,59 +4499,62 @@ status_t QueryCodec( caps->mFlags = 0; caps->mComponentName = componentName; - OMX_VIDEO_PARAM_PROFILELEVELTYPE param; - InitOMXParams(¶m); + // NOTE: OMX does not provide a way to query AAC profile support + if (isVideo) { + OMX_VIDEO_PARAM_PROFILELEVELTYPE param; + InitOMXParams(¶m); - param.nPortIndex = !isEncoder ? 0 : 1; + param.nPortIndex = !isEncoder ? 0 : 1; - for (param.nProfileIndex = 0;; ++param.nProfileIndex) { - err = omx->getParameter( - node, OMX_IndexParamVideoProfileLevelQuerySupported, - ¶m, sizeof(param)); - - if (err != OK) { - break; - } + for (param.nProfileIndex = 0;; ++param.nProfileIndex) { + err = omx->getParameter( + node, OMX_IndexParamVideoProfileLevelQuerySupported, + ¶m, sizeof(param)); - CodecProfileLevel profileLevel; - profileLevel.mProfile = param.eProfile; - profileLevel.mLevel = param.eLevel; + if (err != OK) { + break; + } - caps->mProfileLevels.push(profileLevel); - } + CodecProfileLevel profileLevel; + profileLevel.mProfile = param.eProfile; + profileLevel.mLevel = param.eLevel; - // Color format query - // return colors in the order reported by the OMX component - // prefix "flexible" standard ones with the flexible equivalent - OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; - InitOMXParams(&portFormat); - portFormat.nPortIndex = !isEncoder ? 1 : 0; - for (portFormat.nIndex = 0;; ++portFormat.nIndex) { - err = omx->getParameter( - node, OMX_IndexParamVideoPortFormat, - &portFormat, sizeof(portFormat)); - if (err != OK) { - break; + caps->mProfileLevels.push(profileLevel); } - OMX_U32 flexibleEquivalent; - if (ACodec::isFlexibleColorFormat( - omx, node, portFormat.eColorFormat, &flexibleEquivalent)) { - bool marked = false; - for (size_t i = 0; i < caps->mColorFormats.size(); i++) { - if (caps->mColorFormats.itemAt(i) == flexibleEquivalent) { - marked = true; - break; - } + // Color format query + // return colors in the order reported by the OMX component + // prefix "flexible" standard ones with the flexible equivalent + OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; + InitOMXParams(&portFormat); + portFormat.nPortIndex = !isEncoder ? 1 : 0; + for (portFormat.nIndex = 0;; ++portFormat.nIndex) { + err = omx->getParameter( + node, OMX_IndexParamVideoPortFormat, + &portFormat, sizeof(portFormat)); + if (err != OK) { + break; } - if (!marked) { - caps->mColorFormats.push(flexibleEquivalent); + + OMX_U32 flexibleEquivalent; + if (ACodec::isFlexibleColorFormat( + omx, node, portFormat.eColorFormat, &flexibleEquivalent)) { + bool marked = false; + for (size_t i = 0; i < caps->mColorFormats.size(); i++) { + if (caps->mColorFormats.itemAt(i) == flexibleEquivalent) { + marked = true; + break; + } + } + if (!marked) { + caps->mColorFormats.push(flexibleEquivalent); + } } + caps->mColorFormats.push(portFormat.eColorFormat); } - caps->mColorFormats.push(portFormat.eColorFormat); } - if (!isEncoder && !strncmp(mime, "video/", 6)) { + if (isVideo && !isEncoder) { if (omx->storeMetaDataInBuffers( node, 1 /* port index */, OMX_TRUE) == OK || omx->prepareForAdaptivePlayback( diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index aeb8dd7..bd28583 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -38,6 +38,7 @@ extern "C" { int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); + long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); } namespace android { @@ -84,6 +85,8 @@ struct MyVorbisExtractor { private: struct Page { uint64_t mGranulePosition; + int32_t mPrevPacketSize; + uint64_t mPrevPacketPos; uint32_t mSerialNo; uint32_t mPageNo; uint8_t mFlags; @@ -121,6 +124,8 @@ private: status_t verifyHeader( MediaBuffer *buffer, uint8_t type); + int32_t packetBlockSize(MediaBuffer *buffer); + void parseFileMetaData(); status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos); @@ -373,6 +378,7 @@ status_t MyVorbisExtractor::seekToOffset(off64_t offset) { mFirstPacketInPage = true; mCurrentPageSamples = 0; mCurrentPage.mNumSegments = 0; + mCurrentPage.mPrevPacketSize = -1; mNextLaceIndex = 0; // XXX what if new page continues packet from last??? @@ -489,16 +495,6 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { tmp->set_range(0, buffer->range_length()); buffer->release(); } else { - // XXX Not only is this not technically the correct time for - // this packet, we also stamp every packet in this page - // with the same time. This needs fixing later. - - if (mVi.rate) { - // Rate may not have been initialized yet if we're currently - // reading the configuration packets... - // Fortunately, the timestamp doesn't matter for those. - timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate; - } tmp->set_range(0, 0); } buffer = tmp; @@ -521,16 +517,34 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { if (gotFullPacket) { // We've just read the entire packet. - if (timeUs >= 0) { - buffer->meta_data()->setInt64(kKeyTime, timeUs); - } - if (mFirstPacketInPage) { buffer->meta_data()->setInt32( kKeyValidSamples, mCurrentPageSamples); mFirstPacketInPage = false; } + if (mVi.rate) { + // Rate may not have been initialized yet if we're currently + // reading the configuration packets... + // Fortunately, the timestamp doesn't matter for those. + int32_t curBlockSize = packetBlockSize(buffer); + if (mCurrentPage.mPrevPacketSize < 0) { + mCurrentPage.mPrevPacketSize = curBlockSize; + mCurrentPage.mPrevPacketPos = + mCurrentPage.mGranulePosition - mCurrentPageSamples; + timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate; + } else { + // The effective block size is the average of the two overlapped blocks + int32_t actualBlockSize = + (curBlockSize + mCurrentPage.mPrevPacketSize) / 2; + timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate; + // The actual size output by the decoder will be half the effective + // size, due to the overlap + mCurrentPage.mPrevPacketPos += actualBlockSize / 2; + mCurrentPage.mPrevPacketSize = curBlockSize; + } + buffer->meta_data()->setInt64(kKeyTime, timeUs); + } *out = buffer; return OK; @@ -686,6 +700,35 @@ void MyVorbisExtractor::buildTableOfContents() { } } +int32_t MyVorbisExtractor::packetBlockSize(MediaBuffer *buffer) { + const uint8_t *data = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + size_t size = buffer->range_length(); + + ogg_buffer buf; + buf.data = (uint8_t *)data; + buf.size = size; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = size; + ref.next = NULL; + + ogg_packet pack; + pack.packet = &ref; + pack.bytes = ref.length; + pack.b_o_s = 0; + pack.e_o_s = 0; + pack.granulepos = 0; + pack.packetno = 0; + + return vorbis_packet_blocksize(&mVi, &pack); +} + status_t MyVorbisExtractor::verifyHeader( MediaBuffer *buffer, uint8_t type) { const uint8_t *data = @@ -730,6 +773,10 @@ status_t MyVorbisExtractor::verifyHeader( ALOGV("upper-bitrate = %ld", mVi.bitrate_upper); ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal); ALOGV("window-bitrate = %ld", mVi.bitrate_window); + ALOGV("blocksizes: %d/%d", + vorbis_info_blocksize(&mVi, 0), + vorbis_info_blocksize(&mVi, 1) + ); off64_t size; if (mSource->getSize(&size) == OK) { diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 4e1c65c..530383b 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -448,7 +448,7 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { } // Part of the BufferQueue::ConsumerListener -void SurfaceMediaSource::onFrameAvailable() { +void SurfaceMediaSource::onFrameAvailable(const BufferItem& /* item */) { ALOGV("onFrameAvailable"); sp<FrameAvailableListener> listener; diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index fc03607..5ec3438 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -222,28 +222,25 @@ status_t getNextNALUnit( *nalStart = NULL; *nalSize = 0; - if (size == 0) { + if (size < 3) { return -EAGAIN; } - // Skip any number of leading 0x00. - size_t offset = 0; - while (offset < size && data[offset] == 0x00) { - ++offset; - } - - if (offset == size) { - return -EAGAIN; - } // A valid startcode consists of at least two 0x00 bytes followed by 0x01. - - if (offset < 2 || data[offset] != 0x01) { - return ERROR_MALFORMED; + for (; offset + 2 < size; ++offset) { + if (data[offset + 2] == 0x01 && data[offset] == 0x00 + && data[offset + 1] == 0x00) { + break; + } } - - ++offset; + if (offset + 2 >= size) { + *_data = &data[offset]; + *_size = 2; + return -EAGAIN; + } + offset += 3; size_t startOffset = offset; diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 0bb1eb8..0546871 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -929,30 +929,22 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { } if (mEndOfInput) { - if (outputDelayRingBufferSamplesAvailable() > 0 - && outputDelayRingBufferSamplesAvailable() - < mStreamInfo->frameSize * mStreamInfo->numChannels) { - ALOGE("not a complete frame of samples available"); - mSignalledError = true; - notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); - return; - } - - if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) { + int ringBufAvail = outputDelayRingBufferSamplesAvailable(); + if (!outQueue.empty() + && ringBufAvail < mStreamInfo->frameSize * mStreamInfo->numChannels) { if (!mEndOfOutput) { - // send empty block signaling EOS + // send partial or empty block signaling EOS mEndOfOutput = true; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - if (outHeader->nOffset != 0) { - ALOGE("outHeader->nOffset != 0 is not handled"); - mSignalledError = true; - notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); - return; + INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer + + outHeader->nOffset); + int32_t ns = outputDelayRingBufferGetSamples(outBuffer, ringBufAvail); + if (ns < 0) { + ns = 0; } - - outHeader->nFilledLen = 0; + outHeader->nFilledLen = ns; outHeader->nFlags = OMX_BUFFERFLAG_EOS; outHeader->nTimeStamp = mBufferTimestamps.itemAt(0); @@ -991,7 +983,7 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { } int32_t ns = outputDelayRingBufferGetSamples(0, avail); if (ns != avail) { - ALOGE("not a complete frame of samples available"); + ALOGW("not a complete frame of samples available"); break; } mOutputBufferCount++; diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp index 1513b0b..cfc37b7 100644 --- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp +++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp @@ -575,9 +575,13 @@ OMX_ERRORTYPE SoftAVCEncoder::internalSetParameter( &editPortInfo(0)->mDef; portDef->format.video.nFrameWidth = mVideoWidth; portDef->format.video.nFrameHeight = mVideoHeight; + portDef->format.video.nStride = portDef->format.video.nFrameWidth; + portDef->format.video.nSliceHeight = portDef->format.video.nFrameHeight; portDef->format.video.xFramerate = def->format.video.xFramerate; portDef->format.video.eColorFormat = (OMX_COLOR_FORMATTYPE) mVideoColorFormat; + portDef->nBufferSize = + (portDef->format.video.nStride * portDef->format.video.nSliceHeight * 3) / 2; portDef = &editPortInfo(1)->mDef; portDef->format.video.nFrameWidth = mVideoWidth; portDef->format.video.nFrameHeight = mVideoHeight; diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index 142969c..246069b 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -382,5 +382,6 @@ android::SoftOMXComponent *createSoftOMXComponent( } else { CHECK(!"Unknown component"); } + return NULL; } diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp index 28edff8..1d0a2f0 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp @@ -462,9 +462,13 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( &editPortInfo(0)->mDef; portDef->format.video.nFrameWidth = mVideoWidth; portDef->format.video.nFrameHeight = mVideoHeight; + portDef->format.video.nStride = portDef->format.video.nFrameWidth; + portDef->format.video.nSliceHeight = portDef->format.video.nFrameHeight; portDef->format.video.xFramerate = def->format.video.xFramerate; portDef->format.video.eColorFormat = (OMX_COLOR_FORMATTYPE) mVideoColorFormat; + portDef->nBufferSize = + (portDef->format.video.nStride * portDef->format.video.nSliceHeight * 3) / 2; portDef = &editPortInfo(1)->mDef; portDef->format.video.nFrameWidth = mVideoWidth; portDef->format.video.nFrameHeight = mVideoHeight; diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 828577a..87d6961 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -189,4 +189,5 @@ android::SoftOMXComponent *createSoftOMXComponent( } else { CHECK(!"Unknown component"); } + return NULL; } diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index eb621d5..0285feb 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -805,8 +805,12 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; def->format.video.nFrameWidth = mWidth; def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; def->format.video.xFramerate = mFramerate; def->format.video.eColorFormat = mColorFormat; + def->nBufferSize = + (def->format.video.nStride * def->format.video.nSliceHeight * 3) / 2; def = &editPortInfo(kOutputPortIndex)->mDef; def->format.video.nFrameWidth = mWidth; def->format.video.nFrameHeight = mHeight; diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml index f599004..85f6615 100644 --- a/media/libstagefright/data/media_codecs_google_audio.xml +++ b/media/libstagefright/data/media_codecs_google_audio.xml @@ -48,7 +48,7 @@ </MediaCodec> <MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis"> <Limit name="channel-count" max="8" /> - <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000,96000" /> + <Limit name="sample-rate" ranges="8000-96000" /> <Limit name="bitrate" range="32000-500000" /> </MediaCodec> <MediaCodec name="OMX.google.opus.decoder" type="audio/opus"> diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp new file mode 100644 index 0000000..ec4a960 --- /dev/null +++ b/media/libstagefright/foundation/ADebug.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2014 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 <errno.h> +#include <stdlib.h> +#include <ctype.h> + +#define LOG_TAG "ADebug" +#include <utils/Log.h> +#include <utils/misc.h> + +#include <cutils/properties.h> + +#include <ADebug.h> +#include <AStringUtils.h> +#include <AUtils.h> + +namespace android { + +//static +ADebug::Level ADebug::GetDebugLevelFromString( + const char *name, const char *value, ADebug::Level def) { + // split on , + const char *next = value, *current; + const unsigned long maxLevel = (unsigned long)kDebugMax; + while (next != NULL) { + current = next; + next = strchr(current, ','); + if (next != NULL) { + ++next; // pass , + } + + while (isspace(*current)) { + ++current; + } + // check for : + char *colon = strchr(current, ':'); + + // get level + char *end; + errno = 0; // strtoul does not clear errno, but it can be set for any return value + unsigned long level = strtoul(current, &end, 10); + while (isspace(*end)) { + ++end; + } + if (errno != 0 || end == current || (end != colon && *end != '\0' && end != next)) { + // invalid level - skip + continue; + } + if (colon != NULL) { + // check if pattern matches + do { // skip colon and spaces + ++colon; + } while (isspace(*colon)); + size_t globLen = (next == NULL ? strlen(colon) : (next - 1 - colon)); + while (globLen > 0 && isspace(colon[globLen - 1])) { + --globLen; // trim glob + } + + if (!AStringUtils::MatchesGlob( + colon, globLen, name, strlen(name), true /* ignoreCase */)) { + continue; + } + } + + // update debug level + def = (Level)min(level, maxLevel); + } + return def; +} + +//static +ADebug::Level ADebug::GetDebugLevelFromProperty( + const char *name, const char *propertyName, ADebug::Level def) { + char value[PROPERTY_VALUE_MAX]; + if (property_get(propertyName, value, NULL)) { + return GetDebugLevelFromString(name, value, def); + } + return def; +} + +//static +char *ADebug::GetDebugName(const char *name) { + char *debugName = strdup(name); + const char *terms[] = { "omx", "video", "audio" }; + for (size_t i = 0; i < NELEM(terms) && debugName != NULL; i++) { + const char *term = terms[i]; + const size_t len = strlen(term); + char *match = strcasestr(debugName, term); + if (match != NULL && (match == debugName || match[-1] == '.' + || match[len] == '.' || match[len] == '\0')) { + char *src = match + len; + if (match == debugName || match[-1] == '.') { + src += (*src == '.'); // remove trailing or double . + } + memmove(match, src, debugName + strlen(debugName) - src + 1); + } + } + + return debugName; +} + +} // namespace android + diff --git a/media/libstagefright/foundation/AStringUtils.cpp b/media/libstagefright/foundation/AStringUtils.cpp new file mode 100644 index 0000000..e5a846c --- /dev/null +++ b/media/libstagefright/foundation/AStringUtils.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2014 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 <string.h> +#include <AStringUtils.h> + +namespace android { + +// static +int AStringUtils::Compare(const char *a, const char *b, size_t len, bool ignoreCase) { + // this method relies on a trailing '\0' if a or b are shorter than len + return ignoreCase ? strncasecmp(a, b, len) : strncmp(a, b, len); +} + +// static +bool AStringUtils::MatchesGlob( + const char *glob, size_t globLen, const char *str, size_t strLen, bool ignoreCase) { + // this method does not assume a trailing '\0' + size_t ix = 0, globIx = 0; + + // pattern must match until first '*' + while (globIx < globLen && glob[globIx] != '*') { + ++globIx; + } + if (strLen < globIx || Compare(str, glob, globIx /* len */, ignoreCase)) { + return false; + } + ix = globIx; + + // process by * separated sections + while (globIx < globLen) { + ++globIx; + size_t start = globIx; + while (globIx < globLen && glob[globIx] != '*') { + ++globIx; + } + size_t len = globIx - start; + const char *pattern = glob + start; + + if (globIx == globLen) { + // last pattern must match tail + if (ix + len > strLen) { + return false; + } + const char *tail = str + strLen - len; + return !Compare(tail, pattern, len, ignoreCase); + } + // progress after first occurrence of pattern + while (ix + len <= strLen && Compare(str + ix, pattern, len, ignoreCase)) { + ++ix; + } + if (ix + len > strLen) { + return false; + } + ix += len; + // we will loop around as globIx < globLen + } + + // we only get here if there were no * in the pattern + return ix == strLen; +} + +} // namespace android + diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk index 90a6a23..c1dd6ce 100644 --- a/media/libstagefright/foundation/Android.mk +++ b/media/libstagefright/foundation/Android.mk @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \ AAtomizer.cpp \ ABitReader.cpp \ ABuffer.cpp \ + ADebug.cpp \ AHandler.cpp \ AHierarchicalStateMachine.cpp \ ALooper.cpp \ @@ -12,6 +13,7 @@ LOCAL_SRC_FILES:= \ AMessage.cpp \ ANetworkSession.cpp \ AString.cpp \ + AStringUtils.cpp \ ParsedMessage.cpp \ base64.cpp \ hexdump.cpp @@ -22,6 +24,7 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES := \ libbinder \ libutils \ + libcutils \ liblog LOCAL_CFLAGS += -Wno-multichar -Werror diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index a934e72..6522ad7 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1165,6 +1165,14 @@ status_t LiveSession::selectTrack(size_t index, bool select) { return err; } +ssize_t LiveSession::getSelectedTrack(media_track_type type) const { + if (mPlaylist == NULL) { + return -1; + } else { + return mPlaylist->getSelectedTrack(type); + } +} + bool LiveSession::canSwitchUp() { // Allow upwards bandwidth switch when a stream has buffered at least 10 seconds. status_t err = OK; @@ -1458,7 +1466,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { extra->setInt64("timeUs", timeUs); discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); discontinuityQueue->queueDiscontinuity( - ATSParser::DISCONTINUITY_SEEK, extra, true); + ATSParser::DISCONTINUITY_TIME, extra, true); } else { int32_t type; sp<AMessage> meta; diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index 7aacca6..896a8fc 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -19,6 +19,7 @@ #define LIVE_SESSION_H_ #include <media/stagefright/foundation/AHandler.h> +#include <media/mediaplayer.h> #include <utils/String8.h> @@ -73,6 +74,7 @@ struct LiveSession : public AHandler { size_t getTrackCount() const; sp<AMessage> getTrackInfo(size_t trackIndex) const; status_t selectTrack(size_t index, bool select); + ssize_t getSelectedTrack(media_track_type /* type */) const; bool isSeekable() const; bool hasDynamicDuration() const; diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 1651dee..eb62c7a 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -66,6 +66,9 @@ protected: virtual ~MediaGroup(); private: + + friend struct M3UParser; + struct Media { AString mName; AString mURI; @@ -356,6 +359,38 @@ ssize_t M3UParser::getSelectedIndex() const { return mSelectedIndex; } +ssize_t M3UParser::getSelectedTrack(media_track_type type) const { + MediaGroup::Type groupType; + switch (type) { + case MEDIA_TRACK_TYPE_VIDEO: + groupType = MediaGroup::TYPE_VIDEO; + break; + + case MEDIA_TRACK_TYPE_AUDIO: + groupType = MediaGroup::TYPE_AUDIO; + break; + + case MEDIA_TRACK_TYPE_SUBTITLE: + groupType = MediaGroup::TYPE_SUBS; + break; + + default: + return -1; + } + + for (size_t i = 0, ii = 0; i < mMediaGroups.size(); ++i) { + sp<MediaGroup> group = mMediaGroups.valueAt(i); + size_t tracks = group->countTracks(); + if (groupType != group->mType) { + ii += tracks; + } else if (group->mSelectedIndex >= 0) { + return ii + group->mSelectedIndex; + } + } + + return -1; +} + bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const { if (!mIsVariantPlaylist) { *uri = mBaseURI; diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index d588afe..1cad060 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -21,6 +21,7 @@ #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AString.h> +#include <media/mediaplayer.h> #include <utils/Vector.h> namespace android { @@ -46,6 +47,7 @@ struct M3UParser : public RefBase { size_t getTrackCount() const; sp<AMessage> getTrackInfo(size_t index) const; ssize_t getSelectedIndex() const; + ssize_t getSelectedTrack(media_track_type /* type */) const; bool getTypeURI(size_t index, const char *key, AString *uri) const; diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 2873fc4..c0c7ed9 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -699,8 +699,7 @@ status_t PlaylistFetcher::refreshPlaylist() { mRefreshState = (RefreshState)(mRefreshState + 1); } } else { - ALOGE("failed to load playlist at url '%s'", mURI.c_str()); - notifyError(ERROR_IO); + ALOGE("failed to load playlist at url '%s'", uriDebugString(mURI).c_str()); return ERROR_IO; } } else { @@ -723,26 +722,25 @@ bool PlaylistFetcher::bufferStartsWithTsSyncByte(const sp<ABuffer>& buffer) { } void PlaylistFetcher::onDownloadNext() { - if (refreshPlaylist() != OK) { - return; - } - - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - + status_t err = refreshPlaylist(); + int32_t firstSeqNumberInPlaylist = 0; + int32_t lastSeqNumberInPlaylist = 0; bool discontinuity = false; - const int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + if (mPlaylist != NULL) { + if (mPlaylist->meta() != NULL) { + mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist); + } + + lastSeqNumberInPlaylist = + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; - if (mDiscontinuitySeq < 0) { - mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); + if (mDiscontinuitySeq < 0) { + mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); + } } - if (mSeqNumber < 0) { + if (mPlaylist != NULL && mSeqNumber < 0) { CHECK_GE(mStartTimeUs, 0ll); if (mSegmentStartTimeUs < 0) { @@ -784,19 +782,26 @@ void PlaylistFetcher::onDownloadNext() { } } + // if mPlaylist is NULL then err must be non-OK; but the other way around might not be true if (mSeqNumber < firstSeqNumberInPlaylist - || mSeqNumber > lastSeqNumberInPlaylist) { - if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) { + || mSeqNumber > lastSeqNumberInPlaylist + || err != OK) { + if ((err != OK || !mPlaylist->isComplete()) && mNumRetries < kMaxNumRetries) { ++mNumRetries; - if (mSeqNumber > lastSeqNumberInPlaylist) { + if (mSeqNumber > lastSeqNumberInPlaylist || err != OK) { + // make sure we reach this retry logic on refresh failures + // by adding an err != OK clause to all enclosing if's. + // refresh in increasing fraction (1/2, 1/3, ...) of the // playlist's target duration or 3 seconds, whichever is less - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32( - "target-duration", &targetDurationSecs)); - int64_t delayUs = mPlaylist->size() * targetDurationSecs * - 1000000ll / (1 + mNumRetries); + int64_t delayUs = kMaxMonitorDelayUs; + if (mPlaylist != NULL && mPlaylist->meta() != NULL) { + int32_t targetDurationSecs; + CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); + delayUs = mPlaylist->size() * targetDurationSecs * + 1000000ll / (1 + mNumRetries); + } if (delayUs > kMaxMonitorDelayUs) { delayUs = kMaxMonitorDelayUs; } @@ -808,13 +813,30 @@ void PlaylistFetcher::onDownloadNext() { return; } - // we've missed the boat, let's start from the lowest sequence + if (err != OK) { + notifyError(err); + return; + } + + // we've missed the boat, let's start 3 segments prior to the latest sequence // number available and signal a discontinuity. ALOGI("We've missed the boat, restarting playback." " mStartup=%d, was looking for %d in %d-%d", mStartup, mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); + if (mStopParams != NULL) { + // we should have kept on fetching until we hit the boundaries in mStopParams, + // but since the segments we are supposed to fetch have already rolled off + // the playlist, i.e. we have already missed the boat, we inevitably have to + // skip. + for (size_t i = 0; i < mPacketSources.size(); i++) { + sp<ABuffer> formatChange = mSession->createFormatChangeBuffer(); + mPacketSources.valueAt(i)->queueAccessUnit(formatChange); + } + stopAsync(/* clear = */ false); + return; + } mSeqNumber = lastSeqNumberInPlaylist - 3; if (mSeqNumber < firstSeqNumberInPlaylist) { mSeqNumber = firstSeqNumberInPlaylist; @@ -962,8 +984,8 @@ void PlaylistFetcher::onDownloadNext() { } while (bytesRead != 0); if (bufferStartsWithTsSyncByte(buffer)) { - // If we still don't see a stream after fetching a full ts segment mark it as - // nonexistent. + // If we don't see a stream in the program table after fetching a full ts segment + // mark it as nonexistent. const size_t kNumTypes = ATSParser::NUM_SOURCE_TYPES; ATSParser::SourceType srcTypes[kNumTypes] = { ATSParser::VIDEO, ATSParser::AUDIO }; @@ -978,7 +1000,7 @@ void PlaylistFetcher::onDownloadNext() { static_cast<AnotherPacketSource *>( mTSParser->getSource(srcType).get()); - if (source == NULL) { + if (!mTSParser->hasSource(srcType)) { ALOGW("MPEG2 Transport stream does not contain %s data.", srcType == ATSParser::VIDEO ? "video" : "audio"); @@ -995,7 +1017,7 @@ void PlaylistFetcher::onDownloadNext() { return; } - status_t err = OK; + err = OK; if (tsBuffer != NULL) { AString method; CHECK(buffer->meta()->findString("cipher-method", &method)); @@ -1154,7 +1176,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu extra->setInt64(IStreamListener::kKeyMediaTimeUs, 0); mTSParser->signalDiscontinuity( - ATSParser::DISCONTINUITY_SEEK, extra); + ATSParser::DISCONTINUITY_TIME, extra); mAbsoluteTimeAnchorUs = mNextPTSTimeUs; mNextPTSTimeUs = -1ll; @@ -1255,6 +1277,11 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) { + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } int32_t targetDurationSecs; CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); @@ -1265,6 +1292,8 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu // mStartTimeUs. // mSegmentStartTimeUs >= 0 // mSegmentStartTimeUs is non-negative when adapting or switching tracks + // mSeqNumber > firstSeqNumberInPlaylist + // don't decrement mSeqNumber if it already points to the 1st segment // timeUs - mStartTimeUs > targetDurationUs: // This and the 2 above conditions should only happen when adapting in a live // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher @@ -1274,6 +1303,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu // stop as early as possible. The definition of being "too far ahead" is // arbitrary; here we use targetDurationUs as threshold. if (mStartup && mSegmentStartTimeUs >= 0 + && mSeqNumber > firstSeqNumberInPlaylist && timeUs - mStartTimeUs > targetDurationUs) { // we just guessed a starting timestamp that is too high when adapting in a // live stream; re-adjust based on the actual timestamp extracted from the @@ -1586,6 +1616,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( mStartTimeUsNotify->setInt32("streamMask", LiveSession::STREAMTYPE_AUDIO); mStartTimeUsNotify->post(); mStartTimeUsNotify.clear(); + mStartup = false; } } diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index 24d431c..104dcfc 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -31,7 +31,7 @@ struct GraphicBufferSource; struct OMXNodeInstance { OMXNodeInstance( - OMX *owner, const sp<IOMXObserver> &observer); + OMX *owner, const sp<IOMXObserver> &observer, const char *name); void setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle); @@ -149,6 +149,18 @@ private: KeyedVector<OMX_BUFFERHEADERTYPE *, OMX::buffer_id> mBufferHeaderToBufferID; #endif + // For debug support + char *mName; + int DEBUG; + size_t mNumPortBuffers[2]; // modified under mLock, read outside for debug + Mutex mDebugLock; + // following are modified and read under mDebugLock + int DEBUG_BUMP; + SortedVector<OMX_BUFFERHEADERTYPE *> mInputBuffersWithCodec, mOutputBuffersWithCodec; + size_t mDebugLevelBumpPendingBuffers[2]; + void bumpDebugLevel_l(size_t numInputBuffers, size_t numOutputBuffers); + void unbumpDebugLevel_l(size_t portIndex); + ~OMXNodeInstance(); void addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id); @@ -186,6 +198,10 @@ private: OMX_U32 portIndex, OMX_BOOL enable, OMX_BOOL useGraphicBuffer, OMX_BOOL *usingGraphicBufferInMeta); + status_t emptyBuffer_l( + OMX_BUFFERHEADERTYPE *header, + OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr); + sp<GraphicBufferSource> getGraphicBufferSource(); void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource); diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 89ffa0c..0712bf0 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -413,16 +413,16 @@ void BlockIterator::seek( const mkvparser::CuePoint* pCP; mkvparser::Tracks const *pTracks = pSegment->GetTracks(); - unsigned long int trackCount = pTracks->GetTracksCount(); while (!pCues->DoneParsing()) { pCues->LoadCuePoint(); pCP = pCues->GetLast(); CHECK(pCP); + size_t trackCount = mExtractor->mTracks.size(); for (size_t index = 0; index < trackCount; ++index) { - const mkvparser::Track *pTrack = pTracks->GetTrackByIndex(index); + MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(index); + const mkvparser::Track *pTrack = pTracks->GetTrackByNumber(track.mTrackNum); if (pTrack && pTrack->GetType() == 1 && pCP->Find(pTrack)) { // VIDEO_TRACK - MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(index); track.mCuePoints.push_back(pCP); } } @@ -434,12 +434,13 @@ void BlockIterator::seek( } const mkvparser::CuePoint::TrackPosition *pTP = NULL; - const mkvparser::Track *thisTrack = pTracks->GetTrackByIndex(mIndex); + const mkvparser::Track *thisTrack = pTracks->GetTrackByNumber(mTrackNum); if (thisTrack->GetType() == 1) { // video MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(mIndex); pTP = track.find(seekTimeNs); } else { // The Cue index is built around video keyframes + unsigned long int trackCount = pTracks->GetTracksCount(); for (size_t index = 0; index < trackCount; ++index) { const mkvparser::Track *pTrack = pTracks->GetTrackByIndex(index); if (pTrack && pTrack->GetType() == 1 && pCues->Find(seekTimeNs, pTrack, pCP, pTP)) { diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 6d8866a..482ccff 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -63,6 +63,7 @@ struct ATSParser::Program : public RefBase { void signalEOS(status_t finalResult); sp<MediaSource> getSource(SourceType type); + bool hasSource(SourceType type) const; int64_t convertPTSToTimestamp(uint64_t PTS); @@ -119,6 +120,9 @@ struct ATSParser::Stream : public RefBase { sp<MediaSource> getSource(SourceType type); + bool isAudio() const; + bool isVideo() const; + protected: virtual ~Stream(); @@ -146,9 +150,6 @@ private: void extractAACFrames(const sp<ABuffer> &buffer); - bool isAudio() const; - bool isVideo() const; - DISALLOW_EVIL_CONSTRUCTORS(Stream); }; @@ -244,11 +245,16 @@ struct StreamInfo { status_t ATSParser::Program::parseProgramMap(ABitReader *br) { unsigned table_id = br->getBits(8); ALOGV(" table_id = %u", table_id); - CHECK_EQ(table_id, 0x02u); - + if (table_id != 0x02u) { + ALOGE("PMT data error!"); + return ERROR_MALFORMED; + } unsigned section_syntax_indicator = br->getBits(1); ALOGV(" section_syntax_indicator = %u", section_syntax_indicator); - CHECK_EQ(section_syntax_indicator, 1u); + if (section_syntax_indicator != 1u) { + ALOGE("PMT data error!"); + return ERROR_MALFORMED; + } CHECK_EQ(br->getBits(1), 0u); MY_LOGV(" reserved = %u", br->getBits(2)); @@ -435,6 +441,19 @@ sp<MediaSource> ATSParser::Program::getSource(SourceType type) { return NULL; } +bool ATSParser::Program::hasSource(SourceType type) const { + for (size_t i = 0; i < mStreams.size(); ++i) { + const sp<Stream> &stream = mStreams.valueAt(i); + if (type == AUDIO && stream->isAudio()) { + return true; + } else if (type == VIDEO && stream->isVideo()) { + return true; + } + } + + return false; +} + int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) { if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) { if (!mFirstPTSValid) { @@ -660,7 +679,7 @@ void ATSParser::Stream::signalDiscontinuity( int64_t resumeAtMediaTimeUs = mProgram->convertPTSToTimestamp(resumeAtPTS); - extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs); + extra->setInt64("resume-at-mediaTimeUs", resumeAtMediaTimeUs); } } @@ -739,8 +758,10 @@ status_t ATSParser::Stream::parsePES(ABitReader *br) { if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { CHECK_GE(optional_bytes_remaining, 5u); - CHECK_EQ(br->getBits(4), PTS_DTS_flags); - + if (br->getBits(4) != PTS_DTS_flags) { + ALOGE("PES data Error!"); + return ERROR_MALFORMED; + } PTS = ((uint64_t)br->getBits(3)) << 30; CHECK_EQ(br->getBits(1), 1u); PTS |= ((uint64_t)br->getBits(15)) << 15; @@ -1003,8 +1024,10 @@ void ATSParser::signalEOS(status_t finalResult) { void ATSParser::parseProgramAssociationTable(ABitReader *br) { unsigned table_id = br->getBits(8); ALOGV(" table_id = %u", table_id); - CHECK_EQ(table_id, 0x00u); - + if (table_id != 0x00u) { + ALOGE("PAT data error!"); + return ; + } unsigned section_syntax_indictor = br->getBits(1); ALOGV(" section_syntax_indictor = %u", section_syntax_indictor); CHECK_EQ(section_syntax_indictor, 1u); @@ -1074,7 +1097,9 @@ status_t ATSParser::parsePID( sp<PSISection> section = mPSISections.valueAt(sectionIndex); if (payload_unit_start_indicator) { - CHECK(section->isEmpty()); + if (!section->isEmpty()) { + return ERROR_UNSUPPORTED; + } unsigned skip = br->getBits(8); br->skipBits(skip * 8); @@ -1203,7 +1228,10 @@ status_t ATSParser::parseTS(ABitReader *br) { ALOGV("---"); unsigned sync_byte = br->getBits(8); - CHECK_EQ(sync_byte, 0x47u); + if (sync_byte != 0x47u) { + ALOGE("[error] parseTS: return error as sync_byte=0x%x", sync_byte); + return BAD_VALUE; + } if (br->getBits(1)) { // transport_error_indicator // silently ignore. @@ -1264,6 +1292,17 @@ sp<MediaSource> ATSParser::getSource(SourceType type) { return NULL; } +bool ATSParser::hasSource(SourceType type) const { + for (size_t i = 0; i < mPrograms.size(); ++i) { + const sp<Program> &program = mPrograms.itemAt(i); + if (program->hasSource(type)) { + return true; + } + } + + return false; +} + bool ATSParser::PTSTimeDeltaEstablished() { if (mPrograms.isEmpty()) { return false; diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index b5cc6ed..75d76dc 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -41,8 +41,6 @@ struct ATSParser : public RefBase { DISCONTINUITY_ABSOLUTE_TIME = 8, DISCONTINUITY_TIME_OFFSET = 16, - DISCONTINUITY_SEEK = DISCONTINUITY_TIME, - // For legacy reasons this also implies a time discontinuity. DISCONTINUITY_FORMATCHANGE = DISCONTINUITY_AUDIO_FORMAT @@ -76,6 +74,7 @@ struct ATSParser : public RefBase { NUM_SOURCE_TYPES = 2 }; sp<MediaSource> getSource(SourceType type); + bool hasSource(SourceType type) const; bool PTSTimeDeltaEstablished(); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index a03f6f9..c579d4c 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -265,8 +265,12 @@ void AnotherPacketSource::queueDiscontinuity( mEOSResult = OK; mLastQueuedTimeUs = 0; mLatestEnqueuedMeta = NULL; - ++mQueuedDiscontinuityCount; + if (type == ATSParser::DISCONTINUITY_NONE) { + return; + } + + ++mQueuedDiscontinuityCount; sp<ABuffer> buffer = new ABuffer(0); buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type)); buffer->meta()->setMessage("extra", extra); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 459591d..73fe109 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -170,8 +170,9 @@ static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) { return parseAC3SyncFrame(ptr, size, NULL) > 0; } -static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) { - if (size < 3) { +static bool IsSeeminglyValidADTSHeader( + const uint8_t *ptr, size_t size, size_t *frameLength) { + if (size < 7) { // Not enough data to verify header. return false; } @@ -194,6 +195,13 @@ static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) { return false; } + size_t frameLengthInHeader = + ((ptr[3] & 3) << 11) + (ptr[4] << 3) + ((ptr[5] >> 5) & 7); + if (frameLengthInHeader > size) { + return false; + } + + *frameLength = frameLengthInHeader; return true; } @@ -315,8 +323,10 @@ status_t ElementaryStreamQueue::appendData( } #else ssize_t startOffset = -1; + size_t frameLength; for (size_t i = 0; i < size; ++i) { - if (IsSeeminglyValidADTSHeader(&ptr[i], size - i)) { + if (IsSeeminglyValidADTSHeader( + &ptr[i], size - i, &frameLength)) { startOffset = i; break; } @@ -332,6 +342,12 @@ status_t ElementaryStreamQueue::appendData( startOffset); } + if (frameLength != size - startOffset) { + ALOGW("First ADTS AAC frame length is %zd bytes, " + "while the buffer size is %zd bytes.", + frameLength, size - startOffset); + } + data = &ptr[startOffset]; size -= startOffset; #endif diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 3e70956..44c7edc 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -750,7 +750,7 @@ int GraphicBufferSource::findMatchingCodecBuffer_l( } // BufferQueue::ConsumerListener callback -void GraphicBufferSource::onFrameAvailable() { +void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) { Mutex::Autolock autoLock(mMutex); ALOGV("onFrameAvailable exec=%d avail=%zu", diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index c0860ab..c8e3775 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -137,7 +137,7 @@ protected: // into the codec buffer, and call Empty[This]Buffer. If we're not yet // executing or there's no codec buffer available, we just increment // mNumFramesAvailable and return. - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // BufferQueue::ConsumerListener interface, called when the client has // released one or more GraphicBuffers. We clear out the appropriate diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 43f0bc9..f8d38ff 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -225,7 +225,7 @@ status_t OMX::allocateNode( *node = 0; - OMXNodeInstance *instance = new OMXNodeInstance(this, observer); + OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name); OMX_COMPONENTTYPE *handle; OMX_ERRORTYPE err = mMaster->makeComponentInstance( diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index d07ec14..c04d95f 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -18,11 +18,15 @@ #define LOG_TAG "OMXNodeInstance" #include <utils/Log.h> +#include <inttypes.h> + #include "../include/OMXNodeInstance.h" #include "OMXMaster.h" #include "GraphicBufferSource.h" #include <OMX_Component.h> +#include <OMX_IndexExt.h> +#include <OMX_AsString.h> #include <binder/IMemory.h> #include <gui/BufferQueue.h> @@ -30,7 +34,68 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaErrors.h> +#include <utils/misc.h> + static const OMX_U32 kPortIndexInput = 0; +static const OMX_U32 kPortIndexOutput = 1; + +#define CLOGW(fmt, ...) ALOGW("[%x:%s] " fmt, mNodeID, mName, ##__VA_ARGS__) + +#define CLOG_ERROR_IF(cond, fn, err, fmt, ...) \ + ALOGE_IF(cond, #fn "(%x:%s, " fmt ") ERROR: %s(%#x)", \ + mNodeID, mName, ##__VA_ARGS__, asString(err), err) +#define CLOG_ERROR(fn, err, fmt, ...) CLOG_ERROR_IF(true, fn, err, fmt, ##__VA_ARGS__) +#define CLOG_IF_ERROR(fn, err, fmt, ...) \ + CLOG_ERROR_IF((err) != OMX_ErrorNone, fn, err, fmt, ##__VA_ARGS__) + +#define CLOGI_(level, fn, fmt, ...) \ + ALOGI_IF(DEBUG >= (level), #fn "(%x:%s, " fmt ")", mNodeID, mName, ##__VA_ARGS__) +#define CLOGD_(level, fn, fmt, ...) \ + ALOGD_IF(DEBUG >= (level), #fn "(%x:%s, " fmt ")", mNodeID, mName, ##__VA_ARGS__) + +#define CLOG_LIFE(fn, fmt, ...) CLOGI_(ADebug::kDebugLifeCycle, fn, fmt, ##__VA_ARGS__) +#define CLOG_STATE(fn, fmt, ...) CLOGI_(ADebug::kDebugState, fn, fmt, ##__VA_ARGS__) +#define CLOG_CONFIG(fn, fmt, ...) CLOGI_(ADebug::kDebugConfig, fn, fmt, ##__VA_ARGS__) +#define CLOG_INTERNAL(fn, fmt, ...) CLOGD_(ADebug::kDebugInternalState, fn, fmt, ##__VA_ARGS__) + +#define CLOG_DEBUG_IF(cond, fn, fmt, ...) \ + ALOGD_IF(cond, #fn "(%x, " fmt ")", mNodeID, ##__VA_ARGS__) + +#define CLOG_BUFFER(fn, fmt, ...) \ + CLOG_DEBUG_IF(DEBUG >= ADebug::kDebugAll, fn, fmt, ##__VA_ARGS__) +#define CLOG_BUMPED_BUFFER(fn, fmt, ...) \ + CLOG_DEBUG_IF(DEBUG_BUMP >= ADebug::kDebugAll, fn, fmt, ##__VA_ARGS__) + +/* buffer formatting */ +#define BUFFER_FMT(port, fmt, ...) "%s:%u " fmt, portString(port), (port), ##__VA_ARGS__ +#define NEW_BUFFER_FMT(buffer_id, port, fmt, ...) \ + BUFFER_FMT(port, fmt ") (#%zu => %#x", ##__VA_ARGS__, mActiveBuffers.size(), (buffer_id)) + +#define SIMPLE_BUFFER(port, size, data) BUFFER_FMT(port, "%zu@%p", (size), (data)) +#define SIMPLE_NEW_BUFFER(buffer_id, port, size, data) \ + NEW_BUFFER_FMT(buffer_id, port, "%zu@%p", (size), (data)) + +#define EMPTY_BUFFER(addr, header) "%#x [%u@%p]", \ + (addr), (header)->nAllocLen, (header)->pBuffer +#define FULL_BUFFER(addr, header) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld]", \ + (intptr_t)(addr), (header)->nAllocLen, (header)->pBuffer, \ + (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp + +#define WITH_STATS_WRAPPER(fmt, ...) fmt " { IN=%zu/%zu OUT=%zu/%zu }", ##__VA_ARGS__, \ + mInputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexInput], \ + mOutputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexOutput] +// TRICKY: this is needed so formatting macros expand before substitution +#define WITH_STATS(fmt, ...) WITH_STATS_WRAPPER(fmt, ##__VA_ARGS__) + +template<class T> +static void InitOMXParams(T *params) { + memset(params, 0, sizeof(T)); + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} namespace android { @@ -56,8 +121,8 @@ struct BufferMeta { } memcpy((OMX_U8 *)mMem->pointer() + header->nOffset, - header->pBuffer + header->nOffset, - header->nFilledLen); + header->pBuffer + header->nOffset, + header->nFilledLen); } void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) { @@ -66,8 +131,8 @@ struct BufferMeta { } memcpy(header->pBuffer + header->nOffset, - (const OMX_U8 *)mMem->pointer() + header->nOffset, - header->nFilledLen); + (const OMX_U8 *)mMem->pointer() + header->nOffset, + header->nFilledLen); } void setGraphicBuffer(const sp<GraphicBuffer> &graphicBuffer) { @@ -89,8 +154,17 @@ OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = { &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone }; +static inline const char *portString(OMX_U32 portIndex) { + switch (portIndex) { + case kPortIndexInput: return "Input"; + case kPortIndexOutput: return "Output"; + case ~0: return "All"; + default: return "port"; + } +} + OMXNodeInstance::OMXNodeInstance( - OMX *owner, const sp<IOMXObserver> &observer) + OMX *owner, const sp<IOMXObserver> &observer, const char *name) : mOwner(owner), mNodeID(0), mHandle(NULL), @@ -100,15 +174,25 @@ OMXNodeInstance::OMXNodeInstance( , mBufferIDCount(0) #endif { + mName = ADebug::GetDebugName(name); + DEBUG = ADebug::GetDebugLevelFromProperty(name, "debug.stagefright.omx-debug"); + ALOGV("debug level for %s is %d", name, DEBUG); + DEBUG_BUMP = DEBUG; + mNumPortBuffers[0] = 0; + mNumPortBuffers[1] = 0; + mDebugLevelBumpPendingBuffers[0] = 0; + mDebugLevelBumpPendingBuffers[1] = 0; } OMXNodeInstance::~OMXNodeInstance() { + free(mName); CHECK(mHandle == NULL); } void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) { - CHECK(mHandle == NULL); mNodeID = node_id; + CLOG_LIFE(allocateNode, "handle=%p", handle); + CHECK(mHandle == NULL); mHandle = handle; } @@ -120,6 +204,7 @@ sp<GraphicBufferSource> OMXNodeInstance::getGraphicBufferSource() { void OMXNodeInstance::setGraphicBufferSource( const sp<GraphicBufferSource>& bufferSource) { Mutex::Autolock autoLock(mGraphicBufferSourceLock); + CLOG_INTERNAL(setGraphicBufferSource, "%p", bufferSource.get()); mGraphicBufferSource = bufferSource; } @@ -140,6 +225,7 @@ static status_t StatusFromOMXError(OMX_ERRORTYPE err) { case OMX_ErrorNone: return OK; case OMX_ErrorUnsupportedSetting: + case OMX_ErrorUnsupportedIndex: return ERROR_UNSUPPORTED; default: return UNKNOWN_ERROR; @@ -147,8 +233,14 @@ static status_t StatusFromOMXError(OMX_ERRORTYPE err) { } status_t OMXNodeInstance::freeNode(OMXMaster *master) { + CLOG_LIFE(freeNode, "handle=%p", mHandle); static int32_t kMaxNumIterations = 10; + // exit if we have already freed the node + if (mHandle == NULL) { + return OK; + } + // Transition the node from its current state all the way down // to "Loaded". // This ensures that all active buffers are properly freed even @@ -170,10 +262,11 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { OMX_ERRORTYPE err; int32_t iteration = 0; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone - && state != OMX_StateIdle - && state != OMX_StateInvalid) { + && state != OMX_StateIdle + && state != OMX_StateInvalid) { if (++iteration > kMaxNumIterations) { - ALOGE("component failed to enter Idle state, aborting."); + CLOGW("failed to enter Idle state (now %s(%d), aborting.", + asString(state), state); state = OMX_StateInvalid; break; } @@ -199,10 +292,11 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { OMX_ERRORTYPE err; int32_t iteration = 0; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone - && state != OMX_StateLoaded - && state != OMX_StateInvalid) { + && state != OMX_StateLoaded + && state != OMX_StateInvalid) { if (++iteration > kMaxNumIterations) { - ALOGE("component failed to enter Loaded state, aborting."); + CLOGW("failed to enter Loaded state (now %s(%d), aborting.", + asString(state), state); state = OMX_StateInvalid; break; } @@ -220,20 +314,18 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { break; default: - CHECK(!"should not be here, unknown state."); + LOG_ALWAYS_FATAL("unknown state %s(%#x).", asString(state), state); break; } - ALOGV("calling destroyComponentInstance"); + ALOGV("[%x:%s] calling destroyComponentInstance", mNodeID, mName); OMX_ERRORTYPE err = master->destroyComponentInstance( static_cast<OMX_COMPONENTTYPE *>(mHandle)); - ALOGV("destroyComponentInstance returned err %d", err); mHandle = NULL; - - if (err != OMX_ErrorNone) { - ALOGE("FreeHandle FAILED with error 0x%08x.", err); - } + CLOG_IF_ERROR(freeNode, err, ""); + free(mName); + mName = NULL; mOwner->invalidateNodeID(mNodeID); mNodeID = 0; @@ -265,7 +357,17 @@ status_t OMXNodeInstance::sendCommand( Mutex::Autolock autoLock(mLock); + // bump internal-state debug level for 2 input and output frames past a command + { + Mutex::Autolock _l(mDebugLock); + bumpDebugLevel_l(2 /* numInputBuffers */, 2 /* numOutputBuffers */); + } + + const char *paramString = + cmd == OMX_CommandStateSet ? asString((OMX_STATETYPE)param) : portString(param); + CLOG_STATE(sendCommand, "%s(%d), %s(%d)", asString(cmd), cmd, paramString, param); OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL); + CLOG_IF_ERROR(sendCommand, err, "%s(%d), %s(%d)", asString(cmd), cmd, paramString, param); return StatusFromOMXError(err); } @@ -274,17 +376,23 @@ status_t OMXNodeInstance::getParameter( Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params); - ALOGE_IF(err != OMX_ErrorNone, "getParameter(%d) ERROR: %#x", index, err); + OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + // some errors are expected for getParameter + if (err != OMX_ErrorNoMore) { + CLOG_IF_ERROR(getParameter, err, "%s(%#x)", asString(extIndex), index); + } return StatusFromOMXError(err); } status_t OMXNodeInstance::setParameter( - OMX_INDEXTYPE index, const void *params, size_t /* size */) { + OMX_INDEXTYPE index, const void *params, size_t size) { Mutex::Autolock autoLock(mLock); + OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + CLOG_CONFIG(setParameter, "%s(%#x), %zu@%p)", asString(extIndex), index, size, params); OMX_ERRORTYPE err = OMX_SetParameter( mHandle, index, const_cast<void *>(params)); - ALOGE_IF(err != OMX_ErrorNone, "setParameter(%d) ERROR: %#x", index, err); + CLOG_IF_ERROR(setParameter, err, "%s(%#x)", asString(extIndex), index); return StatusFromOMXError(err); } @@ -293,16 +401,23 @@ status_t OMXNodeInstance::getConfig( Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetConfig(mHandle, index, params); + OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + // some errors are expected for getConfig + if (err != OMX_ErrorNoMore) { + CLOG_IF_ERROR(getConfig, err, "%s(%#x)", asString(extIndex), index); + } return StatusFromOMXError(err); } status_t OMXNodeInstance::setConfig( - OMX_INDEXTYPE index, const void *params, size_t /* size */) { + OMX_INDEXTYPE index, const void *params, size_t size) { Mutex::Autolock autoLock(mLock); + OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + CLOG_CONFIG(setConfig, "%s(%#x), %zu@%p)", asString(extIndex), index, size, params); OMX_ERRORTYPE err = OMX_SetConfig( mHandle, index, const_cast<void *>(params)); - + CLOG_IF_ERROR(setConfig, err, "%s(%#x)", asString(extIndex), index); return StatusFromOMXError(err); } @@ -310,13 +425,14 @@ status_t OMXNodeInstance::getState(OMX_STATETYPE* state) { Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetState(mHandle, state); - + CLOG_IF_ERROR(getState, err, ""); return StatusFromOMXError(err); } status_t OMXNodeInstance::enableGraphicBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autoLock(mLock); + CLOG_CONFIG(enableGraphicBuffers, "%s:%u, %d", portString(portIndex), portIndex, enable); OMX_STRING name = const_cast<OMX_STRING>( "OMX.google.android.index.enableAndroidNativeBuffers"); @@ -324,32 +440,19 @@ status_t OMXNodeInstance::enableGraphicBuffers( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - if (enable) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - } - + CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } - OMX_VERSIONTYPE ver; - ver.s.nVersionMajor = 1; - ver.s.nVersionMinor = 0; - ver.s.nRevision = 0; - ver.s.nStep = 0; - EnableAndroidNativeBuffersParams params = { - sizeof(EnableAndroidNativeBuffersParams), ver, portIndex, enable, - }; + EnableAndroidNativeBuffersParams params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + params.enable = enable; err = OMX_SetParameter(mHandle, index, ¶ms); - - if (err != OMX_ErrorNone) { - ALOGE("OMX_EnableAndroidNativeBuffers failed with error %d (0x%08x)", - err, err); - - return UNKNOWN_ERROR; - } - - return OK; + CLOG_IF_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d", name, index, + portString(portIndex), portIndex, enable); + return StatusFromOMXError(err); } status_t OMXNodeInstance::getGraphicBufferUsage( @@ -362,26 +465,19 @@ status_t OMXNodeInstance::getGraphicBufferUsage( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - + CLOG_ERROR(getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } - OMX_VERSIONTYPE ver; - ver.s.nVersionMajor = 1; - ver.s.nVersionMinor = 0; - ver.s.nRevision = 0; - ver.s.nStep = 0; - GetAndroidNativeBufferUsageParams params = { - sizeof(GetAndroidNativeBufferUsageParams), ver, portIndex, 0, - }; + GetAndroidNativeBufferUsageParams params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; err = OMX_GetParameter(mHandle, index, ¶ms); - if (err != OMX_ErrorNone) { - ALOGE("OMX_GetAndroidNativeBufferUsage failed with error %d (0x%08x)", - err, err); - return UNKNOWN_ERROR; + CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u", name, index, + portString(portIndex), portIndex); + return StatusFromOMXError(err); } *usage = params.nUsage; @@ -393,6 +489,7 @@ status_t OMXNodeInstance::storeMetaDataInBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autolock(mLock); + CLOG_CONFIG(storeMetaDataInBuffers, "%s:%u en:%d", portString(portIndex), portIndex, enable); return storeMetaDataInBuffers_l( portIndex, enable, OMX_FALSE /* useGraphicBuffer */, NULL /* usingGraphicBufferInMetadata */); @@ -419,37 +516,42 @@ status_t OMXNodeInstance::storeMetaDataInBuffers_l( : OMX_ErrorBadParameter; if (err == OMX_ErrorNone) { *usingGraphicBufferInMetadata = OMX_TRUE; + name = graphicBufferName; } else { - *usingGraphicBufferInMetadata = OMX_FALSE; err = OMX_GetExtensionIndex(mHandle, name, &index); } - if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - return StatusFromOMXError(err); - } - - StoreMetaDataInBuffersParams params; - memset(¶ms, 0, sizeof(params)); - params.nSize = sizeof(params); + OMX_ERRORTYPE xerr = err; + if (err == OMX_ErrorNone) { + StoreMetaDataInBuffersParams params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + params.bStoreMetaData = enable; - // Version: 1.0.0.0 - params.nVersion.s.nVersionMajor = 1; + err = OMX_SetParameter(mHandle, index, ¶ms); + } - params.nPortIndex = portIndex; - params.bStoreMetaData = enable; - if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) { - ALOGE("OMX_SetParameter() failed for StoreMetaDataInBuffers: 0x%08x", err); + // don't log loud error if component does not support metadata mode on the output + if (err != OMX_ErrorNone) { *usingGraphicBufferInMetadata = OMX_FALSE; - return UNKNOWN_ERROR; + if (err == OMX_ErrorUnsupportedIndex && portIndex == kPortIndexOutput) { + CLOGW("component does not support metadata mode; using fallback"); + } else if (xerr != OMX_ErrorNone) { + CLOG_ERROR(getExtensionIndex, xerr, "%s", name); + } else { + CLOG_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d GB=%d", name, index, + portString(portIndex), portIndex, enable, useGraphicBuffer); + } } - return err; + return StatusFromOMXError(err); } status_t OMXNodeInstance::prepareForAdaptivePlayback( OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) { Mutex::Autolock autolock(mLock); + CLOG_CONFIG(prepareForAdaptivePlayback, "%s:%u en=%d max=%ux%u", + portString(portIndex), portIndex, enable, maxFrameWidth, maxFrameHeight); OMX_INDEXTYPE index; OMX_STRING name = const_cast<OMX_STRING>( @@ -457,33 +559,29 @@ status_t OMXNodeInstance::prepareForAdaptivePlayback( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGW_IF(enable, "OMX_GetExtensionIndex %s failed", name); + CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } PrepareForAdaptivePlaybackParams params; - params.nSize = sizeof(params); - params.nVersion.s.nVersionMajor = 1; - params.nVersion.s.nVersionMinor = 0; - params.nVersion.s.nRevision = 0; - params.nVersion.s.nStep = 0; - + InitOMXParams(¶ms); params.nPortIndex = portIndex; params.bEnable = enable; params.nMaxFrameWidth = maxFrameWidth; params.nMaxFrameHeight = maxFrameHeight; - if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) { - ALOGW("OMX_SetParameter failed for PrepareForAdaptivePlayback " - "with error %d (0x%08x)", err, err); - return UNKNOWN_ERROR; - } - return err; + + err = OMX_SetParameter(mHandle, index, ¶ms); + CLOG_IF_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d max=%ux%u", name, index, + portString(portIndex), portIndex, enable, maxFrameWidth, maxFrameHeight); + return StatusFromOMXError(err); } status_t OMXNodeInstance::configureVideoTunnelMode( OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, native_handle_t **sidebandHandle) { Mutex::Autolock autolock(mLock); + CLOG_CONFIG(configureVideoTunnelMode, "%s:%u tun=%d sync=%u", + portString(portIndex), portIndex, tunneled, audioHwSync); OMX_INDEXTYPE index; OMX_STRING name = const_cast<OMX_STRING>( @@ -491,36 +589,33 @@ status_t OMXNodeInstance::configureVideoTunnelMode( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("configureVideoTunnelMode extension is missing!"); + CLOG_ERROR_IF(tunneled, getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } ConfigureVideoTunnelModeParams tunnelParams; - tunnelParams.nSize = sizeof(tunnelParams); - tunnelParams.nVersion.s.nVersionMajor = 1; - tunnelParams.nVersion.s.nVersionMinor = 0; - tunnelParams.nVersion.s.nRevision = 0; - tunnelParams.nVersion.s.nStep = 0; - + InitOMXParams(&tunnelParams); tunnelParams.nPortIndex = portIndex; tunnelParams.bTunneled = tunneled; tunnelParams.nAudioHwSync = audioHwSync; err = OMX_SetParameter(mHandle, index, &tunnelParams); if (err != OMX_ErrorNone) { - ALOGE("configureVideoTunnelMode failed! (err %d).", err); - return UNKNOWN_ERROR; + CLOG_ERROR(setParameter, err, "%s(%#x): %s:%u tun=%d sync=%u", name, index, + portString(portIndex), portIndex, tunneled, audioHwSync); + return StatusFromOMXError(err); } err = OMX_GetParameter(mHandle, index, &tunnelParams); if (err != OMX_ErrorNone) { - ALOGE("GetVideoTunnelWindow failed! (err %d).", err); - return UNKNOWN_ERROR; + CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u tun=%d sync=%u", name, index, + portString(portIndex), portIndex, tunneled, audioHwSync); + return StatusFromOMXError(err); } if (sidebandHandle) { *sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow; } - return err; + return OK; } status_t OMXNodeInstance::useBuffer( @@ -537,14 +632,14 @@ status_t OMXNodeInstance::useBuffer( params->size(), static_cast<OMX_U8 *>(params->pointer())); if (err != OMX_ErrorNone) { - ALOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err); + CLOG_ERROR(useBuffer, err, SIMPLE_BUFFER(portIndex, params->size(), params->pointer())); delete buffer_meta; buffer_meta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, buffer_meta); @@ -558,6 +653,8 @@ status_t OMXNodeInstance::useBuffer( bufferSource->addCodecBuffer(header); } + CLOG_BUFFER(useBuffer, NEW_BUFFER_FMT( + *buffer, portIndex, "%zu@%p", params->size(), params->pointer())); return OK; } @@ -567,17 +664,14 @@ status_t OMXNodeInstance::useGraphicBuffer2_l( // port definition OMX_PARAM_PORTDEFINITIONTYPE def; - def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); - def.nVersion.s.nVersionMajor = 1; - def.nVersion.s.nVersionMinor = 0; - def.nVersion.s.nRevision = 0; - def.nVersion.s.nStep = 0; + InitOMXParams(&def); def.nPortIndex = portIndex; OMX_ERRORTYPE err = OMX_GetParameter(mHandle, OMX_IndexParamPortDefinition, &def); - if (err != OMX_ErrorNone) - { - ALOGE("%s::%d:Error getting OMX_IndexParamPortDefinition", __FUNCTION__, __LINE__); - return err; + if (err != OMX_ErrorNone) { + OMX_INDEXTYPE index = OMX_IndexParamPortDefinition; + CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u", + asString(index), index, portString(portIndex), portIndex); + return UNKNOWN_ERROR; } BufferMeta *bufferMeta = new BufferMeta(graphicBuffer); @@ -595,11 +689,11 @@ status_t OMXNodeInstance::useGraphicBuffer2_l( bufferHandle); if (err != OMX_ErrorNone) { - ALOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err); + CLOG_ERROR(useBuffer, err, BUFFER_FMT(portIndex, "%u@%p", def.nBufferSize, bufferHandle)); delete bufferMeta; bufferMeta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pBuffer, bufferHandle); @@ -608,7 +702,8 @@ status_t OMXNodeInstance::useGraphicBuffer2_l( *buffer = makeBufferID(header); addActiveBuffer(portIndex, *buffer); - + CLOG_BUFFER(useGraphicBuffer2, NEW_BUFFER_FMT( + *buffer, portIndex, "%u@%p", def.nBufferSize, bufferHandle)); return OK; } @@ -632,10 +727,8 @@ status_t OMXNodeInstance::useGraphicBuffer( OMX_STRING name = const_cast<OMX_STRING>( "OMX.google.android.index.useAndroidNativeBuffer"); OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); - if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - + CLOG_ERROR(getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } @@ -656,15 +749,15 @@ status_t OMXNodeInstance::useGraphicBuffer( err = OMX_SetParameter(mHandle, index, ¶ms); if (err != OMX_ErrorNone) { - ALOGE("OMX_UseAndroidNativeBuffer failed with error %d (0x%08x)", err, - err); + CLOG_ERROR(setParameter, err, "%s(%#x): %s:%u meta=%p GB=%p", name, index, + portString(portIndex), portIndex, bufferMeta, graphicBuffer->handle); delete bufferMeta; bufferMeta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, bufferMeta); @@ -672,12 +765,13 @@ status_t OMXNodeInstance::useGraphicBuffer( *buffer = makeBufferID(header); addActiveBuffer(portIndex, *buffer); - + CLOG_BUFFER(useGraphicBuffer, NEW_BUFFER_FMT( + *buffer, portIndex, "GB=%p", graphicBuffer->handle)); return OK; } status_t OMXNodeInstance::updateGraphicBufferInMeta( - OMX_U32 /* portIndex */, const sp<GraphicBuffer>& graphicBuffer, + OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer, OMX::buffer_id buffer) { Mutex::Autolock autoLock(mLock); @@ -688,7 +782,8 @@ status_t OMXNodeInstance::updateGraphicBufferInMeta( bufferMeta->setGraphicBuffer(graphicBuffer); metadata->eType = kMetadataBufferTypeGrallocSource; metadata->pHandle = graphicBuffer->handle; - + CLOG_BUFFER(updateGraphicBufferInMeta, "%s:%u, %#x := %p", + portString(portIndex), portIndex, buffer, graphicBuffer->handle); return OK; } @@ -714,19 +809,21 @@ status_t OMXNodeInstance::createInputSurface( // Retrieve the width and height of the graphic buffer, set when the // codec was configured. OMX_PARAM_PORTDEFINITIONTYPE def; - def.nSize = sizeof(def); - def.nVersion.s.nVersionMajor = 1; - def.nVersion.s.nVersionMinor = 0; - def.nVersion.s.nRevision = 0; - def.nVersion.s.nStep = 0; + InitOMXParams(&def); def.nPortIndex = portIndex; OMX_ERRORTYPE oerr = OMX_GetParameter( mHandle, OMX_IndexParamPortDefinition, &def); - CHECK(oerr == OMX_ErrorNone); + if (oerr != OMX_ErrorNone) { + OMX_INDEXTYPE index = OMX_IndexParamPortDefinition; + CLOG_ERROR(getParameter, oerr, "%s(%#x): %s:%u", + asString(index), index, portString(portIndex), portIndex); + return UNKNOWN_ERROR; + } if (def.format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque) { - ALOGE("createInputSurface requires COLOR_FormatSurface " - "(AndroidOpaque) color format"); + CLOGW("createInputSurface requires COLOR_FormatSurface " + "(AndroidOpaque) color format instead of %s(%#x)", + asString(def.format.video.eColorFormat), def.format.video.eColorFormat); return INVALID_OPERATION; } @@ -749,9 +846,9 @@ status_t OMXNodeInstance::signalEndOfInputStream() { // flag set). Seems easier than doing the equivalent from here. sp<GraphicBufferSource> bufferSource(getGraphicBufferSource()); if (bufferSource == NULL) { - ALOGW("signalEndOfInputStream can only be used with Surface input"); + CLOGW("signalEndOfInputStream can only be used with Surface input"); return INVALID_OPERATION; - }; + } return bufferSource->signalEndOfInputStream(); } @@ -768,14 +865,13 @@ status_t OMXNodeInstance::allocateBuffer( mHandle, &header, portIndex, buffer_meta, size); if (err != OMX_ErrorNone) { - ALOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err); - + CLOG_ERROR(allocateBuffer, err, BUFFER_FMT(portIndex, "%zu@", size)); delete buffer_meta; buffer_meta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, buffer_meta); @@ -789,6 +885,7 @@ status_t OMXNodeInstance::allocateBuffer( if (bufferSource != NULL && portIndex == kPortIndexInput) { bufferSource->addCodecBuffer(header); } + CLOG_BUFFER(allocateBuffer, NEW_BUFFER_FMT(*buffer, portIndex, "%zu@%p", size, *buffer_data)); return OK; } @@ -806,14 +903,14 @@ status_t OMXNodeInstance::allocateBufferWithBackup( mHandle, &header, portIndex, buffer_meta, params->size()); if (err != OMX_ErrorNone) { - ALOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err); - + CLOG_ERROR(allocateBufferWithBackup, err, + SIMPLE_BUFFER(portIndex, params->size(), params->pointer())); delete buffer_meta; buffer_meta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, buffer_meta); @@ -827,12 +924,16 @@ status_t OMXNodeInstance::allocateBufferWithBackup( bufferSource->addCodecBuffer(header); } + CLOG_BUFFER(allocateBufferWithBackup, NEW_BUFFER_FMT(*buffer, portIndex, "%zu@%p :> %p", + params->size(), params->pointer(), header->pBuffer)); + return OK; } status_t OMXNodeInstance::freeBuffer( OMX_U32 portIndex, OMX::buffer_id buffer) { Mutex::Autolock autoLock(mLock); + CLOG_BUFFER(freeBuffer, "%s:%u %#x", portString(portIndex), portIndex, buffer); removeActiveBuffer(portIndex, buffer); @@ -840,6 +941,7 @@ status_t OMXNodeInstance::freeBuffer( BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate); OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header); + CLOG_IF_ERROR(freeBuffer, err, "%s:%u %#x", portString(portIndex), portIndex, buffer); delete buffer_meta; buffer_meta = NULL; @@ -856,8 +958,18 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { header->nOffset = 0; header->nFlags = 0; - OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); + { + Mutex::Autolock _l(mDebugLock); + mOutputBuffersWithCodec.add(header); + CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header))); + } + OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); + if (err != OMX_ErrorNone) { + CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header)); + Mutex::Autolock _l(mDebugLock); + mOutputBuffersWithCodec.remove(header); + } return StatusFromOMXError(err); } @@ -870,14 +982,66 @@ status_t OMXNodeInstance::emptyBuffer( OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer); header->nFilledLen = rangeLength; header->nOffset = rangeOffset; - header->nFlags = flags; - header->nTimeStamp = timestamp; BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate); buffer_meta->CopyToOMX(header); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer); +} + +// log queued buffer activity for the next few input and/or output frames +// if logging at internal state level +void OMXNodeInstance::bumpDebugLevel_l(size_t numInputBuffers, size_t numOutputBuffers) { + if (DEBUG == ADebug::kDebugInternalState) { + DEBUG_BUMP = ADebug::kDebugAll; + if (numInputBuffers > 0) { + mDebugLevelBumpPendingBuffers[kPortIndexInput] = numInputBuffers; + } + if (numOutputBuffers > 0) { + mDebugLevelBumpPendingBuffers[kPortIndexOutput] = numOutputBuffers; + } + } +} + +void OMXNodeInstance::unbumpDebugLevel_l(size_t portIndex) { + if (mDebugLevelBumpPendingBuffers[portIndex]) { + --mDebugLevelBumpPendingBuffers[portIndex]; + } + if (!mDebugLevelBumpPendingBuffers[0] + && !mDebugLevelBumpPendingBuffers[1]) { + DEBUG_BUMP = DEBUG; + } +} + +status_t OMXNodeInstance::emptyBuffer_l( + OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr) { + header->nFlags = flags; + header->nTimeStamp = timestamp; + + { + Mutex::Autolock _l(mDebugLock); + mInputBuffersWithCodec.add(header); + + // bump internal-state debug level for 2 input frames past a buffer with CSD + if ((flags & OMX_BUFFERFLAG_CODECCONFIG) != 0) { + bumpDebugLevel_l(2 /* numInputBuffers */, 0 /* numOutputBuffers */); + } + + CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header))); + } + OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); + CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header)); + + { + Mutex::Autolock _l(mDebugLock); + if (err != OMX_ErrorNone) { + mInputBuffersWithCodec.remove(header); + } else if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) { + unbumpDebugLevel_l(kPortIndexInput); + } + } return StatusFromOMXError(err); } @@ -891,15 +1055,8 @@ status_t OMXNodeInstance::emptyDirectBuffer( header->nFilledLen = rangeLength; header->nOffset = rangeOffset; - header->nFlags = flags; - header->nTimeStamp = timestamp; - - OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); - if (err != OMX_ErrorNone) { - ALOGW("emptyDirectBuffer failed, OMX err=0x%x", err); - } - return StatusFromOMXError(err); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer); } status_t OMXNodeInstance::getExtensionIndex( @@ -912,11 +1069,25 @@ status_t OMXNodeInstance::getExtensionIndex( return StatusFromOMXError(err); } +inline static const char *asString(IOMX::InternalOptionType i, const char *def = "??") { + switch (i) { + case IOMX::INTERNAL_OPTION_SUSPEND: return "SUSPEND"; + case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY: + return "REPEAT_PREVIOUS_FRAME_DELAY"; + case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP: return "MAX_TIMESTAMP_GAP"; + case IOMX::INTERNAL_OPTION_START_TIME: return "START_TIME"; + case IOMX::INTERNAL_OPTION_TIME_LAPSE: return "TIME_LAPSE"; + default: return def; + } +} + status_t OMXNodeInstance::setInternalOption( OMX_U32 portIndex, IOMX::InternalOptionType type, const void *data, size_t size) { + CLOG_CONFIG(setInternalOption, "%s(%d): %s:%u %zu@%p", + asString(type), type, portString(portIndex), portIndex, size, data); switch (type) { case IOMX::INTERNAL_OPTION_SUSPEND: case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY: @@ -928,6 +1099,7 @@ status_t OMXNodeInstance::setInternalOption( getGraphicBufferSource(); if (bufferSource == NULL || portIndex != kPortIndexInput) { + CLOGW("setInternalOption is only for Surface input"); return ERROR_UNSUPPORTED; } @@ -937,6 +1109,7 @@ status_t OMXNodeInstance::setInternalOption( } bool suspend = *(bool *)data; + CLOG_CONFIG(setInternalOption, "suspend=%d", suspend); bufferSource->suspend(suspend); } else if (type == IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY){ @@ -945,7 +1118,7 @@ status_t OMXNodeInstance::setInternalOption( } int64_t delayUs = *(int64_t *)data; - + CLOG_CONFIG(setInternalOption, "delayUs=%lld", (long long)delayUs); return bufferSource->setRepeatPreviousFrameDelayUs(delayUs); } else if (type == IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP){ @@ -954,7 +1127,7 @@ status_t OMXNodeInstance::setInternalOption( } int64_t maxGapUs = *(int64_t *)data; - + CLOG_CONFIG(setInternalOption, "gapUs=%lld", (long long)maxGapUs); return bufferSource->setMaxTimestampGapUs(maxGapUs); } else if (type == IOMX::INTERNAL_OPTION_START_TIME) { if (size != sizeof(int64_t)) { @@ -962,13 +1135,18 @@ status_t OMXNodeInstance::setInternalOption( } int64_t skipFramesBeforeUs = *(int64_t *)data; - + CLOG_CONFIG(setInternalOption, "beforeUs=%lld", (long long)skipFramesBeforeUs); bufferSource->setSkipFramesBeforeUs(skipFramesBeforeUs); } else { // IOMX::INTERNAL_OPTION_TIME_LAPSE if (size != sizeof(int64_t) * 2) { return INVALID_OPERATION; } + int64_t timePerFrameUs = ((int64_t *)data)[0]; + int64_t timePerCaptureUs = ((int64_t *)data)[1]; + CLOG_CONFIG(setInternalOption, "perFrameUs=%lld perCaptureUs=%lld", + (long long)timePerFrameUs, (long long)timePerCaptureUs); + bufferSource->setTimeLapseUs((int64_t *)data); } @@ -987,6 +1165,16 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { OMX_BUFFERHEADERTYPE *buffer = findBufferHeader(msg.u.extended_buffer_data.buffer); + { + Mutex::Autolock _l(mDebugLock); + mOutputBuffersWithCodec.remove(buffer); + + CLOG_BUMPED_BUFFER( + FBD, WITH_STATS(FULL_BUFFER(msg.u.extended_buffer_data.buffer, buffer))); + + unbumpDebugLevel_l(kPortIndexOutput); + } + BufferMeta *buffer_meta = static_cast<BufferMeta *>(buffer->pAppPrivate); @@ -1002,16 +1190,23 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { return; } } else if (msg.type == omx_message::EMPTY_BUFFER_DONE) { + OMX_BUFFERHEADERTYPE *buffer = + findBufferHeader(msg.u.buffer_data.buffer); + + { + Mutex::Autolock _l(mDebugLock); + mInputBuffersWithCodec.remove(buffer); + + CLOG_BUMPED_BUFFER( + EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer))); + } + if (bufferSource != NULL) { // This is one of the buffers used exclusively by // GraphicBufferSource. // Don't dispatch a message back to ACodec, since it doesn't // know that anyone asked to have the buffer emptied and will // be very confused. - - OMX_BUFFERHEADERTYPE *buffer = - findBufferHeader(msg.u.buffer_data.buffer); - bufferSource->codecBufferEmptied(buffer); return; } @@ -1035,6 +1230,43 @@ void OMXNodeInstance::onGetHandleFailed() { // Don't try to acquire mLock here -- in rare circumstances this will hang. void OMXNodeInstance::onEvent( OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) { + const char *arg1String = "??"; + const char *arg2String = "??"; + ADebug::Level level = ADebug::kDebugInternalState; + + switch (event) { + case OMX_EventCmdComplete: + arg1String = asString((OMX_COMMANDTYPE)arg1); + switch (arg1) { + case OMX_CommandStateSet: + arg2String = asString((OMX_STATETYPE)arg2); + level = ADebug::kDebugState; + break; + case OMX_CommandFlush: + case OMX_CommandPortEnable: + { + // bump internal-state debug level for 2 input and output frames + Mutex::Autolock _l(mDebugLock); + bumpDebugLevel_l(2 /* numInputBuffers */, 2 /* numOutputBuffers */); + } + // fall through + default: + arg2String = portString(arg2); + } + break; + case OMX_EventError: + arg1String = asString((OMX_ERRORTYPE)arg1); + level = ADebug::kDebugLifeCycle; + break; + case OMX_EventPortSettingsChanged: + arg2String = asString((OMX_INDEXEXTTYPE)arg2); + // fall through + default: + arg1String = portString(arg1); + } + + CLOGI_(level, onEvent, "%s(%x), %s(%x), %s(%x)", + asString(event), event, arg1String, arg1, arg2String, arg2); const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource()); if (bufferSource != NULL @@ -1092,23 +1324,27 @@ void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) { active.mPortIndex = portIndex; active.mID = id; mActiveBuffers.push(active); + + if (portIndex < NELEM(mNumPortBuffers)) { + ++mNumPortBuffers[portIndex]; + } } void OMXNodeInstance::removeActiveBuffer( OMX_U32 portIndex, OMX::buffer_id id) { - bool found = false; for (size_t i = 0; i < mActiveBuffers.size(); ++i) { if (mActiveBuffers[i].mPortIndex == portIndex - && mActiveBuffers[i].mID == id) { - found = true; + && mActiveBuffers[i].mID == id) { mActiveBuffers.removeItemsAt(i); - break; + + if (portIndex < NELEM(mNumPortBuffers)) { + --mNumPortBuffers[portIndex]; + } + return; } } - if (!found) { - ALOGW("Attempt to remove an active buffer we know nothing about..."); - } + CLOGW("Attempt to remove an active buffer [%#x] we know nothing about...", id); } void OMXNodeInstance::freeActiveBuffers() { @@ -1129,7 +1365,7 @@ OMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) OMX::buffer_id buffer; do { // handle the very unlikely case of ID overflow if (++mBufferIDCount == 0) { - ++mBufferIDCount; + ++mBufferIDCount; } buffer = (OMX::buffer_id)mBufferIDCount; } while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0); diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp index 4999663..7f99dcd 100644 --- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -152,28 +152,28 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( OMX_PARAM_PORTDEFINITIONTYPE *defParams = (OMX_PARAM_PORTDEFINITIONTYPE *)params; - if (defParams->nPortIndex >= mPorts.size() - || defParams->nSize - != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { - return OMX_ErrorUndefined; + if (defParams->nPortIndex >= mPorts.size()) { + return OMX_ErrorBadPortIndex; + } + if (defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { + return OMX_ErrorUnsupportedSetting; } PortInfo *port = &mPorts.editItemAt(defParams->nPortIndex); - if (defParams->nBufferSize != port->mDef.nBufferSize) { - CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize); + // default behavior is that we only allow buffer size to increase + if (defParams->nBufferSize > port->mDef.nBufferSize) { port->mDef.nBufferSize = defParams->nBufferSize; } - if (defParams->nBufferCountActual - != port->mDef.nBufferCountActual) { - CHECK_GE(defParams->nBufferCountActual, - port->mDef.nBufferCountMin); - - port->mDef.nBufferCountActual = defParams->nBufferCountActual; + if (defParams->nBufferCountActual < port->mDef.nBufferCountMin) { + ALOGW("component requires at least %u buffers (%u requested)", + port->mDef.nBufferCountMin, defParams->nBufferCountActual); + return OMX_ErrorUnsupportedSetting; } + port->mDef.nBufferCountActual = defParams->nBufferCountActual; return OMX_ErrorNone; } diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp index 646cd32..df978f8 100644 --- a/media/libstagefright/omx/SoftOMXComponent.cpp +++ b/media/libstagefright/omx/SoftOMXComponent.cpp @@ -283,7 +283,7 @@ OMX_ERRORTYPE SoftOMXComponent::setConfig( OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex( const char * /* name */, OMX_INDEXTYPE * /* index */) { - return OMX_ErrorUndefined; + return OMX_ErrorUnsupportedIndex; } OMX_ERRORTYPE SoftOMXComponent::useBuffer( diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp index f2825dd..43e0269 100644 --- a/media/libstagefright/tests/Utils_test.cpp +++ b/media/libstagefright/tests/Utils_test.cpp @@ -24,6 +24,7 @@ #include <unistd.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AStringUtils.h> #include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/Utils.h> @@ -32,6 +33,100 @@ namespace android { class UtilsTest : public ::testing::Test { }; +TEST_F(UtilsTest, TestStringUtils) { + ASSERT_EQ(AStringUtils::Compare("Audio", "AudioExt", 5, false), 0); + ASSERT_EQ(AStringUtils::Compare("Audio", "audiOExt", 5, true), 0); + ASSERT_NE(AStringUtils::Compare("Audio", "audioExt", 5, false), 0); + ASSERT_NE(AStringUtils::Compare("Audio", "AudiOExt", 5, false), 0); + + ASSERT_LT(AStringUtils::Compare("Audio", "AudioExt", 7, false), 0); + ASSERT_LT(AStringUtils::Compare("Audio", "audiOExt", 7, true), 0); + + ASSERT_GT(AStringUtils::Compare("AudioExt", "Audio", 7, false), 0); + ASSERT_GT(AStringUtils::Compare("audiOext", "Audio", 7, true), 0); + + ASSERT_LT(AStringUtils::Compare("Audio", "Video", 5, false), 0); + ASSERT_LT(AStringUtils::Compare("Audio1", "Audio2", 6, false), 0); + ASSERT_LT(AStringUtils::Compare("audio", "VIDEO", 5, true), 0); + ASSERT_LT(AStringUtils::Compare("audio1", "AUDIO2", 6, true), 0); + + ASSERT_GT(AStringUtils::Compare("Video", "Audio", 5, false), 0); + ASSERT_GT(AStringUtils::Compare("Audio2", "Audio1", 6, false), 0); + ASSERT_GT(AStringUtils::Compare("VIDEO", "audio", 5, true), 0); + ASSERT_GT(AStringUtils::Compare("AUDIO2", "audio1", 6, true), 0); + + ASSERT_TRUE(AStringUtils::MatchesGlob("AudioA", 5, "AudioB", 5, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 6, "AudioA", 5, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 5, "AudioA", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 5, "audiOB", 5, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("AudioA", 5, "audiOB", 5, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 6, "AudioA", 5, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 5, "AudioA", 6, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 6, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 0, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 0, false)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("*ring1", 5, "String8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*ring2", 5, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring4", 5, "StRing8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring5", 5, "StrinG8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring8", 5, "String8", 7, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring8", 5, "String8", 7, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*1", 4, "String8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*2", 4, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*3", 4, "string8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*4", 4, "StRing8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*5", 4, "AString8", 7, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*6", 4, "AString8", 7, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ng1", 6, "String8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng2", 6, "string8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng3", 6, "StRing8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng4", 6, "StriNg8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng5", 6, "StrinG8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ng6", 6, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng8", 6, "AString8", 7, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng1", 6, "String16", 7, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ing9", 7, "String8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ringA", 8, "String8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng8", 6, "AString8", 7, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng1", 6, "String16", 7, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ing9", 7, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ringA", 8, "String8", 6, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "bestrestroom", 9, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "bestrestrestroom", 13, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*str*stro", 8, "bestrestrestroom", 14, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str*1", 9, "bestrestrestroom", 14, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "beSTReSTRoom", 9, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "beSTRestreSTRoom", 13, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*str*stro", 8, "bestreSTReSTRoom", 14, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str*1", 9, "bestreSTReSTRoom", 14, true)); +} + +TEST_F(UtilsTest, TestDebug) { +#define LVL(x) (ADebug::Level)(x) + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "", LVL(5)), LVL(5)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", " \t \n ", LVL(2)), LVL(2)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "video", "\t\n 3 \t\n:\t\n video \t\n", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo,2:vid*", LVL(5)), LVL(2)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "avideo", "\t\n 3 \t\n:\t\n avideo \t\n,\t\n 2 \t\n:\t\n video \t\n", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "audio.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(2)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "video.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(4)); +#undef LVL +} + TEST_F(UtilsTest, TestFourCC) { ASSERT_EQ(FOURCC('s', 't', 'm' , 'u'), 'stmu'); } |