Merge "In order to recover from video lagging behind audio, drop avc frames"

This commit is contained in:
Andreas Huber
2011-09-19 08:54:59 -07:00
committed by Android (Google) Code Review
8 changed files with 155 additions and 44 deletions

View File

@@ -34,17 +34,21 @@
#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/ACodec.h> #include <media/stagefright/ACodec.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h> #include <media/stagefright/MetaData.h>
#include <surfaceflinger/Surface.h> #include <surfaceflinger/Surface.h>
#include <gui/ISurfaceTexture.h> #include <gui/ISurfaceTexture.h>
#include "avc_utils.h"
namespace android { namespace android {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
NuPlayer::NuPlayer() NuPlayer::NuPlayer()
: mUIDValid(false), : mUIDValid(false),
mVideoIsAVC(false),
mAudioEOS(false), mAudioEOS(false),
mVideoEOS(false), mVideoEOS(false),
mScanSourcesPending(false), mScanSourcesPending(false),
@@ -52,7 +56,12 @@ NuPlayer::NuPlayer()
mFlushingAudio(NONE), mFlushingAudio(NONE),
mFlushingVideo(NONE), mFlushingVideo(NONE),
mResetInProgress(false), mResetInProgress(false),
mResetPostponed(false) { mResetPostponed(false),
mSkipRenderingAudioUntilMediaTimeUs(-1ll),
mSkipRenderingVideoUntilMediaTimeUs(-1ll),
mVideoLateByUs(0ll),
mNumFramesTotal(0ll),
mNumFramesDropped(0ll) {
} }
NuPlayer::~NuPlayer() { NuPlayer::~NuPlayer() {
@@ -185,10 +194,14 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
{ {
LOGV("kWhatStart"); LOGV("kWhatStart");
mVideoIsAVC = false;
mAudioEOS = false; mAudioEOS = false;
mVideoEOS = false; mVideoEOS = false;
mSkipRenderingAudioUntilMediaTimeUs = -1; mSkipRenderingAudioUntilMediaTimeUs = -1;
mSkipRenderingVideoUntilMediaTimeUs = -1; mSkipRenderingVideoUntilMediaTimeUs = -1;
mVideoLateByUs = 0;
mNumFramesTotal = 0;
mNumFramesDropped = 0;
mSource->start(); mSource->start();
@@ -269,6 +282,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
} else { } else {
CHECK(IsFlushingState(mFlushingVideo, &needShutdown)); CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
mFlushingVideo = FLUSHED; mFlushingVideo = FLUSHED;
mVideoLateByUs = 0;
} }
LOGV("decoder %s flush completed", audio ? "audio" : "video"); LOGV("decoder %s flush completed", audio ? "audio" : "video");
@@ -397,13 +412,18 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
int64_t positionUs; int64_t positionUs;
CHECK(msg->findInt64("positionUs", &positionUs)); CHECK(msg->findInt64("positionUs", &positionUs));
CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs));
if (mDriver != NULL) { if (mDriver != NULL) {
sp<NuPlayerDriver> driver = mDriver.promote(); sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) { if (driver != NULL) {
driver->notifyPosition(positionUs); driver->notifyPosition(positionUs);
driver->notifyFrameStats(
mNumFramesTotal, mNumFramesDropped);
} }
} }
} else { } else if (what == Renderer::kWhatFlushComplete) {
CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete); CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
int32_t audio; int32_t audio;
@@ -565,6 +585,12 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
return -EWOULDBLOCK; return -EWOULDBLOCK;
} }
if (!audio) {
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
}
sp<AMessage> notify = sp<AMessage> notify =
new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify, new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
id()); id());
@@ -598,53 +624,70 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
} }
sp<ABuffer> accessUnit; sp<ABuffer> accessUnit;
status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
if (err == -EWOULDBLOCK) { bool dropAccessUnit;
return err; do {
} else if (err != OK) { status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
if (err == INFO_DISCONTINUITY) {
int32_t type;
CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
bool formatChange = if (err == -EWOULDBLOCK) {
type == ATSParser::DISCONTINUITY_FORMATCHANGE; return err;
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
int32_t type;
CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
LOGV("%s discontinuity (formatChange=%d)", bool formatChange =
audio ? "audio" : "video", formatChange); type == ATSParser::DISCONTINUITY_FORMATCHANGE;
if (audio) { LOGV("%s discontinuity (formatChange=%d)",
mSkipRenderingAudioUntilMediaTimeUs = -1; audio ? "audio" : "video", formatChange);
} else {
mSkipRenderingVideoUntilMediaTimeUs = -1;
}
sp<AMessage> extra; if (audio) {
if (accessUnit->meta()->findMessage("extra", &extra) mSkipRenderingAudioUntilMediaTimeUs = -1;
&& extra != NULL) { } else {
int64_t resumeAtMediaTimeUs; mSkipRenderingVideoUntilMediaTimeUs = -1;
if (extra->findInt64( }
"resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
LOGI("suppressing rendering of %s until %lld us",
audio ? "audio" : "video", resumeAtMediaTimeUs);
if (audio) { sp<AMessage> extra;
mSkipRenderingAudioUntilMediaTimeUs = if (accessUnit->meta()->findMessage("extra", &extra)
resumeAtMediaTimeUs; && extra != NULL) {
} else { int64_t resumeAtMediaTimeUs;
mSkipRenderingVideoUntilMediaTimeUs = if (extra->findInt64(
resumeAtMediaTimeUs; "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
LOGI("suppressing rendering of %s until %lld us",
audio ? "audio" : "video", resumeAtMediaTimeUs);
if (audio) {
mSkipRenderingAudioUntilMediaTimeUs =
resumeAtMediaTimeUs;
} else {
mSkipRenderingVideoUntilMediaTimeUs =
resumeAtMediaTimeUs;
}
} }
} }
flushDecoder(audio, formatChange);
} }
flushDecoder(audio, formatChange); reply->setInt32("err", err);
reply->post();
return OK;
} }
reply->setInt32("err", err); if (!audio) {
reply->post(); ++mNumFramesTotal;
return OK; }
}
dropAccessUnit = false;
if (!audio
&& mVideoLateByUs > 100000ll
&& mVideoIsAVC
&& !IsAVCReferenceFrame(accessUnit)) {
dropAccessUnit = true;
++mNumFramesDropped;
}
} while (dropAccessUnit);
// LOGV("returned a valid buffer of %s data", audio ? "audio" : "video"); // LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");

View File

@@ -92,6 +92,7 @@ private:
sp<NativeWindowWrapper> mNativeWindow; sp<NativeWindowWrapper> mNativeWindow;
sp<MediaPlayerBase::AudioSink> mAudioSink; sp<MediaPlayerBase::AudioSink> mAudioSink;
sp<Decoder> mVideoDecoder; sp<Decoder> mVideoDecoder;
bool mVideoIsAVC;
sp<Decoder> mAudioDecoder; sp<Decoder> mAudioDecoder;
sp<Renderer> mRenderer; sp<Renderer> mRenderer;
@@ -119,6 +120,9 @@ private:
int64_t mSkipRenderingAudioUntilMediaTimeUs; int64_t mSkipRenderingAudioUntilMediaTimeUs;
int64_t mSkipRenderingVideoUntilMediaTimeUs; int64_t mSkipRenderingVideoUntilMediaTimeUs;
int64_t mVideoLateByUs;
int64_t mNumFramesTotal, mNumFramesDropped;
status_t instantiateDecoder(bool audio, sp<Decoder> *decoder); status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg); status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);

View File

@@ -31,6 +31,8 @@ NuPlayerDriver::NuPlayerDriver()
: mResetInProgress(false), : mResetInProgress(false),
mDurationUs(-1), mDurationUs(-1),
mPositionUs(-1), mPositionUs(-1),
mNumFramesTotal(0),
mNumFramesDropped(0),
mLooper(new ALooper), mLooper(new ALooper),
mState(UNINITIALIZED), mState(UNINITIALIZED),
mStartupSeekTimeUs(-1) { mStartupSeekTimeUs(-1) {
@@ -292,4 +294,30 @@ void NuPlayerDriver::notifySeekComplete() {
sendEvent(MEDIA_SEEK_COMPLETE); sendEvent(MEDIA_SEEK_COMPLETE);
} }
void NuPlayerDriver::notifyFrameStats(
int64_t numFramesTotal, int64_t numFramesDropped) {
Mutex::Autolock autoLock(mLock);
mNumFramesTotal = numFramesTotal;
mNumFramesDropped = numFramesDropped;
}
status_t NuPlayerDriver::dump(int fd, const Vector<String16> &args) const {
Mutex::Autolock autoLock(mLock);
FILE *out = fdopen(dup(fd), "w");
fprintf(out, " NuPlayer\n");
fprintf(out, " numFramesTotal(%lld), numFramesDropped(%lld), "
"percentageDropped(%.2f)\n",
mNumFramesTotal,
mNumFramesDropped,
mNumFramesTotal == 0
? 0.0 : (double)mNumFramesDropped / mNumFramesTotal);
fclose(out);
out = NULL;
return OK;
}
} // namespace android } // namespace android

View File

@@ -60,16 +60,19 @@ struct NuPlayerDriver : public MediaPlayerInterface {
virtual status_t getMetadata( virtual status_t getMetadata(
const media::Metadata::Filter& ids, Parcel *records); const media::Metadata::Filter& ids, Parcel *records);
virtual status_t dump(int fd, const Vector<String16> &args) const;
void notifyResetComplete(); void notifyResetComplete();
void notifyDuration(int64_t durationUs); void notifyDuration(int64_t durationUs);
void notifyPosition(int64_t positionUs); void notifyPosition(int64_t positionUs);
void notifySeekComplete(); void notifySeekComplete();
void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
protected: protected:
virtual ~NuPlayerDriver(); virtual ~NuPlayerDriver();
private: private:
Mutex mLock; mutable Mutex mLock;
Condition mCondition; Condition mCondition;
// The following are protected through "mLock" // The following are protected through "mLock"
@@ -77,6 +80,8 @@ private:
bool mResetInProgress; bool mResetInProgress;
int64_t mDurationUs; int64_t mDurationUs;
int64_t mPositionUs; int64_t mPositionUs;
int64_t mNumFramesTotal;
int64_t mNumFramesDropped;
// <<< // <<<
sp<ALooper> mLooper; sp<ALooper> mLooper;

View File

@@ -47,7 +47,8 @@ NuPlayer::Renderer::Renderer(
mHasVideo(false), mHasVideo(false),
mSyncQueues(false), mSyncQueues(false),
mPaused(false), mPaused(false),
mLastPositionUpdateUs(-1ll) { mLastPositionUpdateUs(-1ll),
mVideoLateByUs(0ll) {
} }
NuPlayer::Renderer::~Renderer() { NuPlayer::Renderer::~Renderer() {
@@ -357,22 +358,26 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
mVideoQueue.erase(mVideoQueue.begin()); mVideoQueue.erase(mVideoQueue.begin());
entry = NULL; entry = NULL;
mVideoLateByUs = 0ll;
notifyPosition();
return; return;
} }
#if 0
int64_t mediaTimeUs; int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
int64_t lateByUs = ALooper::GetNowUs() - realTimeUs; mVideoLateByUs = ALooper::GetNowUs() - realTimeUs;
if (lateByUs > 40000) { bool tooLate = (mVideoLateByUs > 40000);
LOGI("video late by %lld us (%.2f secs)", lateByUs, lateByUs / 1E6);
if (tooLate) {
LOGV("video late by %lld us (%.2f secs)", lateByUs, lateByUs / 1E6);
} else { } else {
LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6); LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
} }
#endif
entry->mNotifyConsumed->setInt32("render", true); entry->mNotifyConsumed->setInt32("render", true);
entry->mNotifyConsumed->post(); entry->mNotifyConsumed->post();
@@ -604,6 +609,7 @@ void NuPlayer::Renderer::notifyPosition() {
sp<AMessage> notify = mNotify->dup(); sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatPosition); notify->setInt32("what", kWhatPosition);
notify->setInt64("positionUs", positionUs); notify->setInt64("positionUs", positionUs);
notify->setInt64("videoLateByUs", mVideoLateByUs);
notify->post(); notify->post();
} }

View File

@@ -101,6 +101,7 @@ private:
bool mPaused; bool mPaused;
int64_t mLastPositionUpdateUs; int64_t mLastPositionUpdateUs;
int64_t mVideoLateByUs;
bool onDrainAudioQueue(); bool onDrainAudioQueue();
void postDrainAudioQueue(int64_t delayUs = 0); void postDrainAudioQueue(int64_t delayUs = 0);
@@ -118,6 +119,7 @@ private:
void notifyEOS(bool audio, status_t finalResult); void notifyEOS(bool audio, status_t finalResult);
void notifyFlushComplete(bool audio); void notifyFlushComplete(bool audio);
void notifyPosition(); void notifyPosition();
void notifyVideoLateBy(int64_t lateByUs);
void flushQueue(List<QueueEntry> *queue); void flushQueue(List<QueueEntry> *queue);
bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg); bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);

View File

@@ -329,6 +329,28 @@ bool IsIDR(const sp<ABuffer> &buffer) {
return foundIDR; return foundIDR;
} }
bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit) {
const uint8_t *data = accessUnit->data();
size_t size = accessUnit->size();
const uint8_t *nalStart;
size_t nalSize;
while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
CHECK_GT(nalSize, 0u);
unsigned nalType = nalStart[0] & 0x1f;
if (nalType == 5) {
return true;
} else if (nalType == 1) {
unsigned nal_ref_idc = (nalStart[0] >> 5) & 3;
return nal_ref_idc != 0;
}
}
return true;
}
sp<MetaData> MakeAACCodecSpecificData( sp<MetaData> MakeAACCodecSpecificData(
unsigned profile, unsigned sampling_freq_index, unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration) { unsigned channel_configuration) {

View File

@@ -50,6 +50,7 @@ struct MetaData;
sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit); sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
bool IsIDR(const sp<ABuffer> &accessUnit); bool IsIDR(const sp<ABuffer> &accessUnit);
bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit);
const char *AVCProfileToString(uint8_t profile); const char *AVCProfileToString(uint8_t profile);