diff options
28 files changed, 579 insertions, 162 deletions
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp index ee3189b..eeb64c3 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp @@ -43,10 +43,18 @@ android::status_t CryptoFactory::createPlugin( return android::BAD_VALUE; } - android::sp<Session> session = SessionLibrary::get()->findSession( - data, size); - *plugin = new CryptoPlugin(session); - return android::OK; + android::Vector<uint8_t> sessionId; + sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size); + + CryptoPlugin *clearKeyPlugin = new CryptoPlugin(sessionId); + android::status_t result = clearKeyPlugin->getInitStatus(); + if (result == android::OK) { + *plugin = clearKeyPlugin; + } else { + delete clearKeyPlugin; + *plugin = NULL; + } + return result; } } // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp index adad136..53cbf80 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp @@ -19,9 +19,9 @@ #include <utils/Log.h> #include <media/stagefright/MediaErrors.h> -#include <utils/Errors.h> #include "CryptoPlugin.h" +#include "SessionLibrary.h" namespace clearkeydrm { @@ -80,4 +80,18 @@ ssize_t CryptoPlugin::decrypt(bool secure, const KeyId keyId, const Iv iv, } } +android::status_t CryptoPlugin::setMediaDrmSession( + const android::Vector<uint8_t>& sessionId) { + if (!sessionId.size()) { + mSession.clear(); + } else { + mSession = SessionLibrary::get()->findSession(sessionId); + if (!mSession.get()) { + return android::ERROR_DRM_SESSION_NOT_OPENED; + } + } + return android::OK; +} + + } // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h index 002d9e0..fd38f28 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h +++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h @@ -31,7 +31,10 @@ namespace clearkeydrm { class CryptoPlugin : public android::CryptoPlugin { public: - CryptoPlugin(const android::sp<Session>& session) : mSession(session) {} + CryptoPlugin(const android::Vector<uint8_t>& sessionId) { + mInitStatus = setMediaDrmSession(sessionId); + } + virtual ~CryptoPlugin() {} virtual bool requiresSecureDecoderComponent(const char* mime) const { @@ -45,10 +48,16 @@ public: const SubSample* subSamples, size_t numSubSamples, void* dstPtr, android::AString* errorDetailMsg); + virtual android::status_t setMediaDrmSession( + const android::Vector<uint8_t>& sessionId); + + android::status_t getInitStatus() const {return mInitStatus;} + private: DISALLOW_EVIL_CONSTRUCTORS(CryptoPlugin); android::sp<Session> mSession; + android::status_t mInitStatus; }; } // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp index 6b8c772..e5ee403 100644 --- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp @@ -37,7 +37,9 @@ status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) { status_t DrmPlugin::closeSession(const Vector<uint8_t>& sessionId) { sp<Session> session = mSessionLibrary->findSession(sessionId); - mSessionLibrary->destroySession(session); + if (session.get()) { + mSessionLibrary->destroySession(session); + } return android::OK; } @@ -55,8 +57,11 @@ status_t DrmPlugin::getKeyRequest( return android::ERROR_DRM_CANNOT_HANDLE; } *keyRequestType = DrmPlugin::kKeyRequestType_Initial; - sp<Session> session = mSessionLibrary->findSession(scope); defaultUrl.clear(); + sp<Session> session = mSessionLibrary->findSession(scope); + if (!session.get()) { + return android::ERROR_DRM_SESSION_NOT_OPENED; + } return session->getKeyRequest(initData, initDataType, &request); } @@ -65,6 +70,9 @@ status_t DrmPlugin::provideKeyResponse( const Vector<uint8_t>& response, Vector<uint8_t>& keySetId) { sp<Session> session = mSessionLibrary->findSession(scope); + if (!session.get()) { + return android::ERROR_DRM_SESSION_NOT_OPENED; + } status_t res = session->provideKeyResponse(response); if (res == android::OK) { keySetId.clear(); diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp index d047c53..46d7f77 100644 --- a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp +++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp @@ -63,13 +63,6 @@ const sp<Session>& SessionLibrary::findSession( return mSessions.valueFor(sessionId); } -const sp<Session>& SessionLibrary::findSession( - const void* data, size_t size) { - Vector<uint8_t> sessionId; - sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size); - return findSession(sessionId); -} - void SessionLibrary::destroySession(const sp<Session>& session) { Mutex::Autolock lock(mSessionsLock);\ mSessions.removeItem(session->sessionId()); diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/SessionLibrary.h index 56c8828..199ad64 100644 --- a/drm/mediadrm/plugins/clearkey/SessionLibrary.h +++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.h @@ -36,8 +36,6 @@ public: const android::sp<Session>& findSession( const android::Vector<uint8_t>& sessionId); - const android::sp<Session>& findSession(const void* data, size_t size); - void destroySession(const android::sp<Session>& session); private: @@ -50,7 +48,7 @@ private: android::Mutex mSessionsLock; uint32_t mNextSessionId; - android::KeyedVector<android::Vector<uint8_t>, android::sp<Session> > + android::DefaultKeyedVector<android::Vector<uint8_t>, android::sp<Session> > mSessions; }; diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 26cc73e..84fdf83 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -92,7 +92,7 @@ public: node_id node, OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) = 0; - virtual status_t configureVideoTunnelMode( + virtual status_t configureVideoTunnelMode( node_id node, OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, native_handle_t **sidebandHandle) = 0; @@ -152,13 +152,23 @@ public: virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer) = 0; - virtual status_t fillBuffer(node_id node, buffer_id buffer) = 0; - + enum { + kFenceTimeoutMs = 1000 + }; + // Calls OMX_FillBuffer on buffer, and passes |fenceFd| to component if it supports + // fences. Otherwise, it waits on |fenceFd| before calling OMX_FillBuffer. + // Takes ownership of |fenceFd| even if this call fails. + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd = -1) = 0; + + // Calls OMX_EmptyBuffer on buffer (after updating buffer header with |range_offset|, + // |range_length|, |flags| and |timestamp|). Passes |fenceFd| to component if it + // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer. + // Takes ownership of |fenceFd| even if this call fails. virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) = 0; + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1) = 0; virtual status_t getExtensionIndex( node_id node, @@ -190,6 +200,7 @@ struct omx_message { } type; IOMX::node_id node; + int fenceFd; // used for EMPTY_BUFFER_DONE and FILL_BUFFER_DONE; client must close this union { // if type == EVENT diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index bbecc80..f7a3df7 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -160,11 +160,25 @@ private: sp<ABuffer> mData; sp<GraphicBuffer> mGraphicBuffer; + int mFenceFd; + + // The following field and 4 methods are used for debugging only + bool mIsReadFence; + // Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored. + void setReadFence(int fenceFd, const char *dbg); + void setWriteFence(int fenceFd, const char *dbg); + // Log error, if the current fence is not a read/write fence. + void checkReadFence(const char *dbg); + void checkWriteFence(const char *dbg); }; static const char *_asString(BufferInfo::Status s); void dumpBuffers(OMX_U32 portIndex); + // If |fd| is non-negative, waits for fence with |fd| and logs an error if it fails. Returns + // the error code or OK on success. If |fd| is negative, it returns OK + status_t waitForFence(int fd, const char *dbg); + #if TRACK_BUFFER_TIMING struct BufferStats { int64_t mEmptyBufferTimeUs; diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index cac2f7f..ca1cdc7 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -511,11 +511,15 @@ public: return reply.readInt32(); } - virtual status_t fillBuffer(node_id node, buffer_id buffer) { + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32((int32_t)buffer); + data.writeInt32(fenceFd >= 0); + if (fenceFd >= 0) { + data.writeFileDescriptor(fenceFd, true /* takeOwnership */); + } remote()->transact(FILL_BUFFER, data, &reply); return reply.readInt32(); @@ -525,7 +529,7 @@ public: node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); @@ -534,6 +538,10 @@ public: data.writeInt32(range_length); data.writeInt32(flags); data.writeInt64(timestamp); + data.writeInt32(fenceFd >= 0); + if (fenceFd >= 0) { + data.writeFileDescriptor(fenceFd, true /* takeOwnership */); + } remote()->transact(EMPTY_BUFFER, data, &reply); return reply.readInt32(); @@ -1012,7 +1020,9 @@ status_t BnOMX::onTransact( node_id node = (node_id)data.readInt32(); buffer_id buffer = (buffer_id)data.readInt32(); - reply->writeInt32(fillBuffer(node, buffer)); + bool haveFence = data.readInt32(); + int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + reply->writeInt32(fillBuffer(node, buffer, fenceFd)); return NO_ERROR; } @@ -1027,11 +1037,10 @@ status_t BnOMX::onTransact( OMX_U32 range_length = data.readInt32(); OMX_U32 flags = data.readInt32(); OMX_TICKS timestamp = data.readInt64(); - - reply->writeInt32( - emptyBuffer( - node, buffer, range_offset, range_length, - flags, timestamp)); + bool haveFence = data.readInt32(); + int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + reply->writeInt32(emptyBuffer( + node, buffer, range_offset, range_length, flags, timestamp, fenceFd)); return NO_ERROR; } @@ -1072,7 +1081,9 @@ public: Parcel data, reply; data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); data.write(&msg, sizeof(msg)); - + if (msg.fenceFd >= 0) { + data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */); + } ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg)); remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY); @@ -1090,6 +1101,9 @@ status_t BnOMXObserver::onTransact( omx_message msg; data.read(&msg, sizeof(msg)); + if (msg.fenceFd >= 0) { + msg.fenceFd = ::dup(data.readFileDescriptor()); + } ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg)); diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 2ba05c0..02c1c38 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1853,20 +1853,23 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( me, buffer->raw, buffer->size, me->mCallbackCookie, CB_EVENT_FILL_BUFFER); - if ((me->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0 && - actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { - // We've reached EOS but the audio track is not stopped yet, - // keep playing silence. + // Log when no data is returned from the callback. + // (1) We may have no data (especially with network streaming sources). + // (2) We may have reached the EOS and the audio track is not stopped yet. + // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS. + // NuPlayerRenderer will return zero when it doesn't have data (it doesn't block to fill). + // + // This is a benign busy-wait, with the next data request generated 10 ms or more later; + // nevertheless for power reasons, we don't want to see too many of these. - memset(buffer->raw, 0, buffer->size); - actualSize = buffer->size; - } + ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned"); + me->mBytesWritten += actualSize; // benign race with reader. buffer->size = actualSize; } break; - case AudioTrack::EVENT_STREAM_END: + // currently only occurs for offloaded callbacks ALOGV("callbackwrapper: deliver EVENT_STREAM_END"); (*me->mCallback)(me, NULL /* buffer */, 0 /* size */, me->mCallbackCookie, CB_EVENT_STREAM_END); @@ -1879,11 +1882,15 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( break; case AudioTrack::EVENT_UNDERRUN: - // This occurs when there is no data available, typically occurring + // This occurs when there is no data available, typically // when there is a failure to supply data to the AudioTrack. It can also // occur in non-offloaded mode when the audio device comes out of standby. // - // If you see this at the start of playback, there probably was a glitch. + // If an AudioTrack underruns it outputs silence. Since this happens suddenly + // it may sound like an audible pop or glitch. + // + // The underrun event is sent once per track underrun; the condition is reset + // when more data is sent to the AudioTrack. ALOGI("callbackwrapper: EVENT_UNDERRUN (discarded)"); break; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 409dedf..89c261c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -19,7 +19,7 @@ #include <utils/Log.h> #include "NuPlayerRenderer.h" - +#include <cutils/properties.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -36,6 +36,25 @@ namespace android { +/* + * Example of common configuration settings in shell script form + + #Turn offload audio off (use PCM for Play Music) -- AudioPolicyManager + adb shell setprop audio.offload.disable 1 + + #Allow offload audio with video (requires offloading to be enabled) -- AudioPolicyManager + adb shell setprop audio.offload.video 1 + + #Use audio callbacks for PCM data + adb shell setprop media.stagefright.audio.cbk 1 + + * These configurations take effect for the next track played (not the current track). + */ + +static inline bool getUseAudioCallbackSetting() { + return property_get_bool("media.stagefright.audio.cbk", false /* default_value */); +} + // Maximum time in paused state when offloading audio decompression. When elapsed, the AudioSink // is closed to allow the audio DSP to power down. static const int64_t kOffloadPauseMaxUs = 10000000ll; @@ -87,6 +106,7 @@ NuPlayer::Renderer::Renderer( mCurrentPcmInfo(AUDIO_PCMINFO_INITIALIZER), mTotalBuffersQueued(0), mLastAudioBufferDrained(0), + mUseAudioCallback(false), mWakeLock(new AWakeLock()) { mMediaClock = new MediaClock; mPlaybackRate = mPlaybackSettings.mSpeed; @@ -593,7 +613,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) { if (mDrainAudioQueuePending || mSyncQueues || mPaused - || offloadingAudio()) { + || mUseAudioCallback) { return; } @@ -642,12 +662,14 @@ size_t NuPlayer::Renderer::AudioSinkCallback( case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END: { + ALOGV("AudioSink::CB_EVENT_STREAM_END"); me->notifyEOS(true /* audio */, ERROR_END_OF_STREAM); break; } case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN: { + ALOGV("AudioSink::CB_EVENT_TEAR_DOWN"); me->notifyAudioTearDown(); break; } @@ -659,7 +681,7 @@ size_t NuPlayer::Renderer::AudioSinkCallback( size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { Mutex::Autolock autoLock(mLock); - if (!offloadingAudio() || mPaused) { + if (!mUseAudioCallback || mPaused) { return 0; } @@ -667,13 +689,13 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { size_t sizeCopied = 0; bool firstEntry = true; + QueueEntry *entry; // will be valid after while loop if hasEOS is set. while (sizeCopied < size && !mAudioQueue.empty()) { - QueueEntry *entry = &*mAudioQueue.begin(); + entry = &*mAudioQueue.begin(); if (entry->mBuffer == NULL) { // EOS hasEOS = true; mAudioQueue.erase(mAudioQueue.begin()); - entry = NULL; break; } @@ -681,7 +703,7 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { firstEntry = false; int64_t mediaTimeUs; CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); - ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6); + ALOGV("fillAudioBuffer: rendering audio at media time %.2f secs", mediaTimeUs / 1E6); setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs); } @@ -714,10 +736,28 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { mMediaClock->updateAnchor(nowMediaUs, nowUs, INT64_MAX); } + // for non-offloaded audio, we need to compute the frames written because + // there is no EVENT_STREAM_END notification. The frames written gives + // an estimate on the pending played out duration. + if (!offloadingAudio()) { + mNumFramesWritten += sizeCopied / mAudioSink->frameSize(); + } + if (hasEOS) { (new AMessage(kWhatStopAudioSink, this))->post(); + // As there is currently no EVENT_STREAM_END callback notification for + // non-offloaded audio tracks, we need to post the EOS ourselves. + if (!offloadingAudio()) { + int64_t postEOSDelayUs = 0; + if (mAudioSink->needsTrailingPadding()) { + postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs()); + } + ALOGV("fillAudioBuffer: notifyEOS " + "mNumFramesWritten:%u finalResult:%d postEOSDelay:%lld", + mNumFramesWritten, entry->mFinalResult, (long long)postEOSDelayUs); + notifyEOS(true /* audio */, entry->mFinalResult, postEOSDelayUs); + } } - return sizeCopied; } @@ -778,7 +818,8 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { if (entry->mOffset == 0 && entry->mBuffer->size() > 0) { int64_t mediaTimeUs; CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); - ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6); + ALOGV("onDrainAudioQueue: rendering audio at media time %.2f secs", + mediaTimeUs / 1E6); onNewAudioMediaTime(mediaTimeUs); } @@ -1230,9 +1271,8 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { ++mAudioDrainGeneration; prepareForMediaRenderingStart_l(); - if (offloadingAudio()) { - clearAudioFirstAnchorTime_l(); - } + // the frame count will be reset after flush. + clearAudioFirstAnchorTime_l(); } mDrainAudioQueuePending = false; @@ -1590,6 +1630,7 @@ status_t NuPlayer::Renderer::onOpenAudioSink( offloadFlags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER; audioSinkChanged = true; mAudioSink->close(); + err = mAudioSink->open( sampleRate, numChannels, @@ -1623,6 +1664,7 @@ status_t NuPlayer::Renderer::onOpenAudioSink( mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; ALOGV("openAudioSink: offload failed"); } + mUseAudioCallback = true; // offload mode transfers data through callback } } if (!offloadOnly && !offloadingAudio()) { @@ -1646,14 +1688,17 @@ status_t NuPlayer::Renderer::onOpenAudioSink( audioSinkChanged = true; mAudioSink->close(); mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + // Note: It is possible to set up the callback, but not use it to send audio data. + // This requires a fix in AudioSink to explicitly specify the transfer mode. + mUseAudioCallback = getUseAudioCallbackSetting(); status_t err = mAudioSink->open( sampleRate, numChannels, (audio_channel_mask_t)channelMask, AUDIO_FORMAT_PCM_16_BIT, 8 /* bufferCount */, - NULL, - NULL, + mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL, + mUseAudioCallback ? this : NULL, (audio_output_flags_t)pcmFlags, NULL, true /* doNotReconnect */); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index fbdf5bf..c2fea40 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -194,6 +194,7 @@ private: int32_t mTotalBuffersQueued; int32_t mLastAudioBufferDrained; + bool mUseAudioCallback; sp<AWakeLock> mWakeLock; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 08045d1..3a98e8c 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -132,6 +132,7 @@ struct CodecObserver : public BnOMXObserver { case omx_message::EMPTY_BUFFER_DONE: { msg->setInt32("buffer", omx_msg.u.buffer_data.buffer); + msg->setInt32("fence_fd", omx_msg.fenceFd); break; } @@ -151,6 +152,8 @@ struct CodecObserver : public BnOMXObserver { msg->setInt64( "timestamp", omx_msg.u.extended_buffer_data.timestamp); + msg->setInt32( + "fence_fd", omx_msg.fenceFd); break; } @@ -199,13 +202,14 @@ protected: private: bool onOMXMessage(const sp<AMessage> &msg); - bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID); + bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd); bool onOMXFillBufferDone( IOMX::buffer_id bufferID, size_t rangeOffset, size_t rangeLength, OMX_U32 flags, - int64_t timeUs); + int64_t timeUs, + int fenceFd); void getMoreInputDataIfPossible(); @@ -407,6 +411,38 @@ private: //////////////////////////////////////////////////////////////////////////////// +void ACodec::BufferInfo::setWriteFence(int fenceFd, const char *dbg) { + if (mFenceFd >= 0) { + ALOGW("OVERWRITE OF %s fence %d by write fence %d in %s", + mIsReadFence ? "read" : "write", mFenceFd, fenceFd, dbg); + } + mFenceFd = fenceFd; + mIsReadFence = false; +} + +void ACodec::BufferInfo::setReadFence(int fenceFd, const char *dbg) { + if (mFenceFd >= 0) { + ALOGW("OVERWRITE OF %s fence %d by read fence %d in %s", + mIsReadFence ? "read" : "write", mFenceFd, fenceFd, dbg); + } + mFenceFd = fenceFd; + mIsReadFence = true; +} + +void ACodec::BufferInfo::checkWriteFence(const char *dbg) { + if (mFenceFd >= 0 && mIsReadFence) { + ALOGD("REUSING read fence %d as write fence in %s", mFenceFd, dbg); + } +} + +void ACodec::BufferInfo::checkReadFence(const char *dbg) { + if (mFenceFd >= 0 && !mIsReadFence) { + ALOGD("REUSING write fence %d as read fence in %s", mFenceFd, dbg); + } +} + +//////////////////////////////////////////////////////////////////////////////// + ACodec::ACodec() : mQuirks(0), mNode(0), @@ -640,11 +676,12 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { // cancel undequeued buffers to new surface if (!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment) { for (size_t i = 0; i < buffers.size(); ++i) { - const BufferInfo &info = buffers[i]; + BufferInfo &info = buffers.editItemAt(i); if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGV("canceling buffer %p", info.mGraphicBuffer->getNativeBuffer()); err = nativeWindow->cancelBuffer( - nativeWindow, info.mGraphicBuffer->getNativeBuffer(), -1); + nativeWindow, info.mGraphicBuffer->getNativeBuffer(), info.mFenceFd); + info.mFenceFd = -1; if (err != OK) { ALOGE("failed to cancel buffer %p to the new surface: %s (%d)", info.mGraphicBuffer->getNativeBuffer(), @@ -721,6 +758,7 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; + info.mFenceFd = -1; uint32_t requiresAllocateBufferBit = (portIndex == kPortIndexInput) @@ -925,7 +963,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < bufferCount; i++) { ANativeWindowBuffer *buf; - err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); + int fenceFd; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; @@ -934,6 +973,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; + info.mFenceFd = fenceFd; + info.mIsReadFence = false; info.mData = new ABuffer(NULL /* data */, bufferSize /* capacity */); info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); @@ -1004,6 +1045,7 @@ status_t ACodec::allocateOutputMetadataBuffers() { for (OMX_U32 i = 0; i < bufferCount; i++) { BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; + info.mFenceFd = -1; info.mGraphicBuffer = NULL; info.mDequeuedAt = mDequeueCounter; @@ -1040,7 +1082,8 @@ status_t ACodec::allocateOutputMetadataBuffers() { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); ANativeWindowBuffer *buf; - err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); + int fenceFd; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; @@ -1050,6 +1093,7 @@ status_t ACodec::allocateOutputMetadataBuffers() { mOMX->updateGraphicBufferInMeta( mNode, kPortIndexOutput, graphicBuffer, info->mBufferID); info->mStatus = BufferInfo::OWNED_BY_US; + info->setWriteFence(fenceFd, "allocateOutputMetadataBuffers for legacy"); info->mGraphicBuffer = graphicBuffer; } @@ -1083,7 +1127,9 @@ status_t ACodec::submitOutputMetadataBuffer() { mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get()); --mMetadataBuffersToSubmit; - status_t err = mOMX->fillBuffer(mNode, info->mBufferID); + info->checkWriteFence("submitOutputMetadataBuffer"); + status_t err = mOMX->fillBuffer(mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err == OK) { info->mStatus = BufferInfo::OWNED_BY_COMPONENT; } @@ -1091,6 +1137,16 @@ status_t ACodec::submitOutputMetadataBuffer() { return err; } +status_t ACodec::waitForFence(int fd, const char *dbg ) { + status_t res = OK; + if (fd >= 0) { + sp<Fence> fence = new Fence(fd); + res = fence->wait(IOMX::kFenceTimeoutMs); + ALOGW_IF(res != OK, "FENCE TIMEOUT for %d in %s", fd, dbg); + } + return res; +} + // static const char *ACodec::_asString(BufferInfo::Status s) { switch (s) { @@ -1123,8 +1179,10 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { ALOGV("[%s] Calling cancelBuffer on buffer %u", mComponentName.c_str(), info->mBufferID); + info->checkWriteFence("cancelBufferToNativeWindow"); int err = mNativeWindow->cancelBuffer( - mNativeWindow.get(), info->mGraphicBuffer.get(), -1); + mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd); + info->mFenceFd = -1; ALOGW_IF(err != 0, "[%s] can not return buffer %u to native window", mComponentName.c_str(), info->mBufferID); @@ -1144,9 +1202,11 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { return NULL; } + int fenceFd = -1; do { - if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { - ALOGE("dequeueBuffer failed."); + status_t err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); + if (err != 0) { + ALOGE("dequeueBuffer failed: %s(%d).", asString(err), err); return NULL; } @@ -1170,7 +1230,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { } ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer()); info->mStatus = BufferInfo::OWNED_BY_US; - + info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow"); return info; } } @@ -1213,6 +1273,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { // discard buffer in LRU info and replace with new buffer oldest->mGraphicBuffer = new GraphicBuffer(buf, false); oldest->mStatus = BufferInfo::OWNED_BY_US; + oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest"); mOMX->updateGraphicBufferInMeta( mNode, kPortIndexOutput, oldest->mGraphicBuffer, @@ -1224,7 +1285,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)", (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]), mDequeueCounter - oldest->mDequeuedAt, - grallocMeta->hHandle, + grallocMeta->pHandle, oldest->mGraphicBuffer->handle, oldest->mData->base()); } else if (mOutputMetadataType == kMetadataBufferTypeANWBuffer) { VideoNativeMetadata *nativeMeta = @@ -1277,6 +1338,18 @@ status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) { BufferInfo *info = &mBuffers[portIndex].editItemAt(i); status_t err = OK; + // there should not be any fences in the metadata + MetadataBufferType type = + portIndex == kPortIndexOutput ? mOutputMetadataType : mInputMetadataType; + if (type == kMetadataBufferTypeANWBuffer && info->mData != NULL + && info->mData->size() >= sizeof(VideoNativeMetadata)) { + int fenceFd = ((VideoNativeMetadata *)info->mData->data())->nFenceFd; + if (fenceFd >= 0) { + ALOGW("unreleased fence (%d) in %s metadata buffer %zu", + fenceFd, portIndex == kPortIndexInput ? "input" : "output", i); + } + } + switch (info->mStatus) { case BufferInfo::OWNED_BY_US: if (portIndex == kPortIndexOutput && mNativeWindow != NULL) { @@ -1294,6 +1367,10 @@ status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) { break; } + if (info->mFenceFd >= 0) { + ::close(info->mFenceFd); + } + // remove buffer even if mOMX->freeBuffer fails mBuffers[portIndex].removeAt(i); @@ -4433,9 +4510,12 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { case omx_message::EMPTY_BUFFER_DONE: { IOMX::buffer_id bufferID; + int32_t fenceFd; + CHECK(msg->findInt32("buffer", (int32_t*)&bufferID)); + CHECK(msg->findInt32("fence_fd", &fenceFd)); - return onOMXEmptyBufferDone(bufferID); + return onOMXEmptyBufferDone(bufferID, fenceFd); } case omx_message::FILL_BUFFER_DONE: @@ -4443,19 +4523,21 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { IOMX::buffer_id bufferID; CHECK(msg->findInt32("buffer", (int32_t*)&bufferID)); - int32_t rangeOffset, rangeLength, flags; + int32_t rangeOffset, rangeLength, flags, fenceFd; int64_t timeUs; CHECK(msg->findInt32("range_offset", &rangeOffset)); CHECK(msg->findInt32("range_length", &rangeLength)); CHECK(msg->findInt32("flags", &flags)); CHECK(msg->findInt64("timestamp", &timeUs)); + CHECK(msg->findInt32("fence_fd", &fenceFd)); return onOMXFillBufferDone( bufferID, (size_t)rangeOffset, (size_t)rangeLength, (OMX_U32)flags, - timeUs); + timeUs, + fenceFd); } default: @@ -4486,7 +4568,7 @@ bool ACodec::BaseState::onOMXEvent( return true; } -bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) { +bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd) { ALOGV("[%s] onOMXEmptyBufferDone %u", mCodec->mComponentName.c_str(), bufferID); @@ -4495,10 +4577,20 @@ bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) { if (status != BufferInfo::OWNED_BY_COMPONENT) { ALOGE("Wrong ownership in EBD: %s(%d) buffer #%u", _asString(status), status, bufferID); mCodec->dumpBuffers(kPortIndexInput); + if (fenceFd >= 0) { + ::close(fenceFd); + } return false; } info->mStatus = BufferInfo::OWNED_BY_US; + // input buffers cannot take fences, so wait for any fence now + (void)mCodec->waitForFence(fenceFd, "onOMXEmptyBufferDone"); + fenceFd = -1; + + // still save fence for completeness + info->setWriteFence(fenceFd, "onOMXEmptyBufferDone"); + // We're in "store-metadata-in-buffers" mode, the underlying // OMX component had access to data that's implicitly refcounted // by this "MediaBuffer" object. Now that the OMX component has @@ -4670,13 +4762,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { mCodec->submitOutputMetadataBuffer(); } } + info->checkReadFence("onInputBufferFilled"); status_t err2 = mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, 0, buffer->size(), flags, - timeUs); + timeUs, + info->mFenceFd); + info->mFenceFd = -1; if (err2 != OK) { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2)); return; @@ -4704,13 +4799,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { ALOGV("[%s] calling emptyBuffer %u signalling EOS", mCodec->mComponentName.c_str(), bufferID); + info->checkReadFence("onInputBufferFilled"); status_t err2 = mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, 0, 0, OMX_BUFFERFLAG_EOS, - 0); + 0, + info->mFenceFd); + info->mFenceFd = -1; if (err2 != OK) { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2)); return; @@ -4765,7 +4863,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( IOMX::buffer_id bufferID, size_t rangeOffset, size_t rangeLength, OMX_U32 flags, - int64_t timeUs) { + int64_t timeUs, + int fenceFd) { ALOGV("[%s] onOMXFillBufferDone %u time %" PRId64 " us, flags = 0x%08x", mCodec->mComponentName.c_str(), bufferID, timeUs, flags); @@ -4794,12 +4893,22 @@ bool ACodec::BaseState::onOMXFillBufferDone( ALOGE("Wrong ownership in FBD: %s(%d) buffer #%u", _asString(status), status, bufferID); mCodec->dumpBuffers(kPortIndexOutput); mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION); + if (fenceFd >= 0) { + ::close(fenceFd); + } return true; } info->mDequeuedAt = ++mCodec->mDequeueCounter; info->mStatus = BufferInfo::OWNED_BY_US; + // byte buffers cannot take fences, so wait for any fence now + if (mCodec->mNativeWindow == NULL) { + (void)mCodec->waitForFence(fenceFd, "onOMXFillBufferDone"); + fenceFd = -1; + } + info->setReadFence(fenceFd, "onOMXFillBufferDone"); + PortMode mode = getPortMode(kPortIndexOutput); switch (mode) { @@ -4813,7 +4922,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); - err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID); + err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err != OK) { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); return true; @@ -4835,7 +4945,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)info->mData->data(); if (info->mData->size() >= sizeof(grallocMeta) && grallocMeta.eType == kMetadataBufferTypeGrallocSource) { - handle = (native_handle_t *)grallocMeta.hHandle; + handle = (native_handle_t *)grallocMeta.pHandle; } else if (info->mData->size() >= sizeof(nativeMeta) && nativeMeta.eType == kMetadataBufferTypeANWBuffer) { handle = (native_handle_t *)nativeMeta.pBuffer->handle; @@ -4946,17 +5056,23 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs); ALOGW_IF(err != NO_ERROR, "failed to set buffer timestamp: %d", err); + info->checkReadFence("onOutputBufferDrained before queueBuffer"); err = mCodec->mNativeWindow->queueBuffer( - mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), -1); + mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd); + info->mFenceFd = -1; if (err == OK) { info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; } else { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); info->mStatus = BufferInfo::OWNED_BY_US; + // keeping read fence as write fence to avoid clobbering + info->mIsReadFence = false; } } else { if (mCodec->mNativeWindow != NULL && (info->mData == NULL || info->mData->size() != 0)) { + // move read fence into write fence to avoid clobbering + info->mIsReadFence = false; ATRACE_NAME("frame-drop"); } info->mStatus = BufferInfo::OWNED_BY_US; @@ -4991,7 +5107,10 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { if (info != NULL) { ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); - status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID); + info->checkWriteFence("onOutputBufferDrained::RESUBMIT_BUFFERS"); + status_t err = mCodec->mOMX->fillBuffer( + mCodec->mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err == OK) { info->mStatus = BufferInfo::OWNED_BY_COMPONENT; } else { @@ -5754,7 +5873,9 @@ void ACodec::ExecutingState::submitRegularOutputBuffers() { ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); - status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID); + info->checkWriteFence("submitRegularOutputBuffers"); + status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err != OK) { failed = true; break; diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 5d04628..e69890d 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -125,13 +125,13 @@ struct MuxOMX : public IOMX { virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer); - virtual status_t fillBuffer(node_id node, buffer_id buffer); + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd); virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); virtual status_t getExtensionIndex( node_id node, @@ -385,17 +385,17 @@ status_t MuxOMX::freeBuffer( return getOMX(node)->freeBuffer(node, port_index, buffer); } -status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer) { - return getOMX(node)->fillBuffer(node, buffer); +status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) { + return getOMX(node)->fillBuffer(node, buffer, fenceFd); } status_t MuxOMX::emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { return getOMX(node)->emptyBuffer( - node, buffer, range_offset, range_length, flags, timestamp); + node, buffer, range_offset, range_length, flags, timestamp, fenceFd); } status_t MuxOMX::getExtensionIndex( diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 413628d..6828b54 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -601,16 +601,18 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { // reassemble the csd data into its original form sp<ABuffer> csd0; if (msg->findBuffer("csd-0", &csd0)) { - if (mime.startsWith("video/")) { // do we need to be stricter than this? + if (mime == MEDIA_MIMETYPE_VIDEO_AVC) { sp<ABuffer> csd1; if (msg->findBuffer("csd-1", &csd1)) { char avcc[1024]; // that oughta be enough, right? size_t outsize = reassembleAVCC(csd0, csd1, avcc); meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize); } - } else if (mime.startsWith("audio/")) { + } else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) { int csd0size = csd0->size(); char esds[csd0size + 31]; + // The written ESDS is actually for an audio stream, but it's enough + // for transporting the CSD to muxers. reassembleESDS(csd0, esds); meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds)); } diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp index 08e956a..8ac337a 100644..100755 --- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp @@ -121,6 +121,7 @@ SoftAVC::SoftAVC( mIvColorFormat(IV_YUV_420P), mNewWidth(mWidth), mNewHeight(mHeight), + mNewLevel(0), mChangingResolution(false) { initPorts( kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE); @@ -303,18 +304,22 @@ status_t SoftAVC::initDecoder() { uint32_t displayHeight = outputBufferHeight(); uint32_t displaySizeY = displayStride * displayHeight; - if (displaySizeY > (1920 * 1088)) { - i4_level = 50; - } else if (displaySizeY > (1280 * 720)) { - i4_level = 40; - } else if (displaySizeY > (720 * 576)) { - i4_level = 31; - } else if (displaySizeY > (624 * 320)) { - i4_level = 30; - } else if (displaySizeY > (352 * 288)) { - i4_level = 21; + if(mNewLevel == 0){ + if (displaySizeY > (1920 * 1088)) { + i4_level = 50; + } else if (displaySizeY > (1280 * 720)) { + i4_level = 40; + } else if (displaySizeY > (720 * 576)) { + i4_level = 31; + } else if (displaySizeY > (624 * 320)) { + i4_level = 30; + } else if (displaySizeY > (352 * 288)) { + i4_level = 21; + } else { + i4_level = 20; + } } else { - i4_level = 20; + i4_level = mNewLevel; } { @@ -691,6 +696,7 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { bool unsupportedDimensions = (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF)); bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); + bool unsupportedLevel = (IH264D_UNSUPPORTED_LEVEL == (s_dec_op.u4_error_code & 0xFF)); GETTIME(&mTimeEnd, NULL); /* Compute time taken for decode() */ @@ -722,6 +728,18 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { return; } + if (unsupportedLevel && !mFlushNeeded) { + + mNewLevel = 51; + + CHECK_EQ(reInitDecoder(), (status_t)OK); + + setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); + + ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + return; + } + // If the decoder is in the changing resolution mode and there is no output present, // that means the switching is done and it's ready to reset the decoder and the plugin. if (mChangingResolution && !s_dec_op.u4_output_present) { @@ -745,6 +763,17 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { continue; } + if (unsupportedLevel) { + + if (mFlushNeeded) { + setFlushMode(); + } + + mNewLevel = 51; + mInitNeeded = true; + continue; + } + if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { uint32_t width = s_dec_op.u4_pic_wd; uint32_t height = s_dec_op.u4_pic_ht; diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h index 191a71d..2067810 100644..100755 --- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h @@ -100,6 +100,7 @@ private: bool mInitNeeded; uint32_t mNewWidth; uint32_t mNewHeight; + uint32_t mNewLevel; // The input stream has changed to a different resolution, which is still supported by the // codec. So the codec is switching to decode the new resolution. bool mChangingResolution; diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp index 6322dc2..7ff9ee7 100644 --- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp @@ -403,6 +403,14 @@ void SoftOpus::onQueueFilled(OMX_U32 portIndex) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + // Ignore CSD re-submissions. + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + return; + } + BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index c34954b..d468dfc 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -118,13 +118,13 @@ public: virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer); - virtual status_t fillBuffer(node_id node, buffer_id buffer); + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd); virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); virtual status_t getExtensionIndex( node_id node, @@ -148,10 +148,10 @@ public: OMX_IN OMX_PTR pEventData); OMX_ERRORTYPE OnEmptyBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd); OMX_ERRORTYPE OnFillBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd); void invalidateNodeID(node_id node); diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index fe6dccd..76df815 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -105,16 +105,16 @@ struct OMXNodeInstance { status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer); - status_t fillBuffer(OMX::buffer_id buffer); + status_t fillBuffer(OMX::buffer_id buffer, int fenceFd); status_t emptyBuffer( OMX::buffer_id buffer, OMX_U32 rangeOffset, OMX_U32 rangeLength, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); status_t emptyGraphicBuffer( OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &buffer, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); status_t getExtensionIndex( const char *parameterName, OMX_INDEXTYPE *index); @@ -208,9 +208,18 @@ private: status_t storeMetaDataInBuffers_l( OMX_U32 portIndex, OMX_BOOL enable, MetadataBufferType *type); + // Stores fence into buffer if it is ANWBuffer type and has enough space. + // otherwise, waits for the fence to signal. Takes ownership of |fenceFd|. + status_t storeFenceInMeta_l( + OMX_BUFFERHEADERTYPE *header, int fenceFd, OMX_U32 portIndex); + + // Retrieves the fence from buffer if ANWBuffer type and has enough space. Otherwise, returns -1 + int retrieveFenceFromMeta_l( + OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex); + status_t emptyBuffer_l( OMX_BUFFERHEADERTYPE *header, - OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr); + OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd); status_t updateGraphicBufferInMeta_l( OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer, diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index f797e63..d30bba5 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -64,6 +64,7 @@ GraphicBufferSource::GraphicBufferSource( mLatestBufferId(-1), mLatestBufferFrameNum(0), mLatestBufferUseCount(0), + mLatestBufferFence(Fence::NO_FENCE), mRepeatBufferDeferred(false), mTimePerCaptureUs(-1ll), mTimePerFrameUs(-1ll), @@ -226,9 +227,8 @@ void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) { mCodecBuffers.add(codecBuffer); } -void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { +void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd) { Mutex::Autolock autoLock(mMutex); - if (!mExecuting) { return; } @@ -237,6 +237,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (cbi < 0) { // This should never happen. ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); + if (fenceFd >= 0) { + ::close(fenceFd); + } return; } @@ -258,6 +261,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { } // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. + if (fenceFd >= 0) { + ::close(fenceFd); + } return; } @@ -269,10 +275,10 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (type == kMetadataBufferTypeGrallocSource && header->nAllocLen >= sizeof(VideoGrallocMetadata)) { VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)data; - if (grallocMeta.hHandle != codecBuffer.mGraphicBuffer->handle) { + if (grallocMeta.pHandle != codecBuffer.mGraphicBuffer->handle) { // should never happen ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", - grallocMeta.hHandle, codecBuffer.mGraphicBuffer->handle); + grallocMeta.pHandle, codecBuffer.mGraphicBuffer->handle); CHECK(!"codecBufferEmptied: mismatched buffer"); } } else if (type == kMetadataBufferTypeANWBuffer @@ -291,6 +297,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // If we find a match, release that slot. If we don't, the BufferQueue // has dropped that GraphicBuffer, and there's nothing for us to release. int id = codecBuffer.mBuf; + sp<Fence> fence = new Fence(fenceFd); if (mBufferSlot[id] != NULL && mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { ALOGV("cbi %d matches bq slot %d, handle=%p", @@ -304,15 +311,16 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { int outSlot; mConsumer->attachBuffer(&outSlot, mBufferSlot[id]); mConsumer->releaseBuffer(outSlot, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence); } else { mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence); } } } else { ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", cbi); + // we will not reuse codec buffer, so there is no need to wait for fence } // Mark the codec buffer as available by clearing the GraphicBuffer ref. @@ -394,7 +402,7 @@ void GraphicBufferSource::suspend(bool suspend) { mConsumer->detachBuffer(item.mBuf); mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer); mConsumer->releaseBuffer(item.mBuf, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } else { mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); @@ -447,13 +455,6 @@ bool GraphicBufferSource::fillCodecBuffer_l() { mNumFramesAvailable--; - // Wait for it to become available. - err = item.mFence->waitForever("GraphicBufferSource::fillCodecBuffer_l"); - if (err != OK) { - ALOGW("failed to wait for buffer fence: %d", err); - // keep going - } - // If this is the first time we're seeing this buffer, add it to our // slot table. if (item.mGraphicBuffer != NULL) { @@ -489,11 +490,12 @@ bool GraphicBufferSource::fillCodecBuffer_l() { mConsumer->detachBuffer(item.mBuf); mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer); mConsumer->releaseBuffer(item.mBuf, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } else { mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } + // item.mFence is released at the end of this method } else { ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); setLatestBuffer_l(item, dropped); @@ -520,9 +522,10 @@ bool GraphicBufferSource::repeatLatestBuffer_l() { mLatestBufferFrameNum, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE); + mLatestBufferFence); mLatestBufferId = -1; mLatestBufferFrameNum = 0; + mLatestBufferFence = Fence::NO_FENCE; return false; } @@ -537,6 +540,7 @@ bool GraphicBufferSource::repeatLatestBuffer_l() { item.mBuf = mLatestBufferId; item.mFrameNumber = mLatestBufferFrameNum; item.mTimestamp = mRepeatLastFrameTimestamp; + item.mFence = mLatestBufferFence; status_t err = submitBuffer_l(item, cbi); @@ -576,12 +580,13 @@ void GraphicBufferSource::setLatestBuffer_l( mConsumer->attachBuffer(&outSlot, mBufferSlot[mLatestBufferId]); mConsumer->releaseBuffer(outSlot, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, mLatestBufferFence); } else { mConsumer->releaseBuffer( mLatestBufferId, mLatestBufferFrameNum, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, mLatestBufferFence); } + // mLatestBufferFence will be set to new fence just below } } @@ -592,6 +597,7 @@ void GraphicBufferSource::setLatestBuffer_l( mLatestBufferUseCount = dropped ? 0 : 1; mRepeatBufferDeferred = false; mRepeatLastFrameCount = kRepeatLastFrameCount; + mLatestBufferFence = item.mFence; if (mReflector != NULL) { sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector); @@ -687,8 +693,7 @@ int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) { return timeUs; } -status_t GraphicBufferSource::submitBuffer_l( - const BufferItem &item, int cbi) { +status_t GraphicBufferSource::submitBuffer_l(const BufferItem &item, int cbi) { ALOGV("submitBuffer_l cbi=%d", cbi); int64_t timeUs = getTimestamp(item); @@ -704,7 +709,8 @@ status_t GraphicBufferSource::submitBuffer_l( OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; sp<GraphicBuffer> buffer = codecBuffer.mGraphicBuffer; status_t err = mNodeInstance->emptyGraphicBuffer( - header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs); + header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs, + item.mFence->isValid() ? item.mFence->dup() : -1); if (err != OK) { ALOGW("WARNING: emptyNativeWindowBuffer failed: 0x%x", err); codecBuffer.mGraphicBuffer = NULL; @@ -737,7 +743,7 @@ void GraphicBufferSource::submitEndOfInputStream_l() { OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; status_t err = mNodeInstance->emptyGraphicBuffer( header, NULL /* buffer */, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS, - 0 /* timestamp */); + 0 /* timestamp */, -1 /* fenceFd */); if (err != OK) { ALOGW("emptyDirectBuffer EOS failed: 0x%x", err); } else { @@ -799,7 +805,7 @@ void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) { mConsumer->detachBuffer(item.mBuf); mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer); mConsumer->releaseBuffer(item.mBuf, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } else { mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 21ee96a..555bbec 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -93,7 +93,7 @@ public: // Called from OnEmptyBufferDone. If we have a BQ buffer available, // fill it with a new frame of data; otherwise, just mark it as available. - void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header); + void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd); // Called when omx_message::FILL_BUFFER_DONE is received. (Currently the // buffer source will fix timestamp in the header if needed.) @@ -274,6 +274,7 @@ private: int mLatestBufferId; uint64_t mLatestBufferFrameNum; int32_t mLatestBufferUseCount; + sp<Fence> mLatestBufferFence; // The previous buffer should've been repeated but // no codec buffer was available at the time. diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 4ca827c..76217ec 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -415,17 +415,17 @@ status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) { port_index, buffer); } -status_t OMX::fillBuffer(node_id node, buffer_id buffer) { - return findInstance(node)->fillBuffer(buffer); +status_t OMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) { + return findInstance(node)->fillBuffer(buffer, fenceFd); } status_t OMX::emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { return findInstance(node)->emptyBuffer( - buffer, range_offset, range_length, flags, timestamp); + buffer, range_offset, range_length, flags, timestamp, fenceFd); } status_t OMX::getExtensionIndex( @@ -459,6 +459,7 @@ OMX_ERRORTYPE OMX::OnEvent( omx_message msg; msg.type = omx_message::EVENT; msg.node = node; + msg.fenceFd = -1; msg.u.event_data.event = eEvent; msg.u.event_data.data1 = nData1; msg.u.event_data.data2 = nData2; @@ -469,12 +470,13 @@ OMX_ERRORTYPE OMX::OnEvent( } OMX_ERRORTYPE OMX::OnEmptyBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) { ALOGV("OnEmptyBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::EMPTY_BUFFER_DONE; msg.node = node; + msg.fenceFd = fenceFd; msg.u.buffer_data.buffer = buffer; findDispatcher(node)->post(msg); @@ -483,12 +485,13 @@ OMX_ERRORTYPE OMX::OnEmptyBufferDone( } OMX_ERRORTYPE OMX::OnFillBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) { ALOGV("OnFillBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::FILL_BUFFER_DONE; msg.node = node; + msg.fenceFd = fenceFd; msg.u.extended_buffer_data.buffer = buffer; msg.u.extended_buffer_data.range_offset = pBuffer->nOffset; msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen; diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index e4b2de4..9e399f9 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -76,11 +76,11 @@ static const OMX_U32 kPortIndexOutput = 1; #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]", \ +#define EMPTY_BUFFER(addr, header, fenceFd) "%#x [%u@%p fc=%d]", \ + (addr), (header)->nAllocLen, (header)->pBuffer, (fenceFd) +#define FULL_BUFFER(addr, header, fenceFd) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld fc=%d]", \ (intptr_t)(addr), (header)->nAllocLen, (header)->pBuffer, \ - (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp + (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp, (fenceFd) #define WITH_STATS_WRAPPER(fmt, ...) fmt " { IN=%zu/%zu OUT=%zu/%zu }", ##__VA_ARGS__, \ mInputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexInput], \ @@ -812,7 +812,7 @@ status_t OMXNodeInstance::updateGraphicBufferInMeta_l( && header->nAllocLen >= sizeof(VideoGrallocMetadata)) { VideoGrallocMetadata &metadata = *(VideoGrallocMetadata *)(header->pBuffer); metadata.eType = kMetadataBufferTypeGrallocSource; - metadata.hHandle = graphicBuffer == NULL ? NULL : graphicBuffer->handle; + metadata.pHandle = graphicBuffer == NULL ? NULL : graphicBuffer->handle; } else if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer && header->nAllocLen >= sizeof(VideoNativeMetadata)) { VideoNativeMetadata &metadata = *(VideoNativeMetadata *)(header->pBuffer); @@ -1050,7 +1050,7 @@ status_t OMXNodeInstance::freeBuffer( return StatusFromOMXError(err); } -status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { +status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer, int fenceFd) { Mutex::Autolock autoLock(mLock); OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer); @@ -1058,15 +1058,22 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { header->nOffset = 0; header->nFlags = 0; + // meta now owns fenceFd + status_t res = storeFenceInMeta_l(header, fenceFd, kPortIndexOutput); + if (res != OK) { + CLOG_ERROR(fillBuffer::storeFenceInMeta, res, EMPTY_BUFFER(buffer, header, fenceFd)); + return res; + } + { Mutex::Autolock _l(mDebugLock); mOutputBuffersWithCodec.add(header); - CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header))); + CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header, fenceFd))); } OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); if (err != OMX_ErrorNone) { - CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header)); + CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header, fenceFd)); Mutex::Autolock _l(mDebugLock); mOutputBuffersWithCodec.remove(header); } @@ -1076,7 +1083,7 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { status_t OMXNodeInstance::emptyBuffer( OMX::buffer_id buffer, OMX_U32 rangeOffset, OMX_U32 rangeLength, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Mutex::Autolock autoLock(mLock); OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer); @@ -1084,6 +1091,9 @@ status_t OMXNodeInstance::emptyBuffer( // corner case: we permit rangeOffset == end-of-buffer with rangeLength == 0. if (rangeOffset > header->nAllocLen || rangeLength > header->nAllocLen - rangeOffset) { + if (fenceFd >= 0) { + ::close(fenceFd); + } return BAD_VALUE; } header->nFilledLen = rangeLength; @@ -1103,14 +1113,14 @@ status_t OMXNodeInstance::emptyBuffer( VideoNativeMetadata &backupMeta = *(VideoNativeMetadata *)backup->base(); VideoGrallocMetadata &codecMeta = *(VideoGrallocMetadata *)codec->base(); ALOGV("converting ANWB %p to handle %p", backupMeta.pBuffer, backupMeta.pBuffer->handle); - codecMeta.hHandle = backupMeta.pBuffer->handle; + codecMeta.pHandle = backupMeta.pBuffer->handle; codecMeta.eType = kMetadataBufferTypeGrallocSource; header->nFilledLen = sizeof(codecMeta); } else { buffer_meta->CopyToOMX(header); } - return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer, fenceFd); } // log queued buffer activity for the next few input and/or output frames @@ -1137,11 +1147,62 @@ void OMXNodeInstance::unbumpDebugLevel_l(size_t portIndex) { } } +status_t OMXNodeInstance::storeFenceInMeta_l( + OMX_BUFFERHEADERTYPE *header, int fenceFd, OMX_U32 portIndex) { + // propagate fence if component supports it; wait for it otherwise + OMX_U32 metaSize = portIndex == kPortIndexInput ? header->nFilledLen : header->nAllocLen; + if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer + && metaSize >= sizeof(VideoNativeMetadata)) { + VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)(header->pBuffer); + if (nativeMeta.nFenceFd >= 0) { + ALOGE("fence (%d) already exists in meta", nativeMeta.nFenceFd); + if (fenceFd >= 0) { + ::close(fenceFd); + } + return ALREADY_EXISTS; + } + nativeMeta.nFenceFd = fenceFd; + } else if (fenceFd >= 0) { + CLOG_BUFFER(storeFenceInMeta, "waiting for fence %d", fenceFd); + sp<Fence> fence = new Fence(fenceFd); + return fence->wait(IOMX::kFenceTimeoutMs); + } + return OK; +} + +int OMXNodeInstance::retrieveFenceFromMeta_l( + OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex) { + OMX_U32 metaSize = portIndex == kPortIndexInput ? header->nAllocLen : header->nFilledLen; + int fenceFd = -1; + if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer + && header->nAllocLen >= sizeof(VideoNativeMetadata)) { + VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)(header->pBuffer); + if (nativeMeta.eType == kMetadataBufferTypeANWBuffer) { + fenceFd = nativeMeta.nFenceFd; + nativeMeta.nFenceFd = -1; + } + if (metaSize < sizeof(nativeMeta) && fenceFd >= 0) { + CLOG_ERROR(foundFenceInEmptyMeta, BAD_VALUE, FULL_BUFFER( + NULL, header, nativeMeta.nFenceFd)); + fenceFd = -1; + } + } + return fenceFd; +} + status_t OMXNodeInstance::emptyBuffer_l( - OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr) { + OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, + intptr_t debugAddr, int fenceFd) { header->nFlags = flags; header->nTimeStamp = timestamp; + status_t res = storeFenceInMeta_l(header, fenceFd, kPortIndexInput); + if (res != OK) { + CLOG_ERROR(emptyBuffer::storeFenceInMeta, res, WITH_STATS( + FULL_BUFFER(debugAddr, header, fenceFd))); + return res; + } + { Mutex::Autolock _l(mDebugLock); mInputBuffersWithCodec.add(header); @@ -1151,11 +1212,11 @@ status_t OMXNodeInstance::emptyBuffer_l( bumpDebugLevel_l(2 /* numInputBuffers */, 0 /* numOutputBuffers */); } - CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header))); + CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header, fenceFd))); } OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); - CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header)); + CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header, fenceFd)); { Mutex::Autolock _l(mDebugLock); @@ -1172,18 +1233,19 @@ status_t OMXNodeInstance::emptyBuffer_l( // like emptyBuffer, but the data is already in header->pBuffer status_t OMXNodeInstance::emptyGraphicBuffer( OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &graphicBuffer, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Mutex::Autolock autoLock(mLock); OMX::buffer_id buffer = findBufferID(header); status_t err = updateGraphicBufferInMeta_l(kPortIndexInput, graphicBuffer, buffer, header); if (err != OK) { - CLOG_ERROR(emptyGraphicBuffer, err, FULL_BUFFER((intptr_t)header->pBuffer, header)); + CLOG_ERROR(emptyGraphicBuffer, err, FULL_BUFFER( + (intptr_t)header->pBuffer, header, fenceFd)); return err; } header->nOffset = 0; header->nFilledLen = graphicBuffer == NULL ? 0 : header->nAllocLen; - return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer, fenceFd); } status_t OMXNodeInstance::getExtensionIndex( @@ -1307,7 +1369,8 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { mOutputBuffersWithCodec.remove(buffer); CLOG_BUMPED_BUFFER( - FBD, WITH_STATS(FULL_BUFFER(msg.u.extended_buffer_data.buffer, buffer))); + FBD, WITH_STATS(FULL_BUFFER( + msg.u.extended_buffer_data.buffer, buffer, msg.fenceFd))); unbumpDebugLevel_l(kPortIndexOutput); } @@ -1335,7 +1398,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { mInputBuffersWithCodec.remove(buffer); CLOG_BUMPED_BUFFER( - EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer))); + EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer, msg.fenceFd))); } if (bufferSource != NULL) { @@ -1344,7 +1407,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { // 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. - bufferSource->codecBufferEmptied(buffer); + bufferSource->codecBufferEmptied(buffer, msg.fenceFd); return; } } @@ -1439,8 +1502,9 @@ OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone( if (instance->mDying) { return OMX_ErrorNone; } + int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput); return instance->owner()->OnEmptyBufferDone(instance->nodeID(), - instance->findBufferID(pBuffer), pBuffer); + instance->findBufferID(pBuffer), pBuffer, fenceFd); } // static @@ -1452,8 +1516,9 @@ OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone( if (instance->mDying) { return OMX_ErrorNone; } + int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput); return instance->owner()->OnFillBufferDone(instance->nodeID(), - instance->findBufferID(pBuffer), pBuffer); + instance->findBufferID(pBuffer), pBuffer, fenceFd); } void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) { diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp index 5f80cbc..9dd26fb 100644 --- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -517,6 +517,16 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( // TODO do we need to support other formats? srcStride *= 4; } + + if (nativeMeta.nFenceFd >= 0) { + sp<Fence> fence = new Fence(nativeMeta.nFenceFd); + nativeMeta.nFenceFd = -1; + status_t err = fence->wait(IOMX::kFenceTimeoutMs); + if (err != OK) { + ALOGE("Timed out waiting on input fence"); + return NULL; + } + } } else { // TODO: remove this part. Check if anyone uses this. @@ -526,7 +536,7 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( } VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)(src); - handle = grallocMeta.hHandle; + handle = grallocMeta.pHandle; // assume HAL_PIXEL_FORMAT_RGBA_8888 // there is no way to get the src stride without the graphic buffer format = HAL_PIXEL_FORMAT_RGBA_8888; diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp index 33bd416..2e68dad 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/services/audioflinger/ServiceUtilities.cpp @@ -18,6 +18,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> +#include <private/android_filesystem_config.h> #include "ServiceUtilities.h" /* When performing permission checks we do not use permission cache for @@ -53,6 +54,10 @@ bool recordingAllowed(const String16& opPackageName) { } const uid_t uid = IPCThreadState::self()->getCallingUid(); + + // To permit command-line native tests + if (uid == AID_ROOT) return true; + String16 checkedOpPackageName = opPackageName; // In some cases the calling code has no access to the package it runs under. diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 7809eff..dc6710f 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2191,6 +2191,10 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount, mNormalFrameCount); + // Check if we want to throttle the processing to no more than 2x normal rate + mThreadThrottle = property_get_bool("af.thread.throttle", true /* default_value */); + mHalfBufferMs = mNormalFrameCount * 1000 / (2 * mSampleRate); + // mSinkBuffer is the sink buffer. Size is always multiple-of-16 frames. // Originally this was int16_t[] array, need to remove legacy implications. free(mSinkBuffer); @@ -2908,8 +2912,9 @@ bool AudioFlinger::PlaybackThread::threadLoop() if (!waitingAsyncCallback()) { // mSleepTimeUs == 0 means we must write to audio hardware if (mSleepTimeUs == 0) { + ssize_t ret = 0; if (mBytesRemaining) { - ssize_t ret = threadLoop_write(); + ret = threadLoop_write(); if (ret < 0) { mBytesRemaining = 0; } else { @@ -2920,11 +2925,11 @@ bool AudioFlinger::PlaybackThread::threadLoop() (mMixerStatus == MIXER_DRAIN_ALL)) { threadLoop_drain(); } - if (mType == MIXER) { + if (mType == MIXER && !mStandby) { // write blocked detection nsecs_t now = systemTime(); nsecs_t delta = now - mLastWriteTime; - if (!mStandby && delta > maxPeriod) { + if (delta > maxPeriod) { mNumDelayedWrites++; if ((now - lastWarning) > kWarningThrottleNs) { ATRACE_NAME("underrun"); @@ -2933,6 +2938,31 @@ bool AudioFlinger::PlaybackThread::threadLoop() lastWarning = now; } } + + if (mThreadThrottle + && mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks) + && ret > 0) { // we wrote something + // Limit MixerThread data processing to no more than twice the + // expected processing rate. + // + // This helps prevent underruns with NuPlayer and other applications + // which may set up buffers that are close to the minimum size, or use + // deep buffers, and rely on a double-buffering sleep strategy to fill. + // + // The throttle smooths out sudden large data drains from the device, + // e.g. when it comes out of standby, which often causes problems with + // (1) mixer threads without a fast mixer (which has its own warm-up) + // (2) minimum buffer sized tracks (even if the track is full, + // the app won't fill fast enough to handle the sudden draw). + + const int32_t deltaMs = delta / 1000000; + const int32_t throttleMs = mHalfBufferMs - deltaMs; + if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) { + usleep(throttleMs * 1000); + ALOGD("mixer(%p) throttle: ret(%zd) deltaMs(%d) requires sleep %d ms", + this, ret, deltaMs, throttleMs); + } + } } } else { @@ -4023,6 +4053,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } } else { if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) { + ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)", + track, framesReady, desiredFrames); track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames); } // clear effect chain input buffer if an active track underruns to avoid sending diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 07c226e..7b4fb14 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -613,6 +613,9 @@ protected: // updated by readOutputParameters_l() size_t mNormalFrameCount; // normal mixer and effects + bool mThreadThrottle; // throttle the thread processing + uint32_t mHalfBufferMs; // half the buffer size in milliseconds + void* mSinkBuffer; // frame size aligned sink buffer // TODO: |