diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 133 | ||||
-rw-r--r-- | media/libmedia/AudioTrackShared.cpp | 10 | ||||
-rw-r--r-- | media/libmedia/ICrypto.cpp | 42 | ||||
-rw-r--r-- | media/libmediaplayerservice/Crypto.cpp | 10 | ||||
-rw-r--r-- | media/libmediaplayerservice/Crypto.h | 2 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp | 263 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h | 11 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp | 3 | ||||
-rw-r--r-- | media/libstagefright/ACodec.cpp | 49 | ||||
-rw-r--r-- | media/libstagefright/MediaCodec.cpp | 16 | ||||
-rw-r--r-- | media/libstagefright/MediaCodecSource.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSession.cpp | 67 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSession.h | 2 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.cpp | 27 |
14 files changed, 462 insertions, 179 deletions
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 9e9ec5b..89138e2 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -56,6 +56,24 @@ static int64_t getNowUs() return convertTimespecToUs(tv); } +// Must match similar computation in createTrack_l in Threads.cpp. +// TODO: Move to a common library +static size_t calculateMinFrameCount( + uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate, + uint32_t sampleRate, float speed) +{ + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate); + if (minBufCount < 2) { + minBufCount = 2; + } + ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u " + "sampleRate %u speed %f minBufCount: %u", + afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount); + return minBufCount * sourceFramesNeededWithTimestretch( + sampleRate, afFrameCount, afSampleRate, speed); +} + // static status_t AudioTrack::getMinFrameCount( size_t* frameCount, @@ -94,13 +112,10 @@ status_t AudioTrack::getMinFrameCount( return status; } - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate); - if (minBufCount < 2) { - minBufCount = 2; - } + // When called from createTrack, speed is 1.0f (normal speed). + // This is rechecked again on setting playback rate (TODO: on setting sample rate, too). + *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f); - *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate); // The formula above should always produce a non-zero value under normal circumstances: // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX. // Return error in the unlikely event that it does not, as that's part of the API contract. @@ -109,8 +124,8 @@ status_t AudioTrack::getMinFrameCount( streamType, sampleRate); return BAD_VALUE; } - ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u", - *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency); + ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u", + *frameCount, afFrameCount, afSampleRate, afLatency); return NO_ERROR; } @@ -360,6 +375,8 @@ status_t AudioTrack::set( return BAD_VALUE; } mSampleRate = sampleRate; + mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL; + mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL; // Make copy of input parameter offloadInfo so that in the future: // (a) createTrack_l doesn't need it as an input parameter @@ -689,6 +706,7 @@ status_t AudioTrack::setSampleRate(uint32_t rate) if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { return BAD_VALUE; } + // TODO: Should we also check if the buffer size is compatible? mSampleRate = rate; mProxy->setSampleRate(rate); @@ -719,6 +737,42 @@ uint32_t AudioTrack::getSampleRate() const return mSampleRate; } +status_t AudioTrack::setPlaybackRate(float speed, float pitch) +{ + if (speed < AUDIO_TIMESTRETCH_SPEED_MIN + || speed > AUDIO_TIMESTRETCH_SPEED_MAX + || pitch < AUDIO_TIMESTRETCH_PITCH_MIN + || pitch > AUDIO_TIMESTRETCH_PITCH_MAX) { + return BAD_VALUE; + } + AutoMutex lock(mLock); + if (speed == mSpeed && pitch == mPitch) { + return NO_ERROR; + } + if (mIsTimed || isOffloadedOrDirect_l()) { + return INVALID_OPERATION; + } + if (mFlags & AUDIO_OUTPUT_FLAG_FAST) { + return INVALID_OPERATION; + } + // Check if the buffer size is compatible. + if (!isSampleRateSpeedAllowed_l(mSampleRate, speed)) { + ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch); + return BAD_VALUE; + } + mSpeed = speed; + mPitch = pitch; + mProxy->setPlaybackRate(speed, pitch); + return NO_ERROR; +} + +void AudioTrack::getPlaybackRate(float *speed, float *pitch) const +{ + AutoMutex lock(mLock); + *speed = mSpeed; + *pitch = mPitch; +} + status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) { if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) { @@ -1086,8 +1140,16 @@ status_t AudioTrack::createTrack_l() // there _is_ a frameCount parameter. We silently ignore it. frameCount = mSharedBuffer->size() / mFrameSize; } else { - // For fast and normal streaming tracks, - // the frame count calculations and checks are done by server + // For fast tracks the frame count calculations and checks are done by server + + if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) { + // for normal tracks precompute the frame count based on speed. + const size_t minFrameCount = calculateMinFrameCount( + afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed); + if (frameCount < minFrameCount) { + frameCount = minFrameCount; + } + } } IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; @@ -1230,6 +1292,7 @@ status_t AudioTrack::createTrack_l() } mAudioTrack->attachAuxEffect(mAuxEffectId); + // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack) // FIXME don't believe this lie mLatency = afLatency + (1000*frameCount) / mSampleRate; @@ -1255,6 +1318,7 @@ status_t AudioTrack::createTrack_l() mProxy->setSendLevel(mSendLevel); mProxy->setSampleRate(mSampleRate); + mProxy->setPlaybackRate(mSpeed, mPitch); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); @@ -1617,6 +1681,7 @@ nsecs_t AudioTrack::processAudioBuffer() // Cache other fields that will be needed soon uint32_t sampleRate = mSampleRate; + float speed = mSpeed; uint32_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { mRefreshRemaining = false; @@ -1745,7 +1810,7 @@ nsecs_t AudioTrack::processAudioBuffer() if (minFrames != (uint32_t) ~0) { // This "fudge factor" avoids soaking CPU, and compensates for late progress by server static const nsecs_t kFudgeNs = 10000000LL; // 10 ms - ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs; + ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs; } // If not supplying data by EVENT_MORE_DATA, then we're done @@ -1786,7 +1851,8 @@ nsecs_t AudioTrack::processAudioBuffer() if (mRetryOnPartialBuffer && !isOffloaded()) { mRetryOnPartialBuffer = false; if (avail < mRemainingFrames) { - int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate; + int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000) + / ((double)sampleRate * speed); if (ns < 0 || myns < ns) { ns = myns; } @@ -1841,7 +1907,7 @@ nsecs_t AudioTrack::processAudioBuffer() // that total to a sum == notificationFrames. if (0 < misalignment && misalignment <= mRemainingFrames) { mRemainingFrames = misalignment; - return (mRemainingFrames * 1100000000LL) / sampleRate; + return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed); } #endif @@ -1936,6 +2002,41 @@ uint32_t AudioTrack::updateAndGetPosition_l() return mPosition += (uint32_t) delta; } +bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const +{ + // applicable for mixing tracks only (not offloaded or direct) + if (mStaticProxy != 0) { + return true; // static tracks do not have issues with buffer sizing. + } + status_t status; + uint32_t afLatency; + status = AudioSystem::getLatency(mOutput, &afLatency); + if (status != NO_ERROR) { + ALOGE("getLatency(%d) failed status %d", mOutput, status); + return false; + } + + size_t afFrameCount; + status = AudioSystem::getFrameCount(mOutput, &afFrameCount); + if (status != NO_ERROR) { + ALOGE("getFrameCount(output=%d) status %d", mOutput, status); + return false; + } + + uint32_t afSampleRate; + status = AudioSystem::getSamplingRate(mOutput, &afSampleRate); + if (status != NO_ERROR) { + ALOGE("getSamplingRate(output=%d) status %d", mOutput, status); + return false; + } + + const size_t minFrameCount = + calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed); + ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu", + mFrameCount, minFrameCount); + return mFrameCount >= minFrameCount; +} + status_t AudioTrack::setParameters(const String8& keyValuePairs) { AutoMutex lock(mLock); @@ -2001,7 +2102,8 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) return WOULD_BLOCK; // stale timestamp time, occurs before start. } const int64_t deltaTimeUs = timestampTimeUs - mStartUs; - const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate; + const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000 + / ((double)mSampleRate * mSpeed); if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) { // Verify that the counter can't count faster than the sample rate @@ -2088,7 +2190,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus); + snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n", + mSampleRate, mSpeed, mStatus); result.append(buffer); snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency); result.append(buffer); diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 6d5f1af..ba67b40 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -793,6 +793,16 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags); } +void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch) +{ // do not call from multiple threads without holding lock + AudioTrackPlaybackRate playbackRate; + if (mPlaybackRateObserver.poll(playbackRate)) { + mPlaybackRate = playbackRate; + } + *speed = mPlaybackRate.mSpeed; + *pitch = mPlaybackRate.mPitch; +} + // --------------------------------------------------------------------------- StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp index 23308c1..9246a7c 100644 --- a/media/libmedia/ICrypto.cpp +++ b/media/libmedia/ICrypto.cpp @@ -35,6 +35,7 @@ enum { REQUIRES_SECURE_COMPONENT, DECRYPT, NOTIFY_RESOLUTION, + SET_MEDIADRM_SESSION, }; struct BpCrypto : public BpInterface<ICrypto> { @@ -161,7 +162,28 @@ struct BpCrypto : public BpInterface<ICrypto> { remote()->transact(NOTIFY_RESOLUTION, data, &reply); } + virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + + writeVector(data, sessionId); + remote()->transact(SET_MEDIADRM_SESSION, data, &reply); + + return reply.readInt32(); + } + private: + void readVector(Parcel &reply, Vector<uint8_t> &vector) const { + uint32_t size = reply.readInt32(); + vector.insertAt((size_t)0, size); + reply.read(vector.editArray(), size); + } + + void writeVector(Parcel &data, Vector<uint8_t> const &vector) const { + data.writeInt32(vector.size()); + data.write(vector.array(), vector.size()); + } + DISALLOW_EVIL_CONSTRUCTORS(BpCrypto); }; @@ -169,6 +191,17 @@ IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto"); //////////////////////////////////////////////////////////////////////////////// +void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const { + uint32_t size = data.readInt32(); + vector.insertAt((size_t)0, size); + data.read(vector.editArray(), size); +} + +void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const { + reply->writeInt32(vector.size()); + reply->write(vector.array(), vector.size()); +} + status_t BnCrypto::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { @@ -317,6 +350,15 @@ status_t BnCrypto::onTransact( return OK; } + case SET_MEDIADRM_SESSION: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector<uint8_t> sessionId; + readVector(data, sessionId); + reply->writeInt32(setMediaDrmSession(sessionId)); + return OK; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp index e768772..f639193 100644 --- a/media/libmediaplayerservice/Crypto.cpp +++ b/media/libmediaplayerservice/Crypto.cpp @@ -268,4 +268,14 @@ void Crypto::notifyResolution(uint32_t width, uint32_t height) { } } +status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) { + Mutex::Autolock autoLock(mLock); + + status_t result = NO_INIT; + if (mInitCheck == OK && mPlugin != NULL) { + result = mPlugin->setMediaDrmSession(sessionId); + } + return result; +} + } // namespace android diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h index d5f3c50..99ea95d 100644 --- a/media/libmediaplayerservice/Crypto.h +++ b/media/libmediaplayerservice/Crypto.h @@ -47,6 +47,8 @@ struct Crypto : public BnCrypto { virtual void notifyResolution(uint32_t width, uint32_t height); + virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId); + virtual ssize_t decrypt( bool secure, const uint8_t key[16], diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 04ac699..3fff1e6 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -82,25 +82,69 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatCodecNotify: { - if (!isStaleReply(msg)) { - int32_t numInput, numOutput; + if (mPaused) { + break; + } + + int32_t cbID; + CHECK(msg->findInt32("callbackID", &cbID)); + + ALOGV("kWhatCodecNotify: cbID = %d", cbID); + switch (cbID) { + case MediaCodec::CB_INPUT_AVAILABLE: + { + int32_t index; + CHECK(msg->findInt32("index", &index)); - if (!msg->findInt32("input-buffers", &numInput)) { - numInput = INT32_MAX; + handleAnInputBuffer(index); + break; + } + + case MediaCodec::CB_OUTPUT_AVAILABLE: + { + int32_t index; + size_t offset; + size_t size; + int64_t timeUs; + int32_t flags; + + CHECK(msg->findInt32("index", &index)); + CHECK(msg->findSize("offset", &offset)); + CHECK(msg->findSize("size", &size)); + CHECK(msg->findInt64("timeUs", &timeUs)); + CHECK(msg->findInt32("flags", &flags)); + + handleAnOutputBuffer(index, offset, size, timeUs, flags); + break; } - if (!msg->findInt32("output-buffers", &numOutput)) { - numOutput = INT32_MAX; + case MediaCodec::CB_OUTPUT_FORMAT_CHANGED: + { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + + handleOutputFormatChange(format); + break; } - if (!mPaused) { - while (numInput-- > 0 && handleAnInputBuffer()) {} + case MediaCodec::CB_ERROR: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + ALOGE("Decoder (%s) reported error : 0x%x", + mIsAudio ? "audio" : "video", err); + + handleError(err); + break; } - while (numOutput-- > 0 && handleAnOutputBuffer()) {} + default: + { + TRESPASS(); + break; + } } - requestCodecNotification(); break; } @@ -188,6 +232,9 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); + sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); + mCodec->setCallback(reply); + err = mCodec->start(); if (err != OK) { ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err); @@ -197,18 +244,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { return; } - // 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(), - mInputBuffers.size(), - mOutputBuffers.size()); - if (mRenderer != NULL) { - requestCodecNotification(); - } mPaused = false; mResumePending = false; } @@ -217,16 +254,14 @@ void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) { bool hadNoRenderer = (mRenderer == NULL); mRenderer = renderer; if (hadNoRenderer && mRenderer != NULL) { - requestCodecNotification(); + // this means that the widevine legacy source is ready + onRequestInputBuffers(); } } void NuPlayer::Decoder::onGetInputBuffers( Vector<sp<ABuffer> > *dstBuffers) { - dstBuffers->clear(); - for (size_t i = 0; i < mInputBuffers.size(); i++) { - dstBuffers->push(mInputBuffers[i]); - } + CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers)); } void NuPlayer::Decoder::onResume(bool notifyComplete) { @@ -235,6 +270,7 @@ void NuPlayer::Decoder::onResume(bool notifyComplete) { if (notifyComplete) { mResumePending = true; } + mCodec->start(); } void NuPlayer::Decoder::doFlush(bool notifyComplete) { @@ -261,8 +297,10 @@ void NuPlayer::Decoder::doFlush(bool notifyComplete) { // we attempt to release the buffers even if flush fails. } releaseAndResetMediaBuffers(); + mPaused = true; } + void NuPlayer::Decoder::onFlush() { doFlush(true); @@ -276,7 +314,6 @@ void NuPlayer::Decoder::onFlush() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); - mPaused = true; } void NuPlayer::Decoder::onShutdown(bool notifyComplete) { @@ -320,7 +357,9 @@ void NuPlayer::Decoder::onShutdown(bool notifyComplete) { } void NuPlayer::Decoder::doRequestBuffers() { - if (isDiscontinuityPending()) { + // mRenderer is only NULL if we have a legacy widevine source that + // is not yet ready. In this case we must not fetch input. + if (isDiscontinuityPending() || mRenderer == NULL) { return; } status_t err = OK; @@ -347,34 +386,50 @@ void NuPlayer::Decoder::doRequestBuffers() { } } -bool NuPlayer::Decoder::handleAnInputBuffer() { +void NuPlayer::Decoder::handleError(int32_t err) +{ + // We cannot immediately release the codec due to buffers still outstanding + // in the renderer. We signal to the player the error so it can shutdown/release the + // decoder after flushing and increment the generation to discard unnecessary messages. + + ++mBufferGeneration; + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) { if (isDiscontinuityPending()) { return false; } - size_t bufferIx = -1; - status_t res = mCodec->dequeueInputBuffer(&bufferIx); - ALOGV("[%s] dequeued input: %d", - mComponentName.c_str(), res == OK ? (int)bufferIx : res); - if (res != OK) { - if (res != -EAGAIN) { - ALOGE("Failed to dequeue input buffer for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); + + sp<ABuffer> buffer; + mCodec->getInputBuffer(index, &buffer); + + if (index >= mInputBuffers.size()) { + for (size_t i = mInputBuffers.size(); i <= index; ++i) { + mInputBuffers.add(); + mMediaBuffers.add(); + mInputBufferIsDequeued.add(); + mMediaBuffers.editItemAt(i) = NULL; + mInputBufferIsDequeued.editItemAt(i) = false; } - return false; } + mInputBuffers.editItemAt(index) = buffer; - CHECK_LT(bufferIx, mInputBuffers.size()); + //CHECK_LT(bufferIx, mInputBuffers.size()); - if (mMediaBuffers[bufferIx] != NULL) { - mMediaBuffers[bufferIx]->release(); - mMediaBuffers.editItemAt(bufferIx) = NULL; + if (mMediaBuffers[index] != NULL) { + mMediaBuffers[index]->release(); + mMediaBuffers.editItemAt(index) = NULL; } - mInputBufferIsDequeued.editItemAt(bufferIx) = true; + mInputBufferIsDequeued.editItemAt(index) = true; if (!mCSDsToSubmit.isEmpty()) { sp<AMessage> msg = new AMessage(); - msg->setSize("buffer-ix", bufferIx); + msg->setSize("buffer-ix", index); sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0); ALOGI("[%s] resubmitting CSD", mComponentName.c_str()); @@ -392,94 +447,38 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { mPendingInputMessages.erase(mPendingInputMessages.begin()); } - if (!mInputBufferIsDequeued.editItemAt(bufferIx)) { + if (!mInputBufferIsDequeued.editItemAt(index)) { return true; } - mDequeuedInputBuffers.push_back(bufferIx); + mDequeuedInputBuffers.push_back(index); onRequestInputBuffers(); return true; } -bool NuPlayer::Decoder::handleAnOutputBuffer() { - size_t bufferIx = -1; - size_t offset; - size_t size; - int64_t timeUs; - uint32_t flags; - status_t res = mCodec->dequeueOutputBuffer( - &bufferIx, &offset, &size, &timeUs, &flags); - - if (res != OK) { - ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res); - } else { - ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")", - mComponentName.c_str(), (int)bufferIx, timeUs, flags); +bool NuPlayer::Decoder::handleAnOutputBuffer( + size_t index, + size_t offset, + size_t size, + int64_t timeUs, + int32_t flags) { + if (mFormatChangePending) { + return false; } - if (res == INFO_OUTPUT_BUFFERS_CHANGED) { - res = mCodec->getOutputBuffers(&mOutputBuffers); - if (res != OK) { - ALOGE("Failed to get output buffers for %s after INFO event (err=%d)", - mComponentName.c_str(), res); - handleError(res); - return false; - } - // NuPlayer ignores this - return true; - } else if (res == INFO_FORMAT_CHANGED) { - sp<AMessage> format = new AMessage(); - res = mCodec->getOutputFormat(&format); - if (res != OK) { - ALOGE("Failed to get output format for %s after INFO event (err=%d)", - mComponentName.c_str(), res); - handleError(res); - return false; - } - - if (!mIsAudio) { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatVideoSizeChanged); - notify->setMessage("format", format); - notify->post(); - } else if (mRenderer != NULL) { - uint32_t flags; - int64_t durationUs; - bool hasVideo = (mSource->getFormat(false /* audio */) != NULL); - if (!hasVideo && - mSource->getDuration(&durationUs) == OK && - durationUs - > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } else { - flags = AUDIO_OUTPUT_FLAG_NONE; - } +// CHECK_LT(bufferIx, mOutputBuffers.size()); + sp<ABuffer> buffer; + mCodec->getOutputBuffer(index, &buffer); - res = mRenderer->openAudioSink( - format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */); - if (res != OK) { - ALOGE("Failed to open AudioSink on format change for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); - return false; - } - } - return true; - } else if (res == INFO_DISCONTINUITY) { - // nothing to do - return true; - } else if (res != OK) { - if (res != -EAGAIN) { - ALOGE("Failed to dequeue output buffer for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); + if (index >= mOutputBuffers.size()) { + for (size_t i = mOutputBuffers.size(); i <= index; ++i) { + mOutputBuffers.add(); } - return false; } - CHECK_LT(bufferIx, mOutputBuffers.size()); - sp<ABuffer> buffer = mOutputBuffers[bufferIx]; + mOutputBuffers.editItemAt(index) = buffer; + buffer->setRange(offset, size); buffer->meta()->clear(); buffer->meta()->setInt64("timeUs", timeUs); @@ -488,7 +487,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() { // we do not expect CODECCONFIG or SYNCFRAME for decoder sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this); - reply->setSize("buffer-ix", bufferIx); + reply->setSize("buffer-ix", index); reply->setInt32("generation", mBufferGeneration); if (eos) { @@ -522,6 +521,29 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() { return true; } +void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) { + if (!mIsAudio) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatVideoSizeChanged); + notify->setMessage("format", format); + notify->post(); + } else if (mRenderer != NULL) { + uint32_t flags; + int64_t durationUs; + bool hasVideo = (mSource->getFormat(false /* audio */) != NULL); + if (!hasVideo && + mSource->getDuration(&durationUs) == OK && + durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } else { + flags = AUDIO_OUTPUT_FLAG_NONE; + } + + mRenderer->openAudioSink( + format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */); + } +} + void NuPlayer::Decoder::releaseAndResetMediaBuffers() { for (size_t i = 0; i < mMediaBuffers.size(); i++) { if (mMediaBuffers[i] != NULL) { @@ -825,7 +847,8 @@ void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) { mPaused = true; } else if (mTimeChangePending) { if (flushOnTimeChange) { - doFlush(false /*notifyComplete*/); + doFlush(false /* notifyComplete */); + signalResume(false /* notifyComplete */); } // restart fetching input diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 4aab2c6..0c0e90c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -87,8 +87,15 @@ private: bool mResumePending; AString mComponentName; - bool handleAnInputBuffer(); - bool handleAnOutputBuffer(); + void handleError(int32_t err); + bool handleAnInputBuffer(size_t index); + bool handleAnOutputBuffer( + size_t index, + size_t offset, + size_t size, + int64_t timeUs, + int32_t flags); + void handleOutputFormatChange(const sp<AMessage> &format); void releaseAndResetMediaBuffers(); void requestCodecNotification(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index a2ec51c..827bdc1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -312,6 +312,9 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { int64_t delayUs = mAudioSink->msecsPerFrame() * numFramesPendingPlayout * 1000ll; + if (mPlaybackRate > 1.0f) { + delayUs /= mPlaybackRate; + } // Let's give it more data after about half that time // has elapsed. diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 97f3e20..45f6339 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1685,6 +1685,16 @@ status_t ACodec::configureCodec( err = setPriority(priority); } + int32_t rateInt = -1; + float rateFloat = -1; + if (!msg->findFloat("operating-rate", &rateFloat)) { + msg->findInt32("operating-rate", &rateInt); + rateFloat = (float)rateInt; // 16MHz (FLINTMAX) is OK for upper bound. + } + if (rateFloat > 0) { + err = setOperatingRate(rateFloat, video); + } + mBaseOutputFormat = outputFormat; CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK); @@ -1711,6 +1721,34 @@ status_t ACodec::setPriority(int32_t priority) { return OK; } +status_t ACodec::setOperatingRate(float rateFloat, bool isVideo) { + if (rateFloat < 0) { + return BAD_VALUE; + } + OMX_U32 rate; + if (isVideo) { + if (rateFloat > 65535) { + return BAD_VALUE; + } + rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f); + } else { + if (rateFloat > UINT_MAX) { + return BAD_VALUE; + } + rate = (OMX_U32)(rateFloat); + } + OMX_PARAM_U32TYPE config; + InitOMXParams(&config); + config.nU32 = rate; + status_t err = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigOperatingRate, + &config, sizeof(config)); + if (err != OK) { + ALOGI("codec does not support config operating rate (err %d)", err); + } + return OK; +} + status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -4902,6 +4940,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { sp<CodecObserver> observer = new CodecObserver; IOMX::node_id node = NULL; + status_t err = OMX_ErrorComponentNotFound; for (size_t matchIndex = 0; matchIndex < matchingCodecs.size(); ++matchIndex) { componentName = matchingCodecs.itemAt(matchIndex).mName.string(); @@ -4910,7 +4949,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { pid_t tid = gettid(); int prevPriority = androidGetThreadPriority(tid); androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND); - status_t err = omx->allocateNode(componentName.c_str(), observer, &node); + err = omx->allocateNode(componentName.c_str(), observer, &node); androidSetThreadPriority(tid, prevPriority); if (err == OK) { @@ -4924,13 +4963,13 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { if (node == NULL) { if (!mime.empty()) { - ALOGE("Unable to instantiate a %scoder for type '%s'.", - encoder ? "en" : "de", mime.c_str()); + ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.", + encoder ? "en" : "de", mime.c_str(), err); } else { - ALOGE("Unable to instantiate codec '%s'.", componentName.c_str()); + ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err); } - mCodec->signalError(OMX_ErrorComponentNotFound); + mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err)); return false; } diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 55548f8..5538cb0 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -610,6 +610,16 @@ status_t MediaCodec::getName(AString *name) const { return OK; } +status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetBuffers, this); + msg->setInt32("portIndex", kPortIndexInput); + msg->setPointer("buffers", buffers); + msg->setInt32("widevine", true); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { sp<AMessage> msg = new AMessage(kWhatGetBuffers, this); msg->setInt32("portIndex", kPortIndexInput); @@ -1666,8 +1676,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { { sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + // Unfortunately widevine legacy source requires knowing all of the + // codec input buffers, so we have to provide them even in async mode. + int32_t widevine = 0; + msg->findInt32("widevine", &widevine); - if (!isExecuting() || (mFlags & kFlagIsAsync)) { + if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) { PostReplyWithError(replyID, INVALID_OPERATION); break; } else if (mFlags & kFlagStickyError) { diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index b6fa810..6568d25 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -399,6 +399,9 @@ status_t MediaCodecSource::initEncoder() { ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str()); + mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector); + mEncoder->setCallback(mEncoderActivityNotify); + status_t err = mEncoder->configure( mOutputFormat, NULL /* nativeWindow */, @@ -422,9 +425,6 @@ status_t MediaCodecSource::initEncoder() { } } - mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector); - mEncoder->setCallback(mEncoderActivityNotify); - err = mEncoder->start(); if (err != OK) { diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 26f8da1..74f58e9 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1455,6 +1455,10 @@ void LiveSession::changeConfiguration( if (bandwidthIndex >= 0) { mOrigBandwidthIndex = mCurBandwidthIndex; mCurBandwidthIndex = bandwidthIndex; + if (mOrigBandwidthIndex != mCurBandwidthIndex) { + ALOGI("#### Starting Bandwidth Switch: %zd => %zd", + mOrigBandwidthIndex, mCurBandwidthIndex); + } } CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size()); const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex); @@ -1574,6 +1578,7 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { if (timeUs >= 0) { mLastSeekTimeUs = timeUs; + mLastDequeuedTimeUs = timeUs; for (size_t i = 0; i < mPacketSources.size(); i++) { mPacketSources.editValueAt(i)->clear(); @@ -1626,8 +1631,10 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { ALOGV("stream %zu changed: oldURI %s, newURI %s", i, mStreams[i].mUri.c_str(), URIs[i].c_str()); sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i)); - source->queueDiscontinuity( - ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + if (source->getLatestDequeuedMeta() != NULL) { + source->queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + } } // Determine which decoders to shutdown on the player side, // a decoder has to be shutdown if its streamtype was active @@ -1687,10 +1694,6 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { // and resume audio. mSwapMask = mNewStreamMask & mStreamMask & ~resumeMask; switching = (mSwapMask != 0); - if (!switching) { - ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd", - mOrigBandwidthIndex, mCurBandwidthIndex); - } } mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs; } else { @@ -1843,7 +1846,11 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { mSwitchInProgress = true; } else { mStreamMask = mNewStreamMask; - mOrigBandwidthIndex = mCurBandwidthIndex; + if (mOrigBandwidthIndex != mCurBandwidthIndex) { + ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd", + mOrigBandwidthIndex, mCurBandwidthIndex); + mOrigBandwidthIndex = mCurBandwidthIndex; + } } ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x", @@ -1970,11 +1977,19 @@ void LiveSession::onPollBuffering() { bool underflow, ready, down, up; if (checkBuffering(underflow, ready, down, up)) { - if (mInPreparationPhase && ready) { - postPrepared(OK); + if (mInPreparationPhase) { + // Allow down switch even if we're still preparing. + // + // Some streams have a high bandwidth index as default, + // when bandwidth is low, it takes a long time to buffer + // to ready mark, then it immediately pauses after start + // as we have to do a down switch. It's better experience + // to restart from a lower index, if we detect low bw. + if (!switchBandwidthIfNeeded(false /* up */, down) && ready) { + postPrepared(OK); + } } - // don't switch before we report prepared if (!mInPreparationPhase) { if (ready) { stopBufferingIfNecessary(); @@ -1982,8 +1997,7 @@ void LiveSession::onPollBuffering() { startBufferingIfNecessary(); } switchBandwidthIfNeeded(up, down); - } - + } } schedulePollBuffering(); @@ -2075,7 +2089,8 @@ bool LiveSession::checkBuffering( if (mPacketSources[i]->isFinished(0 /* duration */)) { percent = 100; } else { - percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs); + percent = (int32_t)(100.0 * + (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs); } if (minBufferPercent < 0 || percent < minBufferPercent) { minBufferPercent = percent; @@ -2158,10 +2173,14 @@ void LiveSession::notifyBufferingUpdate(int32_t percentage) { notify->post(); } -void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { +/* + * returns true if a bandwidth switch is actually needed (and started), + * returns false otherwise + */ +bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // no need to check bandwidth if we only have 1 bandwidth settings if (mSwitchInProgress || mBandwidthItems.size() < 2) { - return; + return false; } int32_t bandwidthBps; @@ -2170,7 +2189,7 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { mLastBandwidthBps = bandwidthBps; } else { ALOGV("no bandwidth estimate."); - return; + return false; } int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth; @@ -2189,16 +2208,16 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70% // of measured bw. In that case we don't want to do anything, since we have // both enough buffer and enough bw. - if (bandwidthIndex == mCurBandwidthIndex - || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex) - || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) { - return; + if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex) + || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) { + // if not yet prepared, just restart again with new bw index. + // this is faster and playback experience is cleaner. + changeConfiguration( + mInPreparationPhase ? 0 : -1ll, bandwidthIndex); + return true; } - - ALOGI("#### Starting Bandwidth Switch: %zd => %zd", - mCurBandwidthIndex, bandwidthIndex); - changeConfiguration(-1, bandwidthIndex, false); } + return false; } void LiveSession::postError(status_t err) { diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index c587f40..9117bb1 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -292,7 +292,7 @@ private: bool checkSwitchProgress( sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil); - void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); + bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); void schedulePollBuffering(); void cancelPollBuffering(); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 0676a33..c7912c0 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -355,10 +355,15 @@ int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) { int64_t time2 = -1; int64_t durationUs = 0; - List<sp<ABuffer> >::iterator it = mBuffers.begin(); - while (it != mBuffers.end()) { + List<sp<ABuffer> >::iterator it; + for (it = mBuffers.begin(); it != mBuffers.end(); it++) { const sp<ABuffer> &buffer = *it; + int32_t discard; + if (buffer->meta()->findInt32("discard", &discard) && discard) { + continue; + } + int64_t timeUs; if (buffer->meta()->findInt64("timeUs", &timeUs)) { if (time1 < 0 || timeUs < time1) { @@ -373,8 +378,6 @@ int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) { durationUs += time2 - time1; time1 = time2 = -1; } - - ++it; } return durationUs + (time2 - time1); @@ -393,11 +396,19 @@ int64_t AnotherPacketSource::getEstimatedDurationUs() { return getBufferedDurationUs_l(&finalResult); } - List<sp<ABuffer> >::iterator it = mBuffers.begin(); - sp<ABuffer> buffer = *it; + sp<ABuffer> buffer; + int32_t discard; + int64_t startTimeUs = -1ll; + List<sp<ABuffer> >::iterator it; + for (it = mBuffers.begin(); it != mBuffers.end(); it++) { + buffer = *it; + if (buffer->meta()->findInt32("discard", &discard) && discard) { + continue; + } + buffer->meta()->findInt64("timeUs", &startTimeUs); + break; + } - int64_t startTimeUs; - buffer->meta()->findInt64("timeUs", &startTimeUs); if (startTimeUs < 0) { return 0; } |