1. MediaPlayer::prepare()
………………………………………………………………………………………..
2. AwesomePlayer::prepareAsync()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
status_tAwesomePlayer::prepareAsync() {
ATRACE_CALL();
Mutex::Autolock autoLock(mLock);
if (mFlags & PREPARING) {
return UNKNOWN_ERROR; // async prepare already pending
}
mIsAsyncPrepare = true;
return prepareAsync_l();
}
status_tAwesomePlayer::prepareAsync_l() {
if (mFlags & PREPARING) {
return UNKNOWN_ERROR; // async prepare already pending
}
if (!mQueueStarted) {
mQueue.start();
mQueueStarted = true;
}
modifyFlags(PREPARING, SET);
mAsyncPrepareEvent= new AwesomeEvent(
this,&AwesomePlayer::onPrepareAsyncEvent);
mQueue.postEvent(mAsyncPrepareEvent);
return OK;
}
voidAwesomePlayer::onPrepareAsyncEvent() {
Mutex::Autolock autoLock(mLock);
if (mFlags & PREPARE_CANCELLED) {
ALOGI("prepare was cancelledbefore doing anything");
abortPrepare(UNKNOWN_ERROR);
return;
}
if (mUri.size() > 0) {
status_t err =finishSetDataSource_l();
if (err != OK) {
abortPrepare(err);
return;
}
}
if (mVideoTrack != NULL &&mVideoSource == NULL) {
status_t err =initVideoDecoder();
if (err != OK) {
abortPrepare(err);
return;
}
}
if (mAudioTrack != NULL &&mAudioSource == NULL) {
status_t err =initAudioDecoder();
if (err != OK) {
abortPrepare(err);
return;
}
}
modifyFlags(PREPARING_CONNECTED, SET);
if (isStreamingHTTP()) {
postBufferingEvent_l();
} else {
finishAsyncPrepare_l();
}
}
3. AwesomePlayer::finishSetDataSource_l()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
status_tAwesomePlayer::finishSetDataSource_l() {
ATRACE_CALL();
sp<DataSource> dataSource;
bool isWidevineStreaming = false;
if (!strncasecmp("widevine://",mUri.string(), 11)) {
isWidevineStreaming = true;
String8 newURI =String8("http://");
newURI.append(mUri.string() + 11);
mUri = newURI;
}
AStringsniffedMIME;
if (!strncasecmp("http://",mUri.string(), 7)
||!strncasecmp("https://", mUri.string(), 8)
|| isWidevineStreaming) {
mConnectingDataSource =HTTPBase::Create(
(mFlags & INCOGNITO)
? HTTPBase::kFlagIncognito
: 0);
if (mUIDValid) {
mConnectingDataSource->setUID(mUID);
}
String8 cacheConfig;
bool disconnectAtHighwatermark;
NuCachedSource2::RemoveCacheSpecificHeaders(
&mUriHeaders,&cacheConfig, &disconnectAtHighwatermark);
mLock.unlock();
status_t err =mConnectingDataSource->connect(mUri, &mUriHeaders);
mLock.lock();
if (err != OK) {
mConnectingDataSource.clear();
ALOGI("mConnectingDataSource->connect() returned %d", err);
return err;
}
if (!isWidevineStreaming) {
// The widevine extractor does itsown caching.
#if 0
mCachedSource = newNuCachedSource2(
new ThrottledSource(
mConnectingDataSource,50 * 1024 /* bytes/sec */));
#else
mCachedSource = new NuCachedSource2(
mConnectingDataSource,
cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
disconnectAtHighwatermark);
#endif
dataSource = mCachedSource;
} else {
dataSource = mConnectingDataSource;
}
mConnectingDataSource.clear();
String8contentType = dataSource->getMIMEType();
if (strncasecmp(contentType.string(),"audio/", 6)) {
// We're not doing this for streamsthat appear to be audio-only
// streams to ensure that even lowbandwidth streams start
// playing back fairly instantly.
// We're going to prefill the cachebefore trying to instantiate
// the extractor below, as thelatter is an operation that otherwise
// could block on the datasourcefor a significant amount of time.
// During that time we'd be unableto abort the preparation phase
// without this prefill.
if (mCachedSource != NULL) {
// We're going to prefill thecache before trying to instantiate
// the extractor below, as thelatter is an operation that otherwise
// could block on thedatasource for a significant amount of time.
// During that time we'd beunable to abort the preparation phase
// without this prefill.
mLock.unlock();
// Initially make sure we haveat least 192 KB for the sniff
// to complete withoutblocking.
static const size_tkMinBytesForSniffing = 192 * 1024;
off64_t metaDataSize = -1ll;
for (;;) {
status_t finalStatus;
size_t cachedDataRemaining=
mCachedSource->approxDataRemaining(&finalStatus);
if (finalStatus != OK
|| (metaDataSize>= 0
&&cachedDataRemaining >= metaDataSize)
|| (mFlags &PREPARE_CANCELLED)) {
break;
}
ALOGV("now cached %dbytes of data", cachedDataRemaining);
if (metaDataSize < 0
&&cachedDataRemaining >= kMinBytesForSniffing) {
String8 tmp;
float confidence;
sp<AMessage>meta;
if (!dataSource->sniff(&tmp, &confidence, &meta)) {
mLock.lock();
returnUNKNOWN_ERROR;
}
// We successfullyidentified the file's extractor to
// be, remember thismime type so we don't have to
// sniff it again whenwe call MediaExtractor::Create()
// below.
sniffedMIME = tmp.string();
if (meta == NULL
||!meta->findInt64(
"meta-data-size", &metaDataSize)) {
metaDataSize =kHighWaterMarkBytes;
}
CHECK_GE(metaDataSize, 0ll);
ALOGV("metaDataSize = %lld bytes", metaDataSize);
}
usleep(200000);
}
mLock.lock();
}
if (mFlags & PREPARE_CANCELLED){
ALOGI("Prepare cancelledwhile waiting for initial cache fill.");
return UNKNOWN_ERROR;
}
}
} else {
dataSource =DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
}
if (dataSource == NULL) {
return UNKNOWN_ERROR;
}
sp<MediaExtractor> extractor;
if (isWidevineStreaming) {
String8 mimeType;
float confidence;
sp<AMessage> dummy;
bool success;
// SniffWVM is potentially blockingsince it may require network access.
// Do not call it with mLock held.
mLock.unlock();
success =SniffWVM(dataSource, &mimeType, &confidence, &dummy);
mLock.lock();
if (!success
|| strcasecmp(
mimeType.string(),MEDIA_MIMETYPE_CONTAINER_WVM)) {
return ERROR_UNSUPPORTED;
}
mWVMExtractor =new WVMExtractor(dataSource);
mWVMExtractor->setAdaptiveStreamingMode(true);
if (mUIDValid)
mWVMExtractor->setUID(mUID);
extractor = mWVMExtractor;
} else {
extractor =MediaExtractor::Create(
dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());
if (extractor == NULL) {
return UNKNOWN_ERROR;
}
}
if (extractor->getDrmFlag()) {
checkDrmStatus(dataSource);
}
status_t err =setDataSource_l(extractor);
if (err != OK) {
mWVMExtractor.clear();
return err;
}
return OK;
}
Frameworks/av/media/libstagefright/MediaExtract.cpp
// static
sp<MediaExtractor>MediaExtractor::Create(
const sp<DataSource> &source,const char *mime) {
sp<AMessage> meta;
String8 tmp;
if (mime == NULL) {
float confidence;
if(!source->sniff(&tmp, &confidence, &meta)) {
ALOGV("FAILED to autodetectmedia content.");
return NULL;
}
mime =tmp.string();
ALOGV("Autodetected media contentas '%s' with confidence %.2f",
mime, confidence);
}
bool isDrm = false;
// DRM MIME type syntax is"drm+type+original" where
// type is "es_based" or"container_based" and
// original is the content's cleartext MIMEtype
if (!strncmp(mime, "drm+", 4)) {
const char *originalMime =strchr(mime+4, '+');
if (originalMime == NULL) {
// second + not found
return NULL;
}
++originalMime;
if (!strncmp(mime,"drm+es_based+", 13)) {
// DRMExtractor sets containermetadata kKeyIsDRM to 1
return new DRMExtractor(source,originalMime);
} else if (!strncmp(mime,"drm+container_based+", 20)) {
mime = originalMime;
isDrm = true;
} else {
return NULL;
}
}
MediaExtractor *ret = NULL;
if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime,"audio/mp4")) {
ret = newMPEG4Extractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_MPEG)) {
ret = new MP3Extractor(source, meta);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AMR_NB)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)){
ret = new AMRExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_FLAC)) {
ret = new FLACExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_WAV)) {
ret = new WAVExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_OGG)) {
ret = new OggExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
ret = new MatroskaExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
ret = new MPEG2TSExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_WVM)) {
// Return now. WVExtractor should not have the DrmFlag setin the block below.
return new WVMExtractor(source);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
ret = new AACExtractor(source, meta);
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
ret = newMPEG2PSExtractor(source);
}
if (ret != NULL) {
if (isDrm) {
ret->setDrmFlag(true);
} else {
ret->setDrmFlag(false);
}
}
return ret;
}
status_tAwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
// Attempt to approximate overall streambitrate by summing all
// tracks' individual bitrates, if not allof them advertise bitrate,
// we have to fail.
int64_t totalBitRate = 0;
mExtractor =extractor;
for (size_t i = 0; i <extractor->countTracks(); ++i) {
sp<MetaData> meta =extractor->getTrackMetaData(i);
int32_t bitrate;
if (!meta->findInt32(kKeyBitRate,&bitrate)) {
const char *mime;
CHECK(meta->findCString(kKeyMIMEType,&mime));
ALOGV("track of type '%s' doesnot publish bitrate", mime);
totalBitRate = -1;
break;
}
totalBitRate += bitrate;
}
mBitrate =totalBitRate;
ALOGV("mBitrate = %lld bits/sec",mBitrate);
{
Mutex::Autolock autoLock(mStatsLock);
mStats.mBitrate = mBitrate;
mStats.mTracks.clear();
mStats.mAudioTrackIndex = -1;
mStats.mVideoTrackIndex = -1;
}
bool haveAudio = false;
boolhaveVideo = false;
for (size_t i = 0; i <extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
const char *_mime;
CHECK(meta->findCString(kKeyMIMEType, &_mime));
String8 mime = String8(_mime);
if (!haveVideo &&!strncasecmp(mime.string(), "video/", 6)) {
setVideoSource(extractor->getTrack(i));
haveVideo = true;
// Set the presentation/displaysize
int32_t displayWidth, displayHeight;
bool success =meta->findInt32(kKeyDisplayWidth, &displayWidth);
if (success) {
success =meta->findInt32(kKeyDisplayHeight, &displayHeight);
}
if (success) {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
}
{
Mutex::AutolockautoLock(mStatsLock);
mStats.mVideoTrackIndex =mStats.mTracks.size();
mStats.mTracks.push();
TrackStat *stat =
&mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
stat->mMIME = mime.string();
}
} else if (!haveAudio &&!strncasecmp(mime.string(), "audio/", 6)) {
setAudioSource(extractor->getTrack(i));
haveAudio = true;
mActiveAudioTrackIndex = i;
{
Mutex::AutolockautoLock(mStatsLock);
mStats.mAudioTrackIndex =mStats.mTracks.size();
mStats.mTracks.push();
TrackStat *stat =
&mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
stat->mMIME = mime.string();
}
if (!strcasecmp(mime.string(),MEDIA_MIMETYPE_AUDIO_VORBIS)) {
// Only do this for vorbisaudio, none of the other audio
// formats even support thisringtone specific hack and
// retrieving the metadata onsome extractors may turn out
// to be very expensive.
sp<MetaData> fileMeta =extractor->getMetaData();
int32_t loop;
if (fileMeta != NULL
&&fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
modifyFlags(AUTO_LOOPING, SET);
}
}
} else if (!strcasecmp(mime.string(),MEDIA_MIMETYPE_TEXT_3GPP)) {
addTextSource_l(i,extractor->getTrack(i));
}
}
if (!haveAudio && !haveVideo) {
if (mWVMExtractor != NULL) {
returnmWVMExtractor->getError();
} else {
return UNKNOWN_ERROR;
}
}
mExtractorFlags = extractor->flags();
return OK;
}
4. AwesomePlayer::initVideoDecoder ()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
status_tAwesomePlayer::initVideoDecoder(uint32_t flags) {
ATRACE_CALL();
#ifdefDEBUG_HDCP
// For debugging, we allow a systemproperty to control the protected usage.
// In case of uninitialized or unexpectedproperty, we default to "DRM only".
bool setProtectionBit = false;
char value[PROPERTY_VALUE_MAX];
if(property_get("persist.sys.hdcp_checking", value, NULL)) {
if (!strcmp(value, "never")){
// nop
} else if (!strcmp(value,"always")) {
setProtectionBit = true;
} else if (!strcmp(value,"drm-only")) {
if (mDecryptHandle != NULL) {
setProtectionBit = true;
}
// property value is empty, orunexpected value
} else {
if (mDecryptHandle != NULL) {
setProtectionBit = true;
}
}
// can' read property value
} else {
if (mDecryptHandle != NULL) {
setProtectionBit = true;
}
}
// note that usage bit is already cleared,so no need to clear it in the "else" case
if (setProtectionBit) {
flags |=OMXCodec::kEnableGrallocUsageProtected;
}
#else
if (mDecryptHandle != NULL) {
flags |=OMXCodec::kEnableGrallocUsageProtected;
}
#endif
ALOGV("initVideoDecoderflags=0x%x", flags);
mVideoSource =OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, //createEncoder
mVideoTrack,
NULL,flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
if (mVideoSource != NULL) {
int64_t durationUs;
if(mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
Mutex::AutolockautoLock(mMiscStateLock);
if (mDurationUs < 0 ||durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
status_t err =mVideoSource->start();
if (err != OK) {
ALOGE("failed to start videosource");
mVideoSource.clear();
return err;
}
}
if (mVideoSource != NULL) {
const char *componentName;
CHECK(mVideoSource->getFormat()
->findCString(kKeyDecoderComponent,&componentName));
{
Mutex::AutolockautoLock(mStatsLock);
TrackStat *stat =&mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
stat->mDecoderName =componentName;
}
static const char *kPrefix ="OMX.Nvidia.";
static const char *kSuffix =".decode";
static const size_t kSuffixLength =strlen(kSuffix);
size_t componentNameLength =strlen(componentName);
if (!strncmp(componentName, kPrefix, strlen(kPrefix))
&& componentNameLength>= kSuffixLength
&&!strcmp(&componentName[
componentNameLength -kSuffixLength], kSuffix)) {
modifyFlags(SLOW_DECODER_HACK,SET);
}
}
return mVideoSource != NULL ? OK :UNKNOWN_ERROR;
}
5. AwesomePlayer::initAudioDecoder ()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
status_tAwesomePlayer::initAudioDecoder() {
ATRACE_CALL();
sp<MetaData>meta = mAudioTrack->getFormat();
const char *mime;
CHECK(meta->findCString(kKeyMIMEType,&mime));
if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_RAW)) {
mAudioSource =mAudioTrack;
} else {
mAudioSource =OMXCodec::Create(
mClient.interface(), mAudioTrack->getFormat(),
false,// createEncoder
mAudioTrack);
}
if (mAudioSource != NULL) {
int64_t durationUs;
if(mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
Mutex::Autolock autoLock(mMiscStateLock);
if (mDurationUs < 0 ||durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
status_t err =mAudioSource->start();
if (err != OK) {
mAudioSource.clear();
return err;
}
} else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_QCELP)) {
// For legacy reasons we're simplygoing to ignore the absence
// of an audio decoder for QCELPinstead of aborting playback
// altogether.
return OK;
}
if (mAudioSource != NULL) {
Mutex::Autolock autoLock(mStatsLock);
TrackStat *stat =&mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
const char *component;
if (!mAudioSource->getFormat()
->findCString(kKeyDecoderComponent, &component)) {
component = "none";
}
stat->mDecoderName = component;
}
return mAudioSource != NULL ? OK :UNKNOWN_ERROR;
}
6. AwesomePlayer::postBufferingEvent_l()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
7. AwesomePlayer::finishAsyncPrepare_l ()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
MediaPlayer::start() 作类似的调用过程
1. MediaPlayerService::Client::start()
Frameworks/av/media/libmediaplayerservice/MediaPlayerService.h
Frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
status_tMediaPlayerService::Client::start()
{
ALOGV("[%d] start", mConnId);
// p 是一个 StageFrightPlayer对象
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
p->setLooping(mLoop);
// 调用StageFrightPlayer对象的start()
returnp->start();
}
2. StagefrightPlayer::start ()
Frameworks/av/media/libmediaplayerservice/StageFrightPlayer.cpp
status_tStagefrightPlayer::start() {
ALOGV("start");
// 调用AwesomePlayer的play()
returnmPlayer->play();
}
3. AwesomePlayer::play ()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
status_tAwesomePlayer::play() {
ATRACE_CALL();
Mutex::Autolock autoLock(mLock);
modifyFlags(CACHE_UNDERRUN, CLEAR);
return play_l();
}
status_tAwesomePlayer::play_l() {
modifyFlags(SEEK_PREVIEW, CLEAR);
if (mFlags & PLAYING) {
return OK;
}
if (!(mFlags & PREPARED)) {
status_t err = prepare_l();
if (err != OK) {
return err;
}
}
modifyFlags(PLAYING, SET);
modifyFlags(FIRST_FRAME, SET);
if (mDecryptHandle != NULL) {
int64_t position;
getPosition(&position);
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::START, position /1000);
}
if (mAudioSource != NULL) {
if (mAudioPlayer == NULL) {
if (mAudioSink != NULL) {
bool allowDeepBuffering;
int64_t cachedDurationUs;
bool eos;
if (mVideoSource == NULL
&& (mDurationUs> AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US ||
(getCachedDuration_l(&cachedDurationUs,&eos) &&
cachedDurationUs >AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US))) {
allowDeepBuffering = true;
} else {
allowDeepBuffering = false;
}
mAudioPlayer = new AudioPlayer(mAudioSink, allowDeepBuffering, this);
mAudioPlayer->setSource(mAudioSource);
mTimeSource = mAudioPlayer;
// If there was a seek requestbefore we ever started,
// honor the request now.
// Make sure to do this beforestarting the audio player
// to avoid a race condition.
seekAudioIfNecessary_l();
}
}
CHECK(!(mFlags & AUDIO_RUNNING));
if (mVideoSource == NULL) {
// We don't want to post an errornotification at this point,
// the error returned fromMediaPlayer::start() will suffice.
status_t err = startAudioPlayer_l(
false /*sendErrorNotification */);
if (err != OK) {
delete mAudioPlayer;
mAudioPlayer = NULL;
modifyFlags((PLAYING |FIRST_FRAME), CLEAR);
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(
mDecryptHandle,Playback::STOP, 0);
}
return err;
}
}
}
if (mTimeSource == NULL &&mAudioPlayer == NULL) {
mTimeSource = &mSystemTimeSource;
}
if (mVideoSource != NULL) {
// Kick off video playback
postVideoEvent_l();
if (mAudioSource != NULL &&mVideoSource != NULL) {
postVideoLagEvent_l();
}
}
if (mFlags & AT_EOS) {
// Legacy behaviour, if a streamfinishes playing and then
// is started again, we play from thestart...
seekTo_l(0);
}
uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted
|IMediaPlayerService::kBatteryDataTrackDecoder;
if ((mAudioSource != NULL) &&(mAudioSource != mAudioTrack)) {
params |=IMediaPlayerService::kBatteryDataTrackAudio;
}
if (mVideoSource != NULL) {
params |=IMediaPlayerService::kBatteryDataTrackVideo;
}
addBatteryData(params);
return OK;
}
4. AwesomePlayer::postVideoEvent_l()
Frameworks/av/media/libstagefright/AwesomePlayer.cpp
voidAwesomePlayer::postVideoEvent_l(int64_t delayUs) {
ATRACE_CALL();
if (mVideoEventPending) {
return;
}
mVideoEventPending = true;
mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 :delayUs);
}
voidAwesomePlayer::onVideoEvent() {
ATRACE_CALL();
Mutex::Autolock autoLock(mLock);
if (!mVideoEventPending) {
// The event has been cancelled inreset_l() but had already
// been scheduled for execution at thattime.
return;
}
mVideoEventPending = false;
if (mSeeking != NO_SEEK) {
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = NULL;
}
if (mSeeking == SEEK &&isStreamingHTTP() && mAudioSource != NULL
&& !(mFlags &SEEK_PREVIEW)) {
// We're going to seek the video sourcefirst, followed by
// the audio source.
// In order to avoid jumps in theDataSource offset caused by
// the audio codec prefetching datafrom the old locations
// while the video codec is alreadyreading data from the new
// locations, we'll"pause" the audio source, causing it to
// stop reading input data until asubsequent seek.
if (mAudioPlayer != NULL &&(mFlags & AUDIO_RUNNING)) {
mAudioPlayer->pause();
modifyFlags(AUDIO_RUNNING,CLEAR);
}
mAudioSource->pause();
}
}
if (!mVideoBuffer) {
MediaSource::ReadOptions options;
if (mSeeking != NO_SEEK) {
ALOGV("seeking to %lld us(%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
options.setSeekTo(
mSeekTimeUs,
mSeeking == SEEK_VIDEO_ONLY
? MediaSource::ReadOptions::SEEK_NEXT_SYNC
:MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
}
for (;;) {
status_terr = mVideoSource->read(&mVideoBuffer, &options);
options.clearSeekTo();
if (err != OK) {
CHECK(mVideoBuffer == NULL);
if (err == INFO_FORMAT_CHANGED){
ALOGV("VideoSourcesignalled format change.");
notifyVideoSize_l();
if (mVideoRenderer != NULL){
mVideoRendererIsPreview= false;
initRenderer_l();
}
continue;
}
// So video playback iscomplete, but we may still have
// a seek request pending thatneeds to be applied
// to the audio track.
if (mSeeking != NO_SEEK) {
ALOGV("video streamended while seeking!");
}
finishSeekIfNecessary(-1);
if (mAudioPlayer != NULL
&& !(mFlags& (AUDIO_RUNNING | SEEK_PREVIEW))) {
startAudioPlayer_l();
}
modifyFlags(VIDEO_AT_EOS, SET);
postStreamDoneEvent_l(err);
return;
}
if (mVideoBuffer->range_length()== 0) {
// Some decoders, notably thePV AVC software decoder
// return spurious emptybuffers that we just want to ignore.
mVideoBuffer->release();
mVideoBuffer = NULL;
continue;
}
break;
}
{
Mutex::AutolockautoLock(mStatsLock);
++mStats.mNumVideoFramesDecoded;
}
}
int64_t timeUs;
CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime,&timeUs));
mLastVideoTimeUs = timeUs;
if (mSeeking == SEEK_VIDEO_ONLY) {
if (mSeekTimeUs > timeUs) {
ALOGI("XXX mSeekTimeUs = %lldus, timeUs = %lld us",
mSeekTimeUs, timeUs);
}
}
{
Mutex::AutolockautoLock(mMiscStateLock);
mVideoTimeUs = timeUs;
}
SeekType wasSeeking = mSeeking;
finishSeekIfNecessary(timeUs);
if (mAudioPlayer != NULL &&!(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
status_t err = startAudioPlayer_l();
if (err != OK) {
ALOGE("Starting the audio playerfailed w/ err %d", err);
return;
}
}
if ((mFlags & TEXTPLAYER_INITIALIZED)
&& !(mFlags &(TEXT_RUNNING | SEEK_PREVIEW))) {
mTextDriver->start();
modifyFlags(TEXT_RUNNING, SET);
}
TimeSource *ts =
((mFlags & AUDIO_AT_EOS) ||!(mFlags & AUDIOPLAYER_STARTED))
? &mSystemTimeSource :mTimeSource;
if (mFlags & FIRST_FRAME) {
modifyFlags(FIRST_FRAME, CLEAR);
mSinceLastDropped = 0;
mTimeSourceDeltaUs =ts->getRealTimeUs() - timeUs;
}
int64_t realTimeUs, mediaTimeUs;
if (!(mFlags & AUDIO_AT_EOS) &&mAudioPlayer != NULL
&&mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
mTimeSourceDeltaUs = realTimeUs -mediaTimeUs;
}
if (wasSeeking == SEEK_VIDEO_ONLY) {
int64_t nowUs = ts->getRealTimeUs()- mTimeSourceDeltaUs;
int64_t latenessUs = nowUs - timeUs;
ATRACE_INT("Video Lateness(ms)", latenessUs / 1E3);
if (latenessUs > 0) {
ALOGI("after SEEK_VIDEO_ONLYwe're late by %.2f secs", latenessUs / 1E6);
}
}
if (wasSeeking == NO_SEEK) {
// Let's display the first frame afterseeking right away.
int64_t nowUs = ts->getRealTimeUs()- mTimeSourceDeltaUs;
int64_t latenessUs = nowUs - timeUs;
ATRACE_INT("Video Lateness(ms)", latenessUs / 1E3);
if (latenessUs > 500000ll
&& mAudioPlayer != NULL
&& mAudioPlayer->getMediaTimeMapping(
&realTimeUs,&mediaTimeUs)) {
if (mWVMExtractor == NULL) {
ALOGI("we're much too late(%.2f secs), video skipping ahead",
latenessUs / 1E6);
mVideoBuffer->release();
mVideoBuffer = NULL;
mSeeking = SEEK_VIDEO_ONLY;
mSeekTimeUs = mediaTimeUs;
postVideoEvent_l();
return;
} else {
// The widevine extractordoesn't deal well with seeking
// audio and videoindependently. We'll just have to wait
// until the decoder catchesup, which won't be long at all.
ALOGI("we're very late(%.2f secs)", latenessUs / 1E6);
}
}
if (latenessUs > 40000) {
// We're more than 40ms late.
ALOGV("we're late by %lld us(%.2f secs)",
latenessUs, latenessUs / 1E6);
if (!(mFlags & SLOW_DECODER_HACK)
|| mSinceLastDropped >FRAME_DROP_FREQ)
{
ALOGV("we're late by %lldus (%.2f secs) dropping "
"one after %dframes",
latenessUs, latenessUs /1E6, mSinceLastDropped);
mSinceLastDropped = 0;
mVideoBuffer->release();
mVideoBuffer = NULL;
{
Mutex::AutolockautoLock(mStatsLock);
++mStats.mNumVideoFramesDropped;
}
postVideoEvent_l();
return;
}
}
if (latenessUs < -10000) {
// We're more than 10ms early.
postVideoEvent_l(10000);
return;
}
}
if ((mNativeWindow != NULL)
&& (mVideoRendererIsPreview|| mVideoRenderer == NULL)) {
mVideoRendererIsPreview = false;
initRenderer_l();
}
if (mVideoRenderer != NULL) {
mSinceLastDropped++;
mVideoRenderer->render(mVideoBuffer);
if (!mVideoRenderingStarted) {
mVideoRenderingStarted = true;
notifyListener_l(MEDIA_INFO,MEDIA_INFO_RENDERING_START);
}
}
mVideoBuffer->release();
mVideoBuffer = NULL;
if (wasSeeking != NO_SEEK &&(mFlags & SEEK_PREVIEW)) {
modifyFlags(SEEK_PREVIEW, CLEAR);
return;
}
postVideoEvent_l();
}