一、引言:
在上一篇博客,对nuplayer的创建流程有了一个分析,并且在末尾引入了输入输出buffer的处理出处,这篇博客将分析nuplayer对音频数据的处理机制。
先贴出输入输出buffer的核心处理函数:
--------------------------------------------------------------------------
onMessageReceived@frameworks\av\media\libmediaplayerservice\nuplayer\NuPlayerDecoder.cpp:
--------------------------------------------------------------------------
case kWhatCodecNotify:
{
if (!isStaleReply(msg)) {
int32_t numInput, numOutput;
if (!msg->findInt32("input-buffers", &numInput)) {
numInput = INT32_MAX;
}
if (!msg->findInt32("output-buffers", &numOutput)) {
numOutput = INT32_MAX;
}
if (!mPaused) {
while (numInput-- > 0 && handleAnInputBuffer()) {
}
}
while (numOutput-- > 0 && handleAnOutputBuffer()) {
}
}
requestCodecNotification();
break;
}
二、音频输入数据处理:
先看handleAnInputBuffer()
函数:
bool NuPlayer::Decoder::handleAnInputBuffer() {
if (mFormatChangePending) {
return false;
}
size_t bufferIx = -1;
/* 从MediaCodec中获取可用的input buffer用于填充数据 */
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);
}
return false;
}
/* 根据index找到对应的buffer并做一些检查工作 */
CHECK_LT(bufferIx, mInputBuffers.size());
if (mMediaBuffers[bufferIx] != NULL) {
mMediaBuffers[bufferIx]->release();
mMediaBuffers.editItemAt(bufferIx) = NULL;
}
mInputBufferIsDequeued.editItemAt(bufferIx) = true;
...
/* 2.调用到父类DecoderBase中 */
onRequestInputBuffers();
return true;
}
函数的工作很简单,先是调用MediaCodec拿到可用的输入buffer index,然后做一些检查工作,onRequestInputBuffers()函数是被声明为protected的,所以这里直接调用到父类中去,跟进看一下:
void NuPlayer::DecoderBase::onRequestInputBuffers() {
if (mRequestInputBuffersPending) {
return;
}
/* 调回去... */
doRequestBuffers();
}
好吧,又调回到Decoder…
void NuPlayer::Decoder::doRequestBuffers() {
if (mFormatChangePending) {
return;
}
status_t err = OK;
while (!mDequeuedInputBuffers.empty()) {
size_t bufferIx = *mDequeuedInputBuffers.begin();
sp<AMessage> msg = new AMessage();
msg->setSize("buffer-ix", bufferIx);
/* 1.从MediaSource中找到待写入buffer */
err = fetchInputData(msg);
if (err != OK) {
break;
}
mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
/* 2.将source中的buffer拷贝到MediaCodec中 */
if (!mPendingInputMessages.empty()
|| !onInputBufferFetched(msg)) {
mPendingInputMessages.push_back(msg);
}
}
/* 确认是否还需要持续写入数据 */
if (err == -EWOULDBLOCK
&& mSource->feedMoreTSData() == OK) {
scheduleRequestBuffers();
}
}
doRequestBuffers函数的目的是将媒体流中的文件内容写入到MediaCodec中的input buffer中
。首先是调用fetchInputData函数找到Source
中的待写入buffer,看一下fetchInputData函数:
status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
sp<ABuffer> accessUnit;
bool dropAccessUnit;
do {
/* 1.从source中获取待写入buffer的强指针 */
status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);
/* 异常处理 */
if (err == -EWOULDBLOCK) {
return err;
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
...
}
reply->setInt32("err", err);
return OK;
}
if (!mIsAudio) {
++mNumFramesTotal;
}
/* 标志位用于确认找到正确的可写buffer */
dropAccessUnit =