"本文转载自:[yanbixing123]的 Android MultiMedia框架完全解析 - start流程分析"
1.概述
前面已经准备好了数据源,这里就开始是调用start()开始播放,先看一下java层该方法的实现:
- mediaplayer.cpp
status_t MediaPlayer::start()
{
......
mPlayer->setLooping(mLoop);
mPlayer->setVolume(mLeftVolume, mRightVolume);
mPlayer->setAuxEffectSendLevel(mSendLevel);
mCurrentState = MEDIA_PLAYER_STARTED;
ret = mPlayer->start();
......
return ret;
}
核心代码就是这些,需要注意的是,这里的mPlayer是IMediaPlayer类型的,是IMediaPlayer这个匿名Binder Server的Bp端,所以最终是通过这个匿名Binder Server类来传输消息的,传输的目的地是MediaPlayerService,其他函数暂时不分析,就从最后的start函数开始分析。
2.start()
通过IMediaPlayer的Bp端传送到Bn端,最后到达MediaPlayerService,而MediaPlayerService为这个客户端创建了一个Client,所以最终对应的函数就是:MediaPlayerService::Client::start()。
status_t MediaPlayerService::Client::start()
{
ALOGV("[%d] start", mConnId);
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
p->setLooping(mLoop);
return p->start();
}
这里获取到的MediaPlayerBase是NuPlayerDriver,所以最后还是调用到NuPlayerDriver的start函数:
status_t NuPlayerDriver::start() {
ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
Mutex::Autolock autoLock(mLock);
switch (mState) {
case STATE_PREPARED:
{
mAtEOS = false;
mPlayer->start();
if (mStartupSeekTimeUs >= 0) {
mPlayer->seekToAsync(mStartupSeekTimeUs);
mStartupSeekTimeUs = -1;
}
break;
}
经过前面的prepare步骤,此时的状态,已经是STATE_PREPARED了,而且这里的mPlayer是NuPlayer,所以继续调用到NuPlayer中的start函数:
void NuPlayer::start() {
(new AMessage(kWhatStart, this))->post();
}
通过消息机制,继续传......
NuPlayer::onMessageReceived(const sp<AMessage> &msg)
case kWhatStart:
{
ALOGV("kWhatStart");
if (mStarted) {
// do not resume yet if the source is still buffering
if (!mPausedForBuffering) {
onResume();
}
} else {
onStart();
}
mPausedByClient = false;
break;
}
终于找到核心函数了,下面就仔细分析这个onStart函数:
void NuPlayer::onStart(int64_t startPositionUs) {
if (!mSourceStarted) {
mSourceStarted = true;
mSource->start();
}
if (startPositionUs > 0) {
performSeek(startPositionUs);
if (mSource->getFormat(false /* audio */) == NULL) {
return;
}
}
mOffloadAudio = false;
mAudioEOS = false;
mVideoEOS = false;
mStarted = true;
uint32_t flags = 0;
if (mSource->isRealTime()) {
flags |= Renderer::FLAG_REAL_TIME;
}
sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
if (mAudioSink != NULL) {
streamType = mAudioSink->getAudioStreamType();
}
sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
mOffloadAudio =
canOffloadStream(audioMeta, (videoFormat != NULL), mSource->isStreaming(), streamType);
if (mOffloadAudio) {
flags |= Renderer::FLAG_OFFLOAD_AUDIO;
}
sp<AMessage> notify = new AMessage(kWhatRendererNotify, this);
++mRendererGeneration;
notify->setInt32("generation", mRendererGeneration);
mRenderer = new Renderer(mAudioSink, notify, flags);
mRendererLooper = new ALooper;
mRendererLooper->setName("NuPlayerRenderer");
mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mRendererLooper->registerHandler(mRenderer);
status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);
if (err != OK) {
mSource->stop();
mSourceStarted = false;
notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
return;
}
float rate = getFrameRate();
if (rate > 0) {
mRenderer->setVideoFrameRate(rate);
}
if (mVideoDecoder != NULL) {
mVideoDecoder->setRenderer(mRenderer);
}
if (mAudioDecoder != NULL) {
mAudioDecoder->setRenderer(mRenderer);
}
if(mVideoDecoder != NULL){
scheduleSetVideoDecoderTime();
}
postScanSources();
}
下面是这部分代码主要流程:
49096998082f7358d4c9fc4e2998e9ff_0eceb584-0a54-4f6e-9340-bfb5219630d4.png(1)首先来看mSource->start()函数,在之前的NuPlayer::setDataSourceAsync函数中,创建了一个GenericSource:
sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID);
然后又在NuPlayer::onMessageReceived函数的kWhatSetDataSource case中设置了NuPlayer中的mSource是创建的这个GenericSource:
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSetDataSource:
{
ALOGV("kWhatSetDataSource");
CHECK(mSource == NULL);
status_t err = OK;
sp<RefBase> obj;
CHECK(msg->findObject("source", &obj));
if (obj != NULL) {
mSource = static_cast<Source *>(obj.get());
所以这里的mSource->start()函数最终跑到GenericSource.cpp中去执行了。
void NuPlayer::GenericSource::start() {
ALOGI("start");
mStopRead = false;
if (mAudioTrack.mSource != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
if (mVideoTrack.mSource != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
(new AMessage(kWhatStart, this))->post();
}
这里通过postReadBuffer函数来分别发送Video Track和Audio Track的数据,并发送了一个kWhatStart的msg。
(2)先来看看postReadBuffer函数,这个函数中会根据不同的媒体类型来执行不同的操作。
void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
Mutex::Autolock _l(mReadBufferLock);
if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
mPendingReadBufferTypes |= (1 << trackType);
sp<AMessage> msg = new AMessage(kWhatReadBuffer, this);
msg->setInt32("trackType", trackType);
msg->post();
}
}
---------------------
void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
case kWhatReadBuffer:
{
onReadBuffer(msg);
break;
}
---------------------
void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) {
int32_t tmpType;
CHECK(msg->findInt32("trackType", &tmpType));
media_track_type trackType = (media_track_type)tmpType;
readBuffer(trackType);
{
// only protect the variable change, as readBuffer may
// take considerable time.
Mutex::Autolock _l(mReadBufferLock);
mPendingReadBufferTypes &= ~(1 << trackType);
}
}
又是通过一系列的转换,最终直到readBuffer函数中,这个函数根据不同的媒体类型来执行不同的操作,继续追踪:
void NuPlayer::GenericSource::readBuffer(
media_track_type trackType, int64_t seekTimeUs, MediaPlayerSeekMode mode,
int64_t *actualTimeUs, bool formatChange) {
...
Track *track;
size_t maxBuffers = 1;
switch (trackType) {// 根据track类型分配最大buffer,并初始化track
case MEDIA_TRACK_TYPE_VIDEO:// 视频
track = &mVideoTrack;
maxBuffers = 8; // too large of a number may influence seeks
break;
case MEDIA_TRACK_TYPE_AUDIO:// 音频
track = &mAudioTrack;
maxBuffers = 64;
if (mIsByteMode) {
maxBuffers = 1;
}
break;
case MEDIA_TRACK_TYPE_SUBTITLE:// 字幕
track = &mSubtitleTrack;
...
}
...
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
Vector<MediaBuffer *> mediaBuffers;
status_t err = NO_ERROR;
if (couldReadMultiple) {//一般为true
// 从文件中读取媒体数据,用于填充mediaBuffers
err = track->mSource->readMultiple(
&mediaBuffers, maxBuffers - numBuffers, &options);
} else {//read函数其实最终也是调用了readMultiple,只是read的最大buffer数为1
MediaBuffer *mbuf = NULL;
err = track->mSource->read(&mbuf, &options);
if (err == OK && mbuf != NULL) {
mediaBuffers.push_back(mbuf);
}
}
options.clearNonPersistent();
size_t id = 0;
size_t count = mediaBuffers.size();
// 将所有刚才读到的MediaBuffer中的数据摘出来封装到mPackets中
for (; id < count; ++id) {
int64_t timeUs;
MediaBuffer *mbuf = mediaBuffers[id];
...
// 根据类型,通过mBufferingMonitor监视器更新状态
if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
mAudioTimeUs = timeUs;
mBufferingMonitor->updateQueuedTime(true /* isAudio */, timeUs);
} else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
mVideoTimeUs = timeUs;
mBufferingMonitor->updateQueuedTime(false /* isAudio */, timeUs);
}
queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
// 根据类型,将MediaBuffer转换为ABuffer
sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType);
...
// 将buffer入队,等待解码
track->mPackets->queueAccessUnit(buffer);
formatChange = false;
seeking = false;
++numBuffers;
}
...
}
}
最核心的函数是这个:track->mSource->read(&mbuf, &options);
同时根据不同的track类型,track为对应的真正的track实体,以Video Track为例,track = &mVideoTrack;
(3)再来回顾一下,在NuPlayer::GenericSource::initFromDataSource()函数中,通过外部的一个for循环,以及内部的 sp<MediaSource> track = extractor->getTrack(i); 最终在MPEG4Extractor::getTrack函数中获取到各个track,先new 一个MPEG4Extractor(这里是根据数据类型创建对应的Extractor,假设是mp4文件)来保存,然后在GenericSource.cpp中保存到mAudioTrack / mVideoTrack 以及Vector<sp<MediaSource> > mSources 这个Vector中。
for (size_t i = 0; i < numtracks; ++i) {// 遍历轨道,将音视频轨道信息的mime添加到mMimes中
sp<IMediaSource> track = extractor->getTrack(i);// 获取各轨道
...
sp<MetaData> meta = extractor->getTrackMetaData(i);// 获取各轨道的元数据
......
mVideoTrack.mIndex = i;
mVideoTrack.mSource = track;
mVideoTrack.mPackets =
new AnotherPacketSource(mVideoTrack.mSource->getFormat());
......
mSources.push(track); // 将各轨道信息统一保存在保存在mSources中
所以这里调用的mVideoTrack最终就是MPEG4Extractor中的MPEG4Source,而这个track->mSource->read也就对应为MPEG4Source::read()函数(MPEG4Extractor.cpp文件中):
media_status_t MPEG4Source::read(
MediaBufferHelper **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
if (options != nullptr && options->getNonBlocking() && !mBufferGroup->has_buffers()) {
*out = nullptr;
return AMEDIA_ERROR_WOULD_BLOCK;
}
if (mFirstMoofOffset > 0) {
return fragmentedRead(out, options);
}
*out = NULL;
int64_t targetSampleTimeUs = -1;
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
if (mIsHeif) {
CHECK(mSampleTable == NULL);
CHECK(mItemTable != NULL);
int32_t imageIndex;
if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_TRACK_ID, &imageIndex)) {
return AMEDIA_ERROR_MALFORMED;
}
status_t err;
if (seekTimeUs >= 0) {
err = mItemTable->findImageItem(imageIndex, &mCurrentSampleIndex);
} else {
err = mItemTable->findThumbnailItem(imageIndex, &mCurrentSampleIndex);
}
if (err != OK) {
return AMEDIA_ERROR_UNKNOWN;
}
} else {
uint32_t findFlags = 0;
switch (mode) {
case ReadOptions::SEEK_PREVIOUS_SYNC:
findFlags = SampleTable::kFlagBefore;
break;
case ReadOptions::SEEK_NEXT_SYNC:
findFlags = SampleTable::kFlagAfter;
break;
case ReadOptions::SEEK_CLOSEST_SYNC:
case ReadOptions::SEEK_CLOSEST:
findFlags = SampleTable::kFlagClosest;
break;
case ReadOptions::SEEK_FRAME_INDEX:
findFlags = SampleTable::kFlagFrameIndex;
break;
default:
CHECK(!"Should not be here.");
break;
}
if( mode != ReadOptions::SEEK_FRAME_INDEX) {
seekTimeUs += ((long double)mElstShiftStartTicks * 1000000) / mTimescale;
}
uint32_t sampleIndex;
status_t err = mSampleTable->findSampleAtTime(
seekTimeUs, 1000000, mTimescale,
&sampleIndex, findFlags);
if (mode == ReadOptions::SEEK_CLOSEST
|| mode == ReadOptions::SEEK_FRAME_INDEX) {
// We found the closest sample already, now we want the sync
// sample preceding it (or the sample itself of course), even
// if the subsequent sync sample is closer.
findFlags = SampleTable::kFlagBefore;
}
uint32_t syncSampleIndex = sampleIndex;
// assume every audio sample is a sync sample. This works around
// seek issues with files that were incorrectly written with an
// empty or single-sample stss block for the audio track
if (err == OK && !mIsAudio) {
err = mSampleTable->findSyncSampleNear(
sampleIndex, &syncSampleIndex, findFlags);
}
uint64_t sampleTime;
if (err == OK) {
err = mSampleTable->getMetaDataForSample(
sampleIndex, NULL, NULL, &sampleTime);
}
if (err != OK) {
if (err == ERROR_OUT_OF_RANGE) {
// An attempt to seek past the end of the stream would
// normally cause this ERROR_OUT_OF_RANGE error. Propagating
// this all the way to the MediaPlayer would cause abnormal
// termination. Legacy behaviour appears to be to behave as if
// we had seeked to the end of stream, ending normally.
return AMEDIA_ERROR_END_OF_STREAM;
}
ALOGV("end of stream");
return AMEDIA_ERROR_UNKNOWN;
}
if (mode == ReadOptions::SEEK_CLOSEST
|| mode == ReadOptions::SEEK_FRAME_INDEX) {
sampleTime -= mElstShiftStartTicks;
targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
}
#if 0
uint32_t syncSampleTime;
CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
syncSampleIndex, NULL, NULL, &syncSampleTime));
ALOGI("seek to time %lld us => sample at time %lld us, "
"sync sample at time %lld us",
seekTimeUs,
sampleTime * 1000000ll / mTimescale,
syncSampleTime * 1000000ll / mTimescale);
#endif
mCurrentSampleIndex = syncSampleIndex;
}
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
}
// fall through
}
off64_t offset = 0;
size_t size = 0;
uint64_t cts, stts;
bool isSyncSample;
bool newBuffer = false;
if (mBuffer == NULL) {
newBuffer = true;
status_t err;
if (!mIsHeif) {
err = mSampleTable->getMetaDataForSample(
mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
if(err == OK) {
/* Composition Time Stamp cannot be negative. Some files have video Sample
* Time(STTS)delta with zero value(b/117402420). Hence subtract only
* min(cts, mElstShiftStartTicks), so that audio tracks can be played.
*/
cts -= std::min(cts, mElstShiftStartTicks);
}
} else {
err = mItemTable->getImageOffsetAndSize(
options && options->getSeekTo(&seekTimeUs, &mode) ?
&mCurrentSampleIndex : NULL, &offset, &size);
cts = stts = 0;
isSyncSample = 0;
ALOGV("image offset %lld, size %zu", (long long)offset, size);
}
if (err != OK) {
if (err == ERROR_END_OF_STREAM) {
return AMEDIA_ERROR_END_OF_STREAM;
}
return AMEDIA_ERROR_UNKNOWN;
}
err = mBufferGroup->acquire_buffer(&mBuffer);
if (err != OK) {
CHECK(mBuffer == NULL);
return AMEDIA_ERROR_UNKNOWN;
}
if (size > mBuffer->size()) {
ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_UNKNOWN; // ERROR_BUFFER_TOO_SMALL
}
}
if (!mIsAVC && !mIsHEVC && !mIsAC4) {
if (newBuffer) {
if (mIsPcm) {
// The twos' PCM block reader assumes that all samples has the same size.
uint32_t samplesToRead = mSampleTable->getLastSampleIndexInChunk()
- mCurrentSampleIndex + 1;
if (samplesToRead > kMaxPcmFrameSize) {
samplesToRead = kMaxPcmFrameSize;
}
ALOGV("Reading %d PCM frames of size %zu at index %d to stop of chunk at %d",
samplesToRead, size, mCurrentSampleIndex,
mSampleTable->getLastSampleIndexInChunk());
size_t totalSize = samplesToRead * size;
uint8_t* buf = (uint8_t *)mBuffer->data();
ssize_t bytesRead = mDataSource->readAt(offset, buf, totalSize);
if (bytesRead < (ssize_t)totalSize) {
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_IO;
}
AMediaFormat *meta = mBuffer->meta_data();
AMediaFormat_clear(meta);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale);
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
int32_t byteOrder;
AMediaFormat_getInt32(mFormat,
AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, &byteOrder);
if (byteOrder == 1) {
// Big-endian -> little-endian
uint16_t *dstData = (uint16_t *)buf;
uint16_t *srcData = (uint16_t *)buf;
for (size_t j = 0; j < bytesRead / sizeof(uint16_t); j++) {
dstData[j] = ntohs(srcData[j]);
}
}
mCurrentSampleIndex += samplesToRead;
mBuffer->set_range(0, totalSize);
} else {
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_IO;
}
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
AMediaFormat *meta = mBuffer->meta_data();
AMediaFormat_clear(meta);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_DURATION, ((long double)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs);
}
if (isSyncSample) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
++mCurrentSampleIndex;
}
}
*out = mBuffer;
mBuffer = NULL;
return AMEDIA_OK;
} else if (mIsAC4) {
CHECK(mBuffer != NULL);
// Make sure there is enough space to write the sync header and the raw frame
if (mBuffer->range_length() < (7 + size)) {
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t dstOffset = 0;
// Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame
// AC40 sync word, meaning no CRC at the end of the frame
dstData[dstOffset++] = 0xAC;
dstData[dstOffset++] = 0x40;
dstData[dstOffset++] = 0xFF;
dstData[dstOffset++] = 0xFF;
dstData[dstOffset++] = (uint8_t)((size >> 16) & 0xFF);
dstData[dstOffset++] = (uint8_t)((size >> 8) & 0xFF);
dstData[dstOffset++] = (uint8_t)((size >> 0) & 0xFF);
ssize_t numBytesRead = mDataSource->readAt(offset, dstData + dstOffset, size);
if (numBytesRead != (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_IO;
}
mBuffer->set_range(0, dstOffset + size);
AMediaFormat *meta = mBuffer->meta_data();
AMediaFormat_clear(meta);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_DURATION, ((long double)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs);
}
if (isSyncSample) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
++mCurrentSampleIndex;
*out = mBuffer;
mBuffer = NULL;
return AMEDIA_OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
ssize_t num_bytes_read = 0;
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t srcOffset = 0;
size_t dstOffset = 0;
while (srcOffset < size) {
bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
size_t nalLength = 0;
if (!isMalFormed) {
nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
srcOffset += mNALLengthSize;
isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
}
if (isMalFormed) {
//if nallength abnormal,ignore it.
ALOGW("abnormal nallength, ignore this NAL");
srcOffset = size;
break;
}
if (nalLength == 0) {
continue;
}
if (dstOffset > SIZE_MAX - 4 ||
dstOffset + 4 > SIZE_MAX - nalLength ||
dstOffset + 4 + nalLength > mBuffer->size()) {
ALOGE("b/27208621 : %zu %zu", dstOffset, mBuffer->size());
android_errorWriteLog(0x534e4554, "27208621");
mBuffer->release();
mBuffer = NULL;
return AMEDIA_ERROR_MALFORMED;
}
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 1;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
CHECK_EQ(srcOffset, size);
CHECK(mBuffer != NULL);
mBuffer->set_range(0, dstOffset);
AMediaFormat *meta = mBuffer->meta_data();
AMediaFormat_clear(meta);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale);
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_DURATION, ((long double)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
AMediaFormat_setInt64(
meta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs);
}
if (mIsAVC) {
uint32_t layerId = FindAVCLayerId(
(const uint8_t *)mBuffer->data(), mBuffer->range_length());
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId);
} else if (mIsHEVC) {
int32_t layerId = parseHEVCLayerId(
(const uint8_t *)mBuffer->data(), mBuffer->range_length());
if (layerId >= 0) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId);
}
}
if (isSyncSample) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
++mCurrentSampleIndex;
*out = mBuffer;
mBuffer = NULL;
return AMEDIA_OK;
}
}
这个read函数太复杂了,主要是把获得的这帧数据保存在*out中,传递到函数外面。这里就不再深究了,感兴趣的可以自己去了解一下。
在NuPlayer::GenericSource::readBuffer函数中,设置了不同Track类型需要读取的最大Buffer的数量,AudioBuffer为64个,VideoBuffer为4个。这里再贴一下代码:
void NuPlayer::GenericSource::readBuffer(
media_track_type trackType, int64_t seekTimeUs, MediaPlayerSeekMode mode,
int64_t *actualTimeUs, bool formatChange) {
...
Track *track;
size_t maxBuffers = 1;
switch (trackType) {// 根据track类型分配最大buffer,并初始化track
case MEDIA_TRACK_TYPE_VIDEO:// 视频
track = &mVideoTrack;
maxBuffers = 8; // too large of a number may influence seeks
break;
case MEDIA_TRACK_TYPE_AUDIO:// 音频
track = &mAudioTrack;
maxBuffers = 64;
if (mIsByteMode) {
maxBuffers = 1;
}
break;
case MEDIA_TRACK_TYPE_SUBTITLE:// 字幕
track = &mSubtitleTrack;
...
}
退出MPEG4Source::read函数,退回到NuPlayer::GenericSource::readBuffer函数中,当读取完所需的buffer后,如果执行了formatChange / seeking 操作的话,就会调用 queueDiscontinuityIfNeeded函数来不连续的Queue Buffer。
同时NuPlayer::GenericSource::start()函数中还发送了一个kWhatStart的msg,这个msg会通过NuPlayer::GenericSource::BufferingMonitor来维护着GenericSource的整个Buffer流程。
最后,返回到NuPlayer::GenericSource::readBuffer时,会将MPEG4Source读取到的数据通过queueAccessUnit()方法保存到buffer队列中,等待解码。
// 将buffer入队,等待解码
track->mPackets->queueAccessUnit(buffer);
网友评论