diff options
Diffstat (limited to 'media')
26 files changed, 908 insertions, 207 deletions
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 3ee5809..80c8c5e 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -211,7 +211,7 @@ status_t AudioRecord::set( mReqFrameCount = frameCount; mNotificationFramesReq = notificationFrames; - mNotificationFramesAct = 0; + // mNotificationFramesAct is initialized in openRecord_l if (sessionId == AUDIO_SESSION_ALLOCATE) { mSessionId = AudioSystem::newAudioSessionId(); @@ -444,60 +444,25 @@ status_t AudioRecord::openRecord_l(size_t epoch) } } - // FIXME Assume double buffering, because we don't know the true HAL sample rate - const uint32_t nBuffering = 2; - - mNotificationFramesAct = mNotificationFramesReq; - size_t frameCount = mReqFrameCount; - - if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) { - // validate framecount - // If fast track was not requested, this preserves - // the old behavior of validating on client side. - // FIXME Eventually the validation should be done on server side - // regardless of whether it's a fast or normal track. It's debatable - // whether to account for the input latency to provision buffers appropriately. - size_t minFrameCount; - status = AudioRecord::getMinFrameCount(&minFrameCount, - mSampleRate, mFormat, mChannelMask); - if (status != NO_ERROR) { - ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; " - "status %d", - mSampleRate, mFormat, mChannelMask, status); - return status; - } - - if (frameCount == 0) { - frameCount = minFrameCount; - } else if (frameCount < minFrameCount) { - ALOGE("frameCount %zu < minFrameCount %zu", frameCount, minFrameCount); - return BAD_VALUE; - } - - // Make sure that application is notified with sufficient margin before overrun - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { - mNotificationFramesAct = frameCount/2; - } - } - audio_io_handle_t input = AudioSystem::getInput(mInputSource, mSampleRate, mFormat, - mChannelMask, mSessionId); + mChannelMask, mSessionId, mFlags); if (input == AUDIO_IO_HANDLE_NONE) { ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, " - "channel mask %#x, session %d", - mInputSource, mSampleRate, mFormat, mChannelMask, mSessionId); + "channel mask %#x, session %d, flags %#x", + mInputSource, mSampleRate, mFormat, mChannelMask, mSessionId, mFlags); return BAD_VALUE; } { // Now that we have a reference to an I/O handle and have not yet handed it off to AudioFlinger, // we must release it ourselves if anything goes wrong. + size_t frameCount = mReqFrameCount; size_t temp = frameCount; // temp may be replaced by a revised value of frameCount, // but we will still need the original value also int originalSessionId = mSessionId; // The notification frame count is the period between callbacks, as suggested by the server. - size_t notificationFrames; + size_t notificationFrames = mNotificationFramesReq; sp<IMemory> iMem; // for cblk sp<IMemory> bufferMem; @@ -576,14 +541,14 @@ status_t AudioRecord::openRecord_l(size_t epoch) // once denied, do not request again if IAudioRecord is re-created mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST); } - // Theoretically double-buffering is not required for fast tracks, - // due to tighter scheduling. But in practice, to accomodate kernels with - // scheduling jitter, and apps with computation jitter, we use double-buffering. - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } } + // Make sure that application is notified with sufficient margin before overrun + if (notificationFrames == 0 || notificationFrames > frameCount) { + ALOGW("Received notificationFrames %zu for frameCount %zu", notificationFrames, frameCount); + } + mNotificationFramesAct = notificationFrames; + // We retain a copy of the I/O handle, but don't own the reference mInput = input; mRefreshRemaining = true; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index a47d45c..fd5824b 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -688,11 +688,12 @@ audio_io_handle_t AudioSystem::getInput(audio_source_t inputSource, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - int sessionId) + int sessionId, + audio_input_flags_t flags) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; - return aps->getInput(inputSource, samplingRate, format, channelMask, sessionId); + return aps->getInput(inputSource, samplingRate, format, channelMask, sessionId, flags); } status_t AudioSystem::startInput(audio_io_handle_t input) diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 5cf42f7..bd7ea46 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -197,6 +197,7 @@ public: lSessionId = *sessionId; } data.writeInt32(lSessionId); + data.writeInt64(notificationFrames != NULL ? *notificationFrames : 0); cblk.clear(); buffers.clear(); status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply); @@ -532,7 +533,8 @@ public: audio_devices_t *pDevices, uint32_t *pSamplingRate, audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask) + audio_channel_mask_t *pChannelMask, + audio_input_flags_t flags) { Parcel data, reply; audio_devices_t devices = pDevices != NULL ? *pDevices : AUDIO_DEVICE_NONE; @@ -547,6 +549,7 @@ public: data.writeInt32(samplingRate); data.writeInt32(format); data.writeInt32(channelMask); + data.writeInt32(flags); remote()->transact(OPEN_INPUT, data, &reply); audio_io_handle_t input = (audio_io_handle_t) reply.readInt32(); devices = (audio_devices_t)reply.readInt32(); @@ -964,7 +967,7 @@ status_t BnAudioFlinger::onTransact( track_flags_t flags = (track_flags_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); - size_t notificationFrames = 0; + size_t notificationFrames = data.readInt64(); sp<IMemory> cblk; sp<IMemory> buffers; status_t status; @@ -1157,12 +1160,14 @@ status_t BnAudioFlinger::onTransact( uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = (audio_channel_mask_t)data.readInt32(); + audio_input_flags_t flags = (audio_input_flags_t) data.readInt32(); audio_io_handle_t input = openInput(module, &devices, &samplingRate, &format, - &channelMask); + &channelMask, + flags); reply->writeInt32((int32_t) input); reply->writeInt32(devices); reply->writeInt32(samplingRate); diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 41a9065..40dfb58 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -225,7 +225,8 @@ public: uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - int audioSession) + int audioSession, + audio_input_flags_t flags) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -234,6 +235,7 @@ public: data.writeInt32(static_cast <uint32_t>(format)); data.writeInt32(channelMask); data.writeInt32(audioSession); + data.writeInt32(flags); remote()->transact(GET_INPUT, data, &reply); return static_cast <audio_io_handle_t> (reply.readInt32()); } @@ -707,11 +709,13 @@ status_t BnAudioPolicyService::onTransact( audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); int audioSession = data.readInt32(); + audio_input_flags_t flags = (audio_input_flags_t) data.readInt32(); audio_io_handle_t input = getInput(inputSource, samplingRate, format, channelMask, - audioSession); + audioSession, + flags); reply->writeInt32(static_cast <int>(input)); return NO_ERROR; } break; diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 2aa0592..d2e381b 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -28,6 +28,7 @@ #include <media/mediaplayer.h> #include <media/SoundPool.h> #include "SoundPoolThread.h" +#include <media/AudioPolicyHelper.h> namespace android { @@ -39,10 +40,10 @@ uint32_t kDefaultFrameCount = 1200; size_t kDefaultHeapSize = 1024 * 1024; // 1MB -SoundPool::SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQuality) +SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes) { - ALOGV("SoundPool constructor: maxChannels=%d, streamType=%d, srcQuality=%d", - maxChannels, streamType, srcQuality); + ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s", + maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags); // check limits mMaxChannels = maxChannels; @@ -56,8 +57,7 @@ SoundPool::SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQua mQuit = false; mDecodeThread = 0; - mStreamType = streamType; - mSrcQuality = srcQuality; + memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); mAllocated = 0; mNextSampleID = 0; mNextChannelID = 0; @@ -580,7 +580,7 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV // initialize track size_t afFrameCount; uint32_t afSampleRate; - audio_stream_type_t streamType = mSoundPool->streamType(); + audio_stream_type_t streamType = audio_attributes_to_stream_type(mSoundPool->attributes()); if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { afFrameCount = kDefaultFrameCount; } diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index cc0cb01..d75408d 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -28,6 +28,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include "../../libstagefright/include/WVMExtractor.h" namespace android { @@ -35,10 +36,16 @@ NuPlayer::GenericSource::GenericSource( const sp<AMessage> ¬ify, const sp<IMediaHTTPService> &httpService, const char *url, - const KeyedVector<String8, String8> *headers) + const KeyedVector<String8, String8> *headers, + bool isWidevine, + bool uidValid, + uid_t uid) : Source(notify), mDurationUs(0ll), - mAudioIsVorbis(false) { + mAudioIsVorbis(false), + mIsWidevine(isWidevine), + mUIDValid(uidValid), + mUID(uid) { DataSource::RegisterDefaultSniffers(); sp<DataSource> dataSource = @@ -63,7 +70,31 @@ NuPlayer::GenericSource::GenericSource( void NuPlayer::GenericSource::initFromDataSource( const sp<DataSource> &dataSource) { - sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + sp<MediaExtractor> extractor; + + if (mIsWidevine) { + String8 mimeType; + float confidence; + sp<AMessage> dummy; + bool success; + + success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); + if (!success + || strcasecmp( + mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { + ALOGE("unsupported widevine mime: %s", mimeType.string()); + return; + } + + sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource); + wvmExtractor->setAdaptiveStreamingMode(true); + if (mUIDValid) { + wvmExtractor->setUID(mUID); + } + extractor = wvmExtractor; + } else { + extractor = MediaExtractor::Create(dataSource); + } CHECK(extractor != NULL); @@ -113,6 +144,13 @@ void NuPlayer::GenericSource::initFromDataSource( } } +status_t NuPlayer::GenericSource::setBuffers(bool audio, Vector<MediaBuffer *> &buffers) { + if (mIsWidevine && !audio) { + return mVideoTrack.mSource->setBuffers(buffers); + } + return INVALID_OPERATION; +} + NuPlayer::GenericSource::~GenericSource() { } @@ -128,7 +166,8 @@ void NuPlayer::GenericSource::prepareAsync() { } notifyFlagsChanged( - FLAG_CAN_PAUSE + (mIsWidevine ? FLAG_SECURE : 0) + | FLAG_CAN_PAUSE | FLAG_CAN_SEEK_BACKWARD | FLAG_CAN_SEEK_FORWARD | FLAG_CAN_SEEK); @@ -180,9 +219,14 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( return -EWOULDBLOCK; } + if (mIsWidevine && !audio) { + // try to read a buffer as we may not have been able to the last time + readBuffer(audio, -1ll); + } + status_t finalResult; if (!track->mPackets->hasBufferAvailable(&finalResult)) { - return finalResult == OK ? -EWOULDBLOCK : finalResult; + return (finalResult == OK ? -EWOULDBLOCK : finalResult); } status_t result = track->mPackets->dequeueAccessUnit(accessUnit); @@ -280,6 +324,10 @@ void NuPlayer::GenericSource::readBuffer( seeking = true; } + if (mIsWidevine && !audio) { + options.setNonBlocking(); + } + for (;;) { MediaBuffer *mbuf; status_t err = track->mSource->read(&mbuf, &options); @@ -293,11 +341,18 @@ void NuPlayer::GenericSource::readBuffer( outLength += sizeof(int32_t); } - sp<ABuffer> buffer = new ABuffer(outLength); - - memcpy(buffer->data(), - (const uint8_t *)mbuf->data() + mbuf->range_offset(), - mbuf->range_length()); + sp<ABuffer> buffer; + if (mIsWidevine && !audio) { + // data is already provided in the buffer + buffer = new ABuffer(NULL, mbuf->range_length()); + buffer->meta()->setPointer("mediaBuffer", mbuf); + mbuf->add_ref(); + } else { + buffer = new ABuffer(outLength); + memcpy(buffer->data(), + (const uint8_t *)mbuf->data() + mbuf->range_offset(), + mbuf->range_length()); + } if (audio && mAudioIsVorbis) { int32_t numPageSamples; @@ -332,6 +387,8 @@ void NuPlayer::GenericSource::readBuffer( track->mPackets->queueAccessUnit(buffer); break; + } else if (err == WOULD_BLOCK) { + break; } else if (err == INFO_FORMAT_CHANGED) { #if 0 track->mPackets->queueDiscontinuity( diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index e0cd20f..8e0209d 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -35,7 +35,10 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { const sp<AMessage> ¬ify, const sp<IMediaHTTPService> &httpService, const char *url, - const KeyedVector<String8, String8> *headers); + const KeyedVector<String8, String8> *headers, + bool isWidevine = false, + bool uidValid = false, + uid_t uid = 0); GenericSource( const sp<AMessage> ¬ify, @@ -54,6 +57,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual sp<AMessage> getTrackInfo(size_t trackIndex) const; virtual status_t seekTo(int64_t seekTimeUs); + virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers); + protected: virtual ~GenericSource(); @@ -73,6 +78,9 @@ private: int64_t mDurationUs; bool mAudioIsVorbis; + bool mIsWidevine; + bool mUIDValid; + uid_t mUID; void initFromDataSource(const sp<DataSource> &dataSource); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 88c59bf..fa6b1e5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -36,6 +36,7 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -221,6 +222,10 @@ void NuPlayer::setDataSourceAsync( || strstr(url, ".sdp?"))) { source = new RTSPSource( notify, httpService, url, headers, mUIDValid, mUID, true); + } else if ((!strncasecmp(url, "widevine://", 11))) { + source = new GenericSource(notify, httpService, url, headers, + true /* isWidevine */, mUIDValid, mUID); + mSourceFlags |= Source::FLAG_SECURE; } else { source = new GenericSource(notify, httpService, url, headers); } @@ -512,6 +517,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mNumFramesDropped = 0; mStarted = true; + /* instantiate decoders now for secure playback */ + if (mSourceFlags & Source::FLAG_SECURE) { + if (mNativeWindow != NULL) { + instantiateDecoder(false, &mVideoDecoder); + } + + if (mAudioSink != NULL) { + instantiateDecoder(true, &mAudioDecoder); + } + } + mSource->start(); uint32_t flags = 0; @@ -540,7 +556,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { new AMessage(kWhatRendererNotify, id()), flags); - looper()->registerHandler(mRenderer); + mRendererLooper = new ALooper; + mRendererLooper->setName("NuPlayerRenderer"); + mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + mRendererLooper->registerHandler(mRenderer); postScanSources(); break; @@ -735,6 +754,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { offloadInfo.has_video = (mVideoDecoder != NULL); offloadInfo.is_streaming = true; + ALOGV("try to open AudioSink in offload mode"); err = mAudioSink->open( sampleRate, numChannels, @@ -774,6 +794,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { if (!mOffloadAudio) { flags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + ALOGV("open AudioSink in NON-offload mode"); CHECK_EQ(mAudioSink->open( sampleRate, numChannels, @@ -921,6 +942,21 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } else if (what == Renderer::kWhatMediaRenderingStart) { ALOGV("media rendering started"); notifyListener(MEDIA_STARTED, 0, 0); + } else if (what == Renderer::kWhatAudioOffloadTearDown) { + ALOGV("Tear down audio offload, fall back to s/w path"); + int64_t positionUs; + CHECK(msg->findInt64("positionUs", &positionUs)); + mAudioSink->close(); + mAudioDecoder.clear(); + mRenderer->flush(true /* audio */); + if (mVideoDecoder != NULL) { + mRenderer->flush(false /* audio */); + } + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; + + performSeek(positionUs); + instantiateDecoder(true /* audio */, &mAudioDecoder); } break; } @@ -1055,6 +1091,10 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id()); mCCDecoder = new CCDecoder(ccNotify); + + if (mSourceFlags & Source::FLAG_SECURE) { + format->setInt32("secure", true); + } } sp<AMessage> notify = @@ -1073,6 +1113,28 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { (*decoder)->init(); (*decoder)->configure(format); + // allocate buffers to decrypt widevine source buffers + if (!audio && (mSourceFlags & Source::FLAG_SECURE)) { + Vector<sp<ABuffer> > inputBufs; + CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK); + + Vector<MediaBuffer *> mediaBufs; + for (size_t i = 0; i < inputBufs.size(); i++) { + const sp<ABuffer> &buffer = inputBufs[i]; + MediaBuffer *mbuf = new MediaBuffer(buffer->data(), buffer->size()); + mediaBufs.push(mbuf); + } + + status_t err = mSource->setBuffers(audio, mediaBufs); + if (err != OK) { + for (size_t i = 0; i < mediaBufs.size(); ++i) { + mediaBufs[i]->release(); + } + mediaBufs.clear(); + ALOGE("Secure source didn't support secure mediaBufs."); + return err; + } + } return OK; } @@ -1184,6 +1246,7 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { dropAccessUnit = false; if (!audio + && !(mSourceFlags & Source::FLAG_SECURE) && mVideoLateByUs > 100000ll && mVideoIsAVC && !IsAVCReferenceFrame(accessUnit)) { @@ -1497,6 +1560,13 @@ void NuPlayer::performReset() { ++mScanSourcesGeneration; mScanSourcesPending = false; + if (mRendererLooper != NULL) { + if (mRenderer != NULL) { + mRendererLooper->unregisterHandler(mRenderer->id()); + } + mRendererLooper->stop(); + mRendererLooper.clear(); + } mRenderer.clear(); if (mSource != NULL) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index d7c00aa..c04e277 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -125,6 +125,7 @@ private: sp<Decoder> mAudioDecoder; sp<CCDecoder> mCCDecoder; sp<Renderer> mRenderer; + sp<ALooper> mRendererLooper; List<sp<Action> > mDeferredActions; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index dd73cc4..1b9bafb 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -26,6 +26,7 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaCodec.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> @@ -54,6 +55,22 @@ NuPlayer::Decoder::Decoder( NuPlayer::Decoder::~Decoder() { } +static +status_t PostAndAwaitResponse( + const sp<AMessage> &msg, sp<AMessage> *response) { + status_t err = msg->postAndAwaitResponse(response); + + if (err != OK) { + return err; + } + + if (!(*response)->findInt32("err", &err)) { + err = OK; + } + + return err; +} + void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); @@ -72,8 +89,20 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get()); mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */); + int32_t secure = 0; + if (format->findInt32("secure", &secure) && secure != 0) { + if (mCodec != NULL) { + mCodec->getName(&mComponentName); + mComponentName.append(".secure"); + mCodec->release(); + ALOGI("[%s] creating", mComponentName.c_str()); + mCodec = MediaCodec::CreateByComponentName( + mCodecLooper, mComponentName.c_str()); + } + } if (mCodec == NULL) { - ALOGE("Failed to create %s decoder", mime.c_str()); + ALOGE("Failed to create %s%s decoder", + (secure ? "secure " : ""), mime.c_str()); handleError(UNKNOWN_ERROR); return; } @@ -107,6 +136,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { // the following should work after start CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers)); + releaseAndResetMediaBuffers(); CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers)); ALOGV("[%s] got %zu input and %zu output buffers", mComponentName.c_str(), @@ -117,6 +147,18 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { mPaused = false; } +void NuPlayer::Decoder::releaseAndResetMediaBuffers() { + for (size_t i = 0; i < mMediaBuffers.size(); i++) { + if (mMediaBuffers[i] != NULL) { + mMediaBuffers[i]->release(); + mMediaBuffers.editItemAt(i) = NULL; + } + } + mMediaBuffers.resize(mInputBuffers.size()); + mInputBufferIsDequeued.clear(); + mInputBufferIsDequeued.resize(mInputBuffers.size()); +} + void NuPlayer::Decoder::requestCodecNotification() { if (mCodec != NULL) { sp<AMessage> reply = new AMessage(kWhatCodecNotify, id()); @@ -141,6 +183,14 @@ void NuPlayer::Decoder::configure(const sp<AMessage> &format) { msg->post(); } +status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id()); + msg->setPointer("buffers", buffers); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + void NuPlayer::Decoder::handleError(int32_t err) { sp<AMessage> notify = mNotify->dup(); @@ -163,6 +213,12 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { CHECK_LT(bufferIx, mInputBuffers.size()); + if (mMediaBuffers[bufferIx] != NULL) { + mMediaBuffers[bufferIx]->release(); + mMediaBuffers.editItemAt(bufferIx) = NULL; + } + mInputBufferIsDequeued.editItemAt(bufferIx) = true; + sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); reply->setSize("buffer-ix", bufferIx); reply->setInt32("generation", mBufferGeneration); @@ -183,6 +239,44 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { sp<ABuffer> buffer; bool hasBuffer = msg->findBuffer("buffer", &buffer); + + // handle widevine classic source - that fills an arbitrary input buffer + MediaBuffer *mediaBuffer = NULL; + if (hasBuffer && buffer->meta()->findPointer( + "mediaBuffer", (void **)&mediaBuffer)) { + if (mediaBuffer == NULL) { + // received no actual buffer + ALOGW("[%s] received null MediaBuffer %s", + mComponentName.c_str(), msg->debugString().c_str()); + buffer = NULL; + } else { + // likely filled another buffer than we requested: adjust buffer index + size_t ix; + for (ix = 0; ix < mInputBuffers.size(); ix++) { + const sp<ABuffer> &buf = mInputBuffers[ix]; + if (buf->data() == mediaBuffer->data()) { + // all input buffers are dequeued on start, hence the check + CHECK(mInputBufferIsDequeued[ix]); + ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu", + mComponentName.c_str(), ix, bufferIx); + + // TRICKY: need buffer for the metadata, so instead, set + // codecBuffer to the same (though incorrect) buffer to + // avoid a memcpy into the codecBuffer + codecBuffer = buffer; + codecBuffer->setRange( + mediaBuffer->range_offset(), + mediaBuffer->range_length()); + bufferIx = ix; + break; + } + } + CHECK(ix < mInputBuffers.size()); + } + } + + mInputBufferIsDequeued.editItemAt(bufferIx) = false; + if (buffer == NULL /* includes !hasBuffer */) { int32_t streamErr = ERROR_END_OF_STREAM; CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); @@ -236,6 +330,11 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { mComponentName.c_str(), err); handleError(err); } + + if (mediaBuffer != NULL) { + CHECK(mMediaBuffers[bufferIx] == NULL); + mMediaBuffers.editItemAt(bufferIx) = mediaBuffer; + } } } @@ -352,6 +451,8 @@ void NuPlayer::Decoder::onFlush() { return; } + releaseAndResetMediaBuffers(); + sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); @@ -379,6 +480,8 @@ void NuPlayer::Decoder::onShutdown() { mComponentName = "decoder"; } + releaseAndResetMediaBuffers(); + if (err != OK) { ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); handleError(err); @@ -403,6 +506,23 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatGetInputBuffers: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + Vector<sp<ABuffer> > *dstBuffers; + CHECK(msg->findPointer("buffers", (void **)&dstBuffers)); + + dstBuffers->clear(); + for (size_t i = 0; i < mInputBuffers.size(); i++) { + dstBuffers->push(mInputBuffers[i]); + } + + (new AMessage)->postReply(replyID); + break; + } + case kWhatCodecNotify: { if (!isStaleReply(msg)) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 4fa0dbd..c6fc237 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -26,6 +26,7 @@ namespace android { struct ABuffer; struct MediaCodec; +struct MediaBuffer; struct NuPlayer::Decoder : public AHandler { Decoder(const sp<AMessage> ¬ify, @@ -34,6 +35,7 @@ struct NuPlayer::Decoder : public AHandler { virtual void configure(const sp<AMessage> &format); virtual void init(); + status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; virtual void signalFlush(); virtual void signalResume(); virtual void initiateShutdown(); @@ -60,6 +62,7 @@ private: enum { kWhatCodecNotify = 'cdcN', kWhatConfigure = 'conf', + kWhatGetInputBuffers = 'gInB', kWhatInputBufferFilled = 'inpF', kWhatRenderBuffer = 'rndr', kWhatFlush = 'flus', @@ -77,11 +80,14 @@ private: Vector<sp<ABuffer> > mInputBuffers; Vector<sp<ABuffer> > mOutputBuffers; + Vector<bool> mInputBufferIsDequeued; + Vector<MediaBuffer *> mMediaBuffers; void handleError(int32_t err); bool handleAnInputBuffer(); bool handleAnOutputBuffer(); + void releaseAndResetMediaBuffers(); void requestCodecNotification(); bool isStaleReply(const sp<AMessage> &msg); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index f520ff7..3640038 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -26,6 +26,8 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> +#include <inttypes.h> + namespace android { // static @@ -221,6 +223,12 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatAudioOffloadTearDown: + { + onAudioOffloadTearDown(); + break; + } + default: TRESPASS(); break; @@ -292,7 +300,7 @@ size_t NuPlayer::Renderer::AudioSinkCallback( case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN: { - // TODO: send this to player. + me->notifyAudioOffloadTearDown(); break; } } @@ -502,6 +510,7 @@ void NuPlayer::Renderer::postDrainVideoQueue() { } } + ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); msg->post(delayUs); mDrainVideoQueuePending = true; @@ -579,6 +588,10 @@ void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) { notify->post(); } +void NuPlayer::Renderer::notifyAudioOffloadTearDown() { + (new AMessage(kWhatAudioOffloadTearDown, id()))->post(); +} + void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) { int32_t audio; CHECK(msg->findInt32("audio", &audio)); @@ -811,6 +824,7 @@ void NuPlayer::Renderer::onAudioSinkChanged() { void NuPlayer::Renderer::onDisableOffloadAudio() { Mutex::Autolock autoLock(mLock); mFlags &= ~FLAG_OFFLOAD_AUDIO; + ++mAudioQueueGeneration; } void NuPlayer::Renderer::notifyPosition() { @@ -877,5 +891,21 @@ void NuPlayer::Renderer::onResume() { } } +void NuPlayer::Renderer::onAudioOffloadTearDown() { + uint32_t numFramesPlayed; + CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); + + int64_t currentPositionUs = mFirstAudioTimeUs + + (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll; + + mAudioSink->stop(); + mAudioSink->flush(); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatAudioOffloadTearDown); + notify->setInt64("positionUs", currentPositionUs); + notify->post(); +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 6e86a8f..1cba1a0 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -62,6 +62,7 @@ struct NuPlayer::Renderer : public AHandler { kWhatPosition = 'posi', kWhatVideoRenderingStart = 'vdrd', kWhatMediaRenderingStart = 'mdrd', + kWhatAudioOffloadTearDown = 'aOTD', }; protected: @@ -143,12 +144,14 @@ private: void onDisableOffloadAudio(); void onPause(); void onResume(); + void onAudioOffloadTearDown(); void notifyEOS(bool audio, status_t finalResult); void notifyFlushComplete(bool audio); void notifyPosition(); void notifyVideoLateBy(int64_t lateByUs); void notifyVideoRenderingStart(); + void notifyAudioOffloadTearDown(); void flushQueue(List<QueueEntry> *queue); bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 632c4a6..259925f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -21,11 +21,14 @@ #include "NuPlayer.h" #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <utils/Vector.h> namespace android { struct ABuffer; struct MetaData; +struct MediaBuffer; struct NuPlayer::Source : public AHandler { enum Flags { @@ -34,6 +37,7 @@ struct NuPlayer::Source : public AHandler { FLAG_CAN_SEEK_FORWARD = 4, // the "10 sec forward button" FLAG_CAN_SEEK = 8, // the "seek bar" FLAG_DYNAMIC_DURATION = 16, + FLAG_SECURE = 32, }; enum { @@ -89,6 +93,10 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } + virtual status_t setBuffers(bool /* audio */, Vector<MediaBuffer *> &/* buffers */) { + return INVALID_OPERATION; + } + virtual bool isRealTime() const { return false; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 9c64d72..6cb1c64 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3989,6 +3989,8 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { if (err == OK) { break; + } else { + ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str()); } node = NULL; @@ -4504,11 +4506,14 @@ void ACodec::ExecutingState::resume() { submitOutputBuffers(); - // Post the first input buffer. + // Post all available input buffers CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u); - BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0); - - postFillThisBuffer(info); + for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); i++) { + BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i); + if (info->mStatus == BufferInfo::OWNED_BY_US) { + postFillThisBuffer(info); + } + } mActive = true; } diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 207acc8..19da6ee 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -3665,7 +3665,7 @@ status_t MPEG4Source::read( uint32_t sampleIndex; status_t err = mSampleTable->findSampleAtTime( - seekTimeUs * mTimescale / 1000000, + seekTimeUs, 1000000, mTimescale, &sampleIndex, findFlags); if (mode == ReadOptions::SEEK_CLOSEST) { diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp index 80aae51..6ac6d4a 100644 --- a/media/libstagefright/MediaBufferGroup.cpp +++ b/media/libstagefright/MediaBufferGroup.cpp @@ -55,7 +55,8 @@ void MediaBufferGroup::add_buffer(MediaBuffer *buffer) { mLastBuffer = buffer; } -status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) { +status_t MediaBufferGroup::acquire_buffer( + MediaBuffer **out, bool nonBlocking) { Mutex::Autolock autoLock(mLock); for (;;) { @@ -70,6 +71,11 @@ status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) { } } + if (nonBlocking) { + *out = NULL; + return WOULD_BLOCK; + } + // All buffers are in use. Block until one of them is returned to us. mCondition.wait(mLock); } diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 7a9cb0b..15e062e 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -16,13 +16,13 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaCodec" -#include <utils/Log.h> #include <inttypes.h> -#include <media/stagefright/MediaCodec.h> - +#include "include/avc_utils.h" #include "include/SoftwareRenderer.h" +#include <binder/IBatteryStats.h> +#include <binder/IServiceManager.h> #include <gui/Surface.h> #include <media/ICrypto.h> #include <media/stagefright/foundation/ABuffer.h> @@ -32,16 +32,85 @@ #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/ACodec.h> #include <media/stagefright/BufferProducerWrapper.h> +#include <media/stagefright/MediaCodec.h> #include <media/stagefright/MediaCodecList.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/NativeWindowWrapper.h> - -#include "include/avc_utils.h" +#include <private/android_filesystem_config.h> +#include <utils/Log.h> +#include <utils/Singleton.h> namespace android { +struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> { + BatteryNotifier(); + + void noteStartVideo(); + void noteStopVideo(); + void noteStartAudio(); + void noteStopAudio(); + +private: + int32_t mVideoRefCount; + int32_t mAudioRefCount; + sp<IBatteryStats> mBatteryStatService; +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier) + +MediaCodec::BatteryNotifier::BatteryNotifier() : + mVideoRefCount(0), + mAudioRefCount(0) { + // get battery service + const sp<IServiceManager> sm(defaultServiceManager()); + if (sm != NULL) { + const String16 name("batterystats"); + mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name)); + if (mBatteryStatService == NULL) { + ALOGE("batterystats service unavailable!"); + } + } +} + +void MediaCodec::BatteryNotifier::noteStartVideo() { + if (mVideoRefCount == 0 && mBatteryStatService != NULL) { + mBatteryStatService->noteStartVideo(AID_MEDIA); + } + mVideoRefCount++; +} + +void MediaCodec::BatteryNotifier::noteStopVideo() { + if (mVideoRefCount == 0) { + ALOGW("BatteryNotifier::noteStop(): video refcount is broken!"); + return; + } + + mVideoRefCount--; + if (mVideoRefCount == 0 && mBatteryStatService != NULL) { + mBatteryStatService->noteStopVideo(AID_MEDIA); + } +} + +void MediaCodec::BatteryNotifier::noteStartAudio() { + if (mAudioRefCount == 0 && mBatteryStatService != NULL) { + mBatteryStatService->noteStartAudio(AID_MEDIA); + } + mAudioRefCount++; +} + +void MediaCodec::BatteryNotifier::noteStopAudio() { + if (mAudioRefCount == 0) { + ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!"); + return; + } + + mAudioRefCount--; + if (mAudioRefCount == 0 && mBatteryStatService != NULL) { + mBatteryStatService->noteStopAudio(AID_MEDIA); + } +} // static sp<MediaCodec> MediaCodec::CreateByType( const sp<ALooper> &looper, const char *mime, bool encoder) { @@ -71,6 +140,8 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper) mReplyID(0), mFlags(0), mSoftRenderer(NULL), + mBatteryStatNotified(false), + mIsVideo(false), mDequeueInputTimeoutGeneration(0), mDequeueInputReplyID(0), mDequeueOutputTimeoutGeneration(0), @@ -756,7 +827,6 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case CodecBase::kWhatComponentConfigured: { CHECK_EQ(mState, CONFIGURING); - setState(CONFIGURED); // reset input surface flag mHaveInputSurface = false; @@ -764,6 +834,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findMessage("input-format", &mInputFormat)); CHECK(msg->findMessage("output-format", &mOutputFormat)); + setState(CONFIGURED); (new AMessage)->postReply(mReplyID); break; } @@ -1620,6 +1691,8 @@ void MediaCodec::setState(State newState) { mState = newState; cancelPendingDequeueOperations(); + + updateBatteryStat(); } void MediaCodec::returnBuffersToCodec() { @@ -2054,4 +2127,34 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData( return OK; } +void MediaCodec::updateBatteryStat() { + if (mState == CONFIGURED && !mBatteryStatNotified) { + AString mime; + CHECK(mOutputFormat != NULL && + mOutputFormat->findString("mime", &mime)); + + mIsVideo = mime.startsWithIgnoreCase("video/"); + + BatteryNotifier& notifier(BatteryNotifier::getInstance()); + + if (mIsVideo) { + notifier.noteStartVideo(); + } else { + notifier.noteStartAudio(); + } + + mBatteryStatNotified = true; + } else if (mState == UNINITIALIZED && mBatteryStatNotified) { + BatteryNotifier& notifier(BatteryNotifier::getInstance()); + + if (mIsVideo) { + notifier.noteStopVideo(); + } else { + notifier.noteStopAudio(); + } + + mBatteryStatNotified = false; + } +} + } // namespace android diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index cd51582..8f54343 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -21,6 +21,7 @@ #include <media/stagefright/MediaCodecList.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> @@ -79,6 +80,19 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) { info->mName.c_str()); mCodecInfos.removeAt(i); +#if LOG_NDEBUG == 0 + } else { + for (size_t type_ix = 0; type_ix < mTypes.size(); ++type_ix) { + uint32_t typeMask = 1ul << mTypes.valueAt(type_ix); + if (info->mTypes & typeMask) { + AString mime = mTypes.keyAt(type_ix); + uint32_t bit = mTypes.valueAt(type_ix); + + ALOGV("%s codec info for %s: %s", info->mName.c_str(), mime.c_str(), + info->mCaps.editValueFor(bit)->debugString().c_str()); + } + } +#endif } } @@ -217,6 +231,8 @@ void MediaCodecList::startElementHandler( return; } + bool inType = true; + if (!strcmp(name, "Include")) { mInitCheck = includeXMLFile(attrs); if (mInitCheck == OK) { @@ -267,6 +283,26 @@ void MediaCodecList::startElementHandler( mInitCheck = addQuirk(attrs); } else if (!strcmp(name, "Type")) { mInitCheck = addTypeFromAttributes(attrs); + mCurrentSection = + (mCurrentSection == SECTION_DECODER + ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); + } + } + inType = false; + // fall through + + case SECTION_DECODER_TYPE: + case SECTION_ENCODER_TYPE: + { + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + // ignore limits and features specified outside of type + bool outside = !inType && info->mSoleType == 0; + if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) { + ALOGW("ignoring %s specified outside of a Type", name); + } else if (!strcmp(name, "Limit")) { + mInitCheck = addLimit(attrs); + } else if (!strcmp(name, "Feature")) { + mInitCheck = addFeature(attrs); } break; } @@ -300,10 +336,27 @@ void MediaCodecList::endElementHandler(const char *name) { break; } + case SECTION_DECODER_TYPE: + case SECTION_ENCODER_TYPE: + { + if (!strcmp(name, "Type")) { + mCurrentSection = + (mCurrentSection == SECTION_DECODER_TYPE + ? SECTION_DECODER : SECTION_ENCODER); + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + info->mCurrentCaps = NULL; + } + break; + } + case SECTION_DECODER: { if (!strcmp(name, "MediaCodec")) { mCurrentSection = SECTION_DECODERS; + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + info->mCurrentCaps = NULL; } break; } @@ -312,6 +365,9 @@ void MediaCodecList::endElementHandler(const char *name) { { if (!strcmp(name, "MediaCodec")) { mCurrentSection = SECTION_ENCODERS; + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + info->mCurrentCaps = NULL; } break; } @@ -373,11 +429,16 @@ void MediaCodecList::addMediaCodec( CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); info->mName = name; info->mIsEncoder = encoder; + info->mSoleType = 0; info->mTypes = 0; info->mQuirks = 0; + info->mCurrentCaps = NULL; if (type != NULL) { addType(type); + // if type was specified in attributes, we do not allow + // subsequent types + info->mSoleType = info->mTypes; } } @@ -427,6 +488,12 @@ status_t MediaCodecList::addQuirk(const char **attrs) { status_t MediaCodecList::addTypeFromAttributes(const char **attrs) { const char *name = NULL; + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + if (info->mSoleType != 0) { + ALOGE("Codec '%s' already had its type specified", info->mName.c_str()); + return -EINVAL; + } + size_t i = 0; while (attrs[i] != NULL) { if (!strcmp(attrs[i], "name")) { @@ -469,6 +536,11 @@ void MediaCodecList::addType(const char *name) { CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); info->mTypes |= 1ul << bit; + if (info->mCaps.indexOfKey(bit) < 0) { + AMessage *msg = new AMessage(); + info->mCaps.add(bit, msg); + } + info->mCurrentCaps = info->mCaps.editValueFor(bit); } ssize_t MediaCodecList::findCodecByType( @@ -494,6 +566,216 @@ ssize_t MediaCodecList::findCodecByType( return -ENOENT; } +static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) { + ALOGE("limit '%s' with %s'%s' attribute", name.c_str(), + (found ? "" : "no "), attr); + return -EINVAL; +} + +static status_t limitError(AString name, const char *msg) { + ALOGE("limit '%s' %s", name.c_str(), msg); + return -EINVAL; +} + +static status_t limitInvalidAttr(AString name, const char *attr, AString value) { + ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(), + attr, value.c_str()); + return -EINVAL; +} + +status_t MediaCodecList::addLimit(const char **attrs) { + sp<AMessage> msg = new AMessage(); + + size_t i = 0; + while (attrs[i] != NULL) { + if (attrs[i + 1] == NULL) { + return -EINVAL; + } + + // attributes with values + if (!strcmp(attrs[i], "name") + || !strcmp(attrs[i], "default") + || !strcmp(attrs[i], "in") + || !strcmp(attrs[i], "max") + || !strcmp(attrs[i], "min") + || !strcmp(attrs[i], "range") + || !strcmp(attrs[i], "ranges") + || !strcmp(attrs[i], "scale") + || !strcmp(attrs[i], "value")) { + msg->setString(attrs[i], attrs[i + 1]); + ++i; + } else { + return -EINVAL; + } + ++i; + } + + AString name; + if (!msg->findString("name", &name)) { + ALOGE("limit with no 'name' attribute"); + return -EINVAL; + } + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + + // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range + // quality: range + default + [scale] + // complexity: range + default + bool found; + if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" + || name == "blocks-per-second" || name == "complexity" + || name == "frame-rate" || name == "quality" || name == "size") { + AString min, max; + if (msg->findString("min", &min) && msg->findString("max", &max)) { + min.append("-"); + min.append(max); + if (msg->contains("range") || msg->contains("value")) { + return limitError(name, "has 'min' and 'max' as well as 'range' or " + "'value' attributes"); + } + msg->setString("range", min); + } else if (msg->contains("min") || msg->contains("max")) { + return limitError(name, "has only 'min' or 'max' attribute"); + } else if (msg->findString("value", &max)) { + min = max; + min.append("-"); + min.append(max); + if (msg->contains("range")) { + return limitError(name, "has both 'range' and 'value' attributes"); + } + msg->setString("range", min); + } + + AString range, scale = "linear", def, in_; + if (!msg->findString("range", &range)) { + return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes"); + } + + if ((name == "quality" || name == "complexity") ^ + (found = msg->findString("default", &def))) { + return limitFoundMissingAttr(name, "default", found); + } + if (name != "quality" && msg->findString("scale", &scale)) { + return limitFoundMissingAttr(name, "scale"); + } + if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) { + return limitFoundMissingAttr(name, "in", found); + } + + if (name == "aspect-ratio") { + if (!(in_ == "pixels") && !(in_ == "blocks")) { + return limitInvalidAttr(name, "in", in_); + } + in_.erase(5, 1); // (pixel|block)-aspect-ratio + in_.append("-"); + in_.append(name); + name = in_; + } + if (name == "quality") { + info->mCurrentCaps->setString("quality-scale", scale); + } + if (name == "quality" || name == "complexity") { + AString tag = name; + tag.append("-default"); + info->mCurrentCaps->setString(tag.c_str(), def); + } + AString tag = name; + tag.append("-range"); + info->mCurrentCaps->setString(tag.c_str(), range); + } else { + AString max, value, ranges; + if (msg->contains("default")) { + return limitFoundMissingAttr(name, "default"); + } else if (msg->contains("in")) { + return limitFoundMissingAttr(name, "in"); + } else if ((name == "channel-count") ^ + (found = msg->findString("max", &max))) { + return limitFoundMissingAttr(name, "max", found); + } else if (msg->contains("min")) { + return limitFoundMissingAttr(name, "min"); + } else if (msg->contains("range")) { + return limitFoundMissingAttr(name, "range"); + } else if ((name == "sample-rate") ^ + (found = msg->findString("ranges", &ranges))) { + return limitFoundMissingAttr(name, "ranges", found); + } else if (msg->contains("scale")) { + return limitFoundMissingAttr(name, "scale"); + } else if ((name == "alignment" || name == "block-size") ^ + (found = msg->findString("value", &value))) { + return limitFoundMissingAttr(name, "value", found); + } + + if (max.size()) { + AString tag = "max-"; + tag.append(name); + info->mCurrentCaps->setString(tag.c_str(), max); + } else if (value.size()) { + info->mCurrentCaps->setString(name.c_str(), value); + } else if (ranges.size()) { + AString tag = name; + tag.append("-ranges"); + info->mCurrentCaps->setString(tag.c_str(), ranges); + } else { + ALOGW("Ignoring unrecognized limit '%s'", name.c_str()); + } + } + return OK; +} + +static bool parseBoolean(const char *s) { + if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) { + return true; + } + char *end; + unsigned long res = strtoul(s, &end, 10); + return *s != '\0' && *end == '\0' && res > 0; +} + +status_t MediaCodecList::addFeature(const char **attrs) { + size_t i = 0; + const char *name = NULL; + int32_t optional = -1; + int32_t required = -1; + + while (attrs[i] != NULL) { + if (attrs[i + 1] == NULL) { + return -EINVAL; + } + + // attributes with values + if (!strcmp(attrs[i], "name")) { + name = attrs[i + 1]; + ++i; + } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) { + int value = (int)parseBoolean(attrs[i + 1]); + if (!strcmp(attrs[i], "optional")) { + optional = value; + } else { + required = value; + } + ++i; + } else { + return -EINVAL; + } + ++i; + } + if (name == NULL) { + ALOGE("feature with no 'name' attribute"); + return -EINVAL; + } + + if (optional == required && optional != -1) { + ALOGE("feature '%s' is both/neither optional and required", name); + return -EINVAL; + } + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + AString tag = "feature-"; + tag.append(name); + info->mCurrentCaps->setInt32(tag.c_str(), (required == 1) || (optional == 0)); + return OK; +} + ssize_t MediaCodecList::findCodecByName(const char *name) const { for (size_t i = 0; i < mCodecInfos.size(); ++i) { const CodecInfo &info = mCodecInfos.itemAt(i); @@ -571,7 +853,8 @@ status_t MediaCodecList::getCodecCapabilities( size_t index, const char *type, Vector<ProfileLevel> *profileLevels, Vector<uint32_t> *colorFormats, - uint32_t *flags) const { + uint32_t *flags, + sp<AMessage> *capabilities) const { profileLevels->clear(); colorFormats->clear(); @@ -581,6 +864,13 @@ status_t MediaCodecList::getCodecCapabilities( const CodecInfo &info = mCodecInfos.itemAt(index); + ssize_t typeIndex = mTypes.indexOfKey(type); + if (typeIndex < 0) { + return -EINVAL; + } + // essentially doing valueFor without the CHECK abort + typeIndex = mTypes.valueAt(typeIndex); + OMXClient client; status_t err = client.connect(); if (err != OK) { @@ -611,6 +901,11 @@ status_t MediaCodecList::getCodecCapabilities( *flags = caps.mFlags; + // TODO this check will be removed once JNI side is merged + if (capabilities != NULL) { + *capabilities = info.mCaps.valueFor(typeIndex); + } + return OK; } diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp index fd0e79c..576471a 100644 --- a/media/libstagefright/MediaSource.cpp +++ b/media/libstagefright/MediaSource.cpp @@ -32,6 +32,19 @@ void MediaSource::ReadOptions::reset() { mOptions = 0; mSeekTimeUs = 0; mLatenessUs = 0; + mNonBlocking = false; +} + +void MediaSource::ReadOptions::setNonBlocking() { + mNonBlocking = true; +} + +void MediaSource::ReadOptions::clearNonBlocking() { + mNonBlocking = false; +} + +bool MediaSource::ReadOptions::getNonBlocking() const { + return mNonBlocking; } void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) { diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 8c15929..821bd81 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -320,22 +320,26 @@ status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { } size_t left = 0; - size_t right = mTableOfContents.size(); - while (left < right) { - size_t center = left / 2 + right / 2 + (left & right & 1); + size_t right_plus_one = mTableOfContents.size(); + while (left < right_plus_one) { + size_t center = left + (right_plus_one - left) / 2; const TOCEntry &entry = mTableOfContents.itemAt(center); if (timeUs < entry.mTimeUs) { - right = center; + right_plus_one = center; } else if (timeUs > entry.mTimeUs) { left = center + 1; } else { - left = right = center; + left = center; break; } } + if (left == mTableOfContents.size()) { + --left; + } + const TOCEntry &entry = mTableOfContents.itemAt(left); ALOGV("seeking to entry %zu / %zu at offset %lld", diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index 9a92805..bad43f2 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -520,83 +520,72 @@ void SampleTable::buildSampleEntriesTable() { } status_t SampleTable::findSampleAtTime( - uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + uint64_t req_time, uint64_t scale_num, uint64_t scale_den, + uint32_t *sample_index, uint32_t flags) { buildSampleEntriesTable(); uint32_t left = 0; - uint32_t right = mNumSampleSizes; - while (left < right) { - uint32_t center = (left + right) / 2; - uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime; + uint32_t right_plus_one = mNumSampleSizes; + while (left < right_plus_one) { + uint32_t center = left + (right_plus_one - left) / 2; + uint64_t centerTime = + getSampleTime(center, scale_num, scale_den); if (req_time < centerTime) { - right = center; + right_plus_one = center; } else if (req_time > centerTime) { left = center + 1; } else { - left = center; - break; + *sample_index = mSampleTimeEntries[center].mSampleIndex; + return OK; } } - if (left == mNumSampleSizes) { + uint32_t closestIndex = left; + + if (closestIndex == mNumSampleSizes) { if (flags == kFlagAfter) { return ERROR_OUT_OF_RANGE; } - - --left; + flags = kFlagBefore; + } else if (closestIndex == 0) { + if (flags == kFlagBefore) { + // normally we should return out of range, but that is + // treated as end-of-stream. instead return first sample + // + // return ERROR_OUT_OF_RANGE; + } + flags = kFlagAfter; } - uint32_t closestIndex = left; - switch (flags) { case kFlagBefore: { - while (closestIndex > 0 - && mSampleTimeEntries[closestIndex].mCompositionTime - > req_time) { - --closestIndex; - } + --closestIndex; break; } case kFlagAfter: { - while (closestIndex + 1 < mNumSampleSizes - && mSampleTimeEntries[closestIndex].mCompositionTime - < req_time) { - ++closestIndex; - } + // nothing to do break; } default: { CHECK(flags == kFlagClosest); - - if (closestIndex > 0) { - // Check left neighbour and pick closest. - uint32_t absdiff1 = - abs_difference( - mSampleTimeEntries[closestIndex].mCompositionTime, - req_time); - - uint32_t absdiff2 = - abs_difference( - mSampleTimeEntries[closestIndex - 1].mCompositionTime, - req_time); - - if (absdiff1 > absdiff2) { - closestIndex = closestIndex - 1; - } + // pick closest based on timestamp. use abs_difference for safety + if (abs_difference( + getSampleTime(closestIndex, scale_num, scale_den), req_time) > + abs_difference( + req_time, getSampleTime(closestIndex - 1, scale_num, scale_den))) { + --closestIndex; } - break; } } *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex; - return OK; } @@ -618,109 +607,85 @@ status_t SampleTable::findSyncSampleNear( } uint32_t left = 0; - uint32_t right = mNumSyncSamples; - while (left < right) { - uint32_t center = left + (right - left) / 2; + uint32_t right_plus_one = mNumSyncSamples; + while (left < right_plus_one) { + uint32_t center = left + (right_plus_one - left) / 2; uint32_t x = mSyncSamples[center]; if (start_sample_index < x) { - right = center; + right_plus_one = center; } else if (start_sample_index > x) { left = center + 1; } else { - left = center; - break; + *sample_index = x; + return OK; } } + if (left == mNumSyncSamples) { if (flags == kFlagAfter) { ALOGE("tried to find a sync frame after the last one: %d", left); return ERROR_OUT_OF_RANGE; } - left = left - 1; + flags = kFlagBefore; } + else if (left == 0) { + if (flags == kFlagBefore) { + ALOGE("tried to find a sync frame before the first one: %d", left); - // Now ssi[left] is the sync sample index just before (or at) - // start_sample_index. - // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples. - - uint32_t x = mSyncSamples[left]; - - if (left + 1 < mNumSyncSamples) { - uint32_t y = mSyncSamples[left + 1]; - - // our sample lies between sync samples x and y. - - status_t err = mSampleIterator->seekTo(start_sample_index); - if (err != OK) { - return err; - } - - uint32_t sample_time = mSampleIterator->getSampleTime(); - - err = mSampleIterator->seekTo(x); - if (err != OK) { - return err; - } - uint32_t x_time = mSampleIterator->getSampleTime(); - - err = mSampleIterator->seekTo(y); - if (err != OK) { - return err; - } - - uint32_t y_time = mSampleIterator->getSampleTime(); - - if (abs_difference(x_time, sample_time) - > abs_difference(y_time, sample_time)) { - // Pick the sync sample closest (timewise) to the start-sample. - x = y; - ++left; + // normally we should return out of range, but that is + // treated as end-of-stream. instead seek to first sync + // + // return ERROR_OUT_OF_RANGE; } + flags = kFlagAfter; } + // Now ssi[left - 1] <(=) start_sample_index <= ssi[left] switch (flags) { case kFlagBefore: { - if (x > start_sample_index) { - CHECK(left > 0); - - x = mSyncSamples[left - 1]; - - if (x > start_sample_index) { - // The table of sync sample indices was not sorted - // properly. - return ERROR_MALFORMED; - } - } + --left; break; } - case kFlagAfter: { - if (x < start_sample_index) { - if (left + 1 >= mNumSyncSamples) { - return ERROR_OUT_OF_RANGE; - } - - x = mSyncSamples[left + 1]; - - if (x < start_sample_index) { - // The table of sync sample indices was not sorted - // properly. - return ERROR_MALFORMED; - } - } - + // nothing to do break; } - default: + { + // this route is not used, but implement it nonetheless + CHECK(flags == kFlagClosest); + + status_t err = mSampleIterator->seekTo(start_sample_index); + if (err != OK) { + return err; + } + uint32_t sample_time = mSampleIterator->getSampleTime(); + + err = mSampleIterator->seekTo(mSyncSamples[left]); + if (err != OK) { + return err; + } + uint32_t upper_time = mSampleIterator->getSampleTime(); + + err = mSampleIterator->seekTo(mSyncSamples[left - 1]); + if (err != OK) { + return err; + } + uint32_t lower_time = mSampleIterator->getSampleTime(); + + // use abs_difference for safety + if (abs_difference(upper_time, sample_time) > + abs_difference(sample_time, lower_time)) { + --left; + } break; + } } - *sample_index = x; - + *sample_index = mSyncSamples[left]; return OK; } diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index dc42f91..d268aa4 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -127,6 +127,20 @@ const AMessage::Item *AMessage::findItem( return NULL; } +bool AMessage::contains(const char *name) const { + name = AAtomizer::Atomize(name); + + for (size_t i = 0; i < mNumItems; ++i) { + const Item *item = &mItems[i]; + + if (item->mName == name) { + return true; + } + } + + return false; +} + #define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \ void AMessage::set##NAME(const char *name, TYPENAME value) { \ Item *item = allocateItem(name); \ @@ -160,6 +174,11 @@ void AMessage::setString( item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len); } +void AMessage::setString( + const char *name, const AString &s) { + setString(name, s.c_str(), s.size()); +} + void AMessage::setObjectInternal( const char *name, const sp<RefBase> &obj, Type type) { Item *item = allocateItem(name); diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index fe146f2..d06df7b 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -75,7 +75,8 @@ public: kFlagClosest }; status_t findSampleAtTime( - uint32_t req_time, uint32_t *sample_index, uint32_t flags); + uint64_t req_time, uint64_t scale_num, uint64_t scale_den, + uint32_t *sample_index, uint32_t flags); status_t findSyncSampleNear( uint32_t start_sample_index, uint32_t *sample_index, @@ -138,6 +139,13 @@ private: friend struct SampleIterator; + // normally we don't round + inline uint64_t getSampleTime( + size_t sample_index, uint64_t scale_num, uint64_t scale_den) const { + return (mSampleTimeEntries[sample_index].mCompositionTime + * scale_num) / scale_den; + } + status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); uint32_t getCompositionTimeOffset(uint32_t sampleIndex); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 871824a..a0319ab 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -230,6 +230,11 @@ void AnotherPacketSource::queueDiscontinuity( int32_t oldDiscontinuityType; if (!oldBuffer->meta()->findInt32( "discontinuity", &oldDiscontinuityType)) { + MediaBuffer *mbuf = NULL; + oldBuffer->meta()->findPointer("mediaBuffer", (void**)&mbuf); + if (mbuf != NULL) { + mbuf->release(); + } it = mBuffers.erase(it); continue; } diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 22b12d9..cc4770a 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -233,7 +233,7 @@ status_t OMX::allocateNode( instance, &handle); if (err != OMX_ErrorNone) { - ALOGV("FAILED to allocate omx component '%s'", name); + ALOGE("FAILED to allocate omx component '%s'", name); instance->onGetHandleFailed(); |