// ignore 0-sized buffer which could be EOS marker with no data if (entry->mOffset == 0 && entry->mBuffer->size() > 0) { int64_t mediaTimeUs; CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); ALOGV("onDrainAudioQueue: rendering audio at media time %.2f secs", mediaTimeUs / 1E6); onNewAudioMediaTime(mediaTimeUs); }
if (written != (ssize_t)copy) { // A short count was received from AudioSink::write() // // AudioSink write is called in non-blocking mode. // It may return with a short count when: // // 1) Size to be copied is not a multiple of the frame size. Fractional frames are // discarded. // 2) The data to be copied exceeds the available buffer in AudioSink. // 3) An error occurs and data has been partially copied to the buffer in AudioSink. // 4) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded.
// (Case 1) // Must be a multiple of the frame size. If it is not a multiple of a frame size, it // needs to fail, as we should not carry over fractional frames between calls. CHECK_EQ(copy % mAudioSink->frameSize(), 0);
// (Case 2, 3, 4) // Return early to the caller. // Beware of calling immediately again as this may busy-loop if you are not careful. ALOGV("AudioSink write short frame count %zd < %zu", written, copy); break; } }
// calculate whether we need to reschedule another write. bool reschedule = !mAudioQueue.empty() && (!mPaused || prevFramesWritten != mNumFramesWritten); // permit pause to fill buffers //ALOGD("reschedule:%d empty:%d mPaused:%d prevFramesWritten:%u mNumFramesWritten:%u", // reschedule, mAudioQueue.empty(), mPaused, prevFramesWritten, mNumFramesWritten); return reschedule; }
// Heuristics to handle situation when media time changed without a // discontinuity. If we have not drained an audio buffer that was // received after this buffer, repost in 10 msec. Otherwise repost // in 500 msec. delayUs = realTimeUs - nowUs; int64_t postDelayUs = -1; if (delayUs > 500000) { postDelayUs = 500000; if (mHasAudio && (mLastAudioBufferDrained - entry.mBufferOrdinal) <= 0) { postDelayUs = 10000; } } elseif (needRepostDrainVideoQueue) { // CHECK(mPlaybackRate > 0); // CHECK(mAudioFirstAnchorTimeMediaUs >= 0); // CHECK(mediaTimeUs - mAudioFirstAnchorTimeMediaUs >= 0); postDelayUs = mediaTimeUs - mAudioFirstAnchorTimeMediaUs; postDelayUs /= mPlaybackRate; }
if (postDelayUs >= 0) { msg->setWhat(kWhatPostDrainVideoQueue); msg->post(postDelayUs); mVideoScheduler->restart(); ALOGI("possible video time jump of %dms or uninitialized media clock, retrying in %dms", (int)(delayUs / 1000), (int)(postDelayUs / 1000)); mDrainVideoQueuePending = true; return; } }
if (tooLate) { ALOGV("video late by %lld us (%.2f secs)", (longlong)mVideoLateByUs, mVideoLateByUs / 1E6); } else { int64_t mediaUs = 0; mMediaClock->getMediaTime(realTimeUs, &mediaUs); ALOGV("rendering video at media time %.2f secs", (mFlags & FLAG_REAL_TIME ? realTimeUs : mediaUs) / 1E6);
if (!(mFlags & FLAG_REAL_TIME) && mLastAudioMediaTimeUs != -1 && mediaTimeUs > mLastAudioMediaTimeUs) { // If audio ends before video, video continues to drive media clock. // Also smooth out videos >= 10fps. mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); } } } else { setVideoLateByUs(0); if (!mVideoSampleReceived && !mHasAudio) { // This will ensure that the first frame after a flush won't be used as anchor // when renderer is in paused state, because resume can happen any time after seek. Mutex::Autolock autoLock(mLock); clearAnchorTime_l(); } }
// Always render the first video frame while keeping stats on A/V sync. if (!mVideoSampleReceived) { realTimeUs = nowUs; tooLate = false; }
Log: ... 07-0311:32:58.6757415075 V NuPlayerRenderer: rendering video at media time 0.36 secs 07-0311:32:58.6767251139 V AudioFlinger: releaseWakeLock_l() AudioOut_25 07-0311:32:58.6767251139 V AudioFlinger: thread 0xef883380 type 0 TID 1139 going to sleep 07-0311:32:58.6797251131 V AudioFlinger: releaseWakeLock_l() AudioOut_D 07-0311:32:58.6827415075 V NuPlayerRenderer: onDrainAudioQueue: rendering audio at media time 0.70 secs 07-0311:32:58.6837415075 V AudioTrack: frame adjustment:2400 timestamp:BOOTTIME offset 0 07-0311:32:58.6837415075 V AudioTrack: ExtendedTimestamp[0] position: 0 time: -1 07-0311:32:58.6837415075 V AudioTrack: ExtendedTimestamp[1] position: 17280 time: 96476185950 07-0311:32:58.6837415075 V AudioTrack: ExtendedTimestamp[2] position: 0 time: -1 07-0311:32:58.6837415075 V AudioTrack: ExtendedTimestamp[3] position: 0 time: -1 07-0311:32:58.6837415075 V AudioTrack: ExtendedTimestamp[4] position: 0 time: -1 07-0311:32:58.6837415075 V AudioSink: getPlayedOutDurationUs(370130) nowUs(96536315) frames(14880) framesAt(96476185) 07-0311:32:58.6847415075 V NuPlayerRenderer: onDrainAudioQueue: rendering audio at media time 0.73 secs 07-0311:32:58.6947251131 V AudioFlinger: thread 0xf0208880 type 0 TID 1131 going to sleep 07-0311:32:58.7017415075 V NuPlayerRenderer: onDrainAudioQueue: rendering audio at media time 0.75 secs 07-0311:32:58.7177415075 V NuPlayerRenderer: rendering video at media time 0.41 secs 07-0311:32:58.7337415075 V NuPlayerRenderer: onDrainAudioQueue: rendering audio at media time 0.77 secs ...