⑥NuPlayer播放源码分析之DecoderBase分析
2017-03-01 15:00
375 查看
NuPlayer播放源码分析之DecoderBase分析
[时间:2017-02] [状态:Open][关键词:android,nuplayer,开源播放器,播放框架,DecoderBase,MediaCodec]
0 导读
DecoderBase是AHandler的一个子类,主要功能是负责解码,按照MediaPlayer的框架,一般是调用MediaCodec完成解码,功能类似FFmpeg的libavcodec。其主要功能是解码器初始化、解码以及和其他模块的交互,比如Source、Renderer等。NuPlayer::Decoder是DecoderBase的子类。
本文是我的NuPlayer播放框架的第六篇。
1 NuPlayer中关于DecoderBase的调用
NuPlayer的成员变量中有两个DecoderBase指针,如下:sp<DecoderBase> mVideoDecoder; sp<DecoderBase> mAudioDecoder;
这里以视频解码器
mVideoDecoder说明相关调用逻辑。
// case kWhatSetVideoSurface: mVideoDecoder->setVideoSurface(surface); // case kWhatConfigPlayback: if (mVideoDecoder != NULL) { float rate = getFrameRate(); if (rate > 0) { sp<AMessage> params = new AMessage(); params->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed); mVideoDecoder->setParameters(params); } } // 很多地方调用 instantiateDecoder(false, &mVideoDecoder); // case kWhatVideoNotify: -- case kWhatAudioNotify: mVideoDecoder.clear(); // NuPlayer::onStart() mVideoDecoder->setRenderer(mRenderer); mVideoDecoder->getStats(); mVideoDecoder->signalResume(needNotify);
这里最主要的初始化位于instantiateDecoder中,其简化版的实现如下(删除音频和字幕相关初始化参数):
status_t NuPlayer::instantiateDecoder( bool audio, sp<DecoderBase> *decoder, bool checkAudioModeChange) { // 从Source中获得原始的音视频格式 sp<AMessage> format = mSource->getFormat(audio); if (format == NULL) { return UNKNOWN_ERROR; } else { status_t err; if (format->findInt32("err", &err) && err) { return err; } } format->setInt32("priority", 0 /* realtime */); { sp<AMessage> notify = new AMessage(kWhatVideoNotify, this); ++mVideoDecoderGeneration; notify->setInt32("generation", mVideoDecoderGeneration); *decoder = new Decoder( notify, mSource, mPID, mRenderer, mSurface, mCCDecoder); } (*decoder)->init(); (*decoder)->configure(format); return OK; }
2 NuPlayer::DecoderBase类分析
DecoderBase类声明如下:struct NuPlayer::DecoderBase : public AHandler { DecoderBase(const sp<AMessage> ¬ify); void configure(const sp<AMessage> &format); void init(); void setParameters(const sp<AMessage> ¶ms); // Synchronous call to ensure decoder will not request or send out data. void pause(); void setRenderer(const sp<Renderer> &renderer); virtual status_t setVideoSurface(const sp<Surface> &) { return INVALID_OPERATION; } status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; void signalFlush(); void signalResume(bool notifyComplete); void initiateShutdown(); virtual sp<AMessage> getStats() const; protected: virtual ~DecoderBase(); virtual void onMessageReceived(const sp<AMessage> &msg); virtual void onConfigure(const sp<AMessage> &format) = 0; virtual void onSetParameters(const sp<AMessage> ¶ms) = 0; virtual void onSetRenderer(const sp<Renderer> &renderer) = 0; virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0; virtual void onResume(bool notifyComplete) = 0; virtual void onFlush() = 0; virtual void onShutdown(bool notifyComplete) = 0; void onRequestInputBuffers(); virtual bool doRequestBuffers() = 0; virtual void handleError(int32_t err); sp<AMessage> mNotify; int32_t mBufferGeneration; bool mPaused; sp<AMessage> mStats; private: sp<ALooper> mDecoderLooper; bool mRequestInputBuffersPending; DISALLOW_EVIL_CONSTRUCTORS(DecoderBase); };
从声明来看,DecoderBase主要是基于AHandler-ALoop搭建一个解码器框架和消息循环泵。将对public接口的调用直接转移到onXXX的调用上。
下面以configure接口调用为例简单说明下DecoderBase中的调用逻辑。其实现代码如下:
void NuPlayer::DecoderBase::configure(const sp<AMessage> &format) { sp<AMessage> msg = new AMessage(kWhatConfigure, this); msg->setMessage("format", format); msg->post(); }
这个函数直接发送了kWhatConfigure消息,对应的消息处理如下:
void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatConfigure: { sp<AMessage> format; CHECK(msg->findMessage("format", &format)); onConfigure(format); break; } // ...
这就直接调用到DecoderBase::onConfigure函数上, 注意这是一个纯虚函数,没有实现的。
其他对外接口的实现逻辑和configure非常类似。
在NuPlayer中DecoderBase有两个子类:Decoder和DecoderPassThrough(从NuPlayer调用来看,这个类仅跟音频解码有关)。
我们还是重点关注一个类NuPlayer::Decoder。
3 NuPlayer::Decoder接口及主要成员分析
这个类内部是真正调用MediaCodec实现解码的类,这里仅关注主要的对外及protected接口,和核心的成员变量。代码如下:struct NuPlayer::Decoder : public DecoderBase { Decoder(const sp<AMessage> ¬ify, const sp<Source> &source, pid_t pid, const sp<Renderer> &renderer = NULL, const sp<Surface> &surface = NULL, const sp<CCDecoder> &ccDecoder = NULL); virtual sp<AMessage> getStats() const; // sets the output surface of video decoders. virtual status_t setVideoSurface(const sp<Surface> &surface); protected: virtual ~Decoder(); virtual void onMessageReceived(const sp<AMessage> &msg); virtual void onConfigure(const sp<AMessage> &format); virtual void onSetParameters(const sp<AMessage> ¶ms); virtual void onSetRenderer(const sp<Renderer> &renderer); virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers); virtual void onResume(bool notifyComplete); virtual void onFlush(); virtual void onShutdown(bool notifyComplete); virtual bool doRequestBuffers(); private: sp<Surface> mSurface; sp<Source> mSource; sp<Renderer> mRenderer; sp<AMessage> mInputFormat; sp<AMessage> mOutputFormat; sp<MediaCodec> mCodec; // 解码 sp<ALooper> mCodecLooper; List<sp<AMessage> > mPendingInputMessages; Vector<sp<ABuffer> > mInputBuffers; // 输入数据 Vector<sp<ABuffer> > mOutputBuffers; // 输出数据 Vector<bool> mInputBufferIsDequeued; Vector<MediaBuffer *> mMediaBuffers; Vector<size_t> mDequeuedInputBuffers; DISALLOW_EVIL_CONSTRUCTORS(Decoder); };
接口多数是继承自DecoderBase的public和protected成员函数,这里最主要的成员是mCodec,接下来的流程梳理也是围绕这一点展开。
4 DecoderBase/Decoder实现解析
构造函数和析构函数
从代码来看这两个函数主要是创建和销毁Looper,同时释放MediaCodec相关资源,代码如下:NuPlayer::Decoder::Decoder( const sp<AMessage> ¬ify, const sp<Source> &source, pid_t pid, const sp<Renderer> &renderer, const sp<Surface> &surface, const sp<CCDecoder> &ccDecoder) : DecoderBase(notify), mSurface(surface), mSource(source), mRenderer(renderer), { mCodecLooper = new ALooper; mCodecLooper->setName("NPDecoder-CL"); mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO); } void NuPlayer::Decoder::releaseAndResetMediaBuffers() { for (size_t i = 0; i < mMediaBuffers.size(); i++) { if (mMediaBuffers[i] != NULL) { mMediaBuffers[i]->release(); mMediaBuffers.editItemAt(i) = NULL; } } mMediaBuffers.resize(mInputBuffers.size()); for (size_t i = 0; i < mMediaBuffers.size(); i++) { mMediaBuffers.editItemAt(i) = NULL; } mInputBufferIsDequeued.clear(); mInputBufferIsDequeued.resize(mInputBuffers.size()); for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) { mInputBufferIsDequeued.editItemAt(i) = false; } mPendingInputMessages.clear(); mDequeuedInputBuffers.clear(); mSkipRenderingUntilMediaTimeUs = -1; } NuPlayer::Decoder::~Decoder() { mCodec->release(); releaseAndResetMediaBuffers(); }
init()和configure()实现
init()实现比较简单,就是把Looper和Handler关联起来,代码如下:void NuPlayer::DecoderBase::init() { mDecoderLooper->registerHandler(this); }
configure()的最终实现是在onConfigure中,代码如下:
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); AString mime; CHECK(format->findString("mime", &mime)); // 需要找到音视频的具体类型 mIsAudio = !strncasecmp("audio/", mime.c_str(), 6); mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str()); mComponentName = mime; mComponentName.append(" decoder"); // 根据mime,创建MediaCodec mCodec = MediaCodec::CreateByType( mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid); int32_t secure = 0; if (format->findInt32("secure", &secure) && secure != 0) { if (mCodec != NULL) { mCodec->getName(&mComponentName); mComponentName.append(".secure"); mCodec->release(); mCodec = MediaCodec::CreateByComponentName( mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid); } } if (mCodec == NULL) { handleError(UNKNOWN_ERROR); return; } mIsSecure = secure; mCodec->getName(&mComponentName); status_t err; if (mSurface != NULL) { // disconnect from surface as MediaCodec will reconnect err = native_window_api_disconnect( mSurface.get(), NATIVE_WINDOW_API_MEDIA); // We treat this as a warning, as this is a preparatory step. // Codec will try to connect to the surface, which is where // any error signaling will occur. ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err); } err = mCodec->configure( format, mSurface, NULL /* crypto */, 0 /* flags */); if (err != OK) { ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err); mCodec->release(); mCodec.clear(); handleError(err); return; } rememberCodecSpecificData(format); // the following should work in configured state CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); mStats->setString("mime", mime.c_str()); mStats->setString("component-name", mComponentName.c_str()); if (!mIsAudio) { int32_t width, height; if (mOutputFormat->findInt32("width", &width) && mOutputFormat->findInt32("height", &height)) { mStats->setInt32("width", width); mStats->setInt32("height", height); } } sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); mCodec->setCallback(reply); err = mCodec->start(); if (err != OK) { ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err); mCodec->release(); mCodec.clear(); handleError(err); return; } releaseAndResetMediaBuffers(); mPaused = false; mResumePending = false; }
setParameters、setRenderer、setVideoSurface
setParameters实现相对简单,直接将参数传递给MediaCodec,代码如下:void NuPlayer::Decoder::onSetParameters(const sp<AMessage> ¶ms) { if (mCodec == NULL) { ALOGW("onSetParameters called before codec is created."); return; } mCodec->setParameters(params); }
setRenderer接口主要的目的是启动解码流程,内部实现事件轮巡。代码如下:
void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) { bool hadNoRenderer = (mRenderer == NULL); mRenderer = renderer; if (hadNoRenderer && mRenderer != NULL) { // this means that the widevine legacy source is ready onRequestInputBuffers(); } } void NuPlayer::DecoderBase::onRequestInputBuffers() { if (mRequestInputBuffersPending) { return; } // doRequestBuffers() return true if we should request more data if (doRequestBuffers()) { mRequestInputBuffersPending = true; sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this); msg->post(10 * 1000ll); // 这里重发了同一个消息 } } // 代码来自NuPlayer::DecoderBase::onMessageReceived函数 case kWhatRequestInputBuffers: { mRequestInputBuffersPending = false; onRequestInputBuffers(); break; }
setVideoSurface的实现如下:
status_t NuPlayer::Decoder::setVideoSurface(const sp<Surface> &surface) { sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this); msg->setObject("surface", surface); sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); if (err == OK && response != NULL) { CHECK(response->findInt32("err", &err)); } return err; }
对应的消息处理代码如下,其中调用了native_window相关的代码:
case kWhatSetVideoSurface: { sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<RefBase> obj; CHECK(msg->findObject("surface", &obj)); sp<Surface> surface = static_cast<Surface *>(obj.get()); // non-null int32_t err = INVALID_OPERATION; // NOTE: in practice mSurface is always non-null, but checking here for completeness if (mCodec != NULL && mSurface != NULL) { // TODO: once AwesomePlayer is removed, remove this automatic connecting // to the surface by MediaPlayerService. // at this point MediaPlayerService::client has already connected to the // surface, which MediaCodec does not expect err = native_window_api_disconnect(surface.get(), NATIVE_WINDOW_API_MEDIA); if (err == OK) { err = mCodec->setSurface(surface); if (err == OK) { // reconnect to the old surface as MPS::Client will expect to // be able to disconnect from it. (void)native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_MEDIA); mSurface = surface; } } if (err != OK) { // reconnect to the new surface on error as MPS::Client will expect to // be able to disconnect from it. (void)native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA); } } sp<AMessage> response = new AMessage; response->setInt32("err", err); response->postReply(replyID); break; }
signalFlush、signalResume、initiateShutdown
signalFlush实现代码如下,主要调用Renderer和MediaCodec的flush接口:void NuPlayer::Decoder::onFlush() { doFlush(true); sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); } void NuPlayer::Decoder::doFlush(bool notifyComplete) { if (mCCDecoder != NULL) { mCCDecoder->flush(); } if (mRenderer != NULL) { mRenderer->flush(mIsAudio, notifyComplete); mRenderer->signalTimeDiscontinuity(); } status_t err = OK; if (mCodec != NULL) { err = mCodec->flush(); mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator ++mBufferGeneration; } if (err != OK) { ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err); handleError(err); // finish with posting kWhatFlushCompleted. // we attempt to release the buffers even if flush fails. } releaseAndResetMediaBuffers(); mPaused = true; }
signalResume相对简单,直接调用MediaCodec接口,代码如下:
void NuPlayer::Decoder::onResume(bool notifyComplete) { mPaused = false; if (notifyComplete) { mResumePending = true; } mCodec->start(); }
initiateShutdown主要是关闭解码器,实现如下,
void NuPlayer::Decoder::onShutdown(bool notifyComplete) { status_t err = OK; if (mCodec != NULL) { err = mCodec->release(); mCodec = NULL; ++mBufferGeneration; if (mSurface != NULL) { // reconnect to surface as MediaCodec disconnected from it status_t error = native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_MEDIA); } mComponentName = "decoder"; } releaseAndResetMediaBuffers(); if (err != OK) { handleError(err); // finish with posting kWhatShutdownCompleted. } if (notifyComplete) { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatShutdownCompleted); notify->post(); mPaused = true; } }
getInputBuffers、getStats
getInputBuffers实现直接调用MediaCodec的接口,如下:void NuPlayer::Decoder::onGetInputBuffers( Vector<sp<ABuffer> > *dstBuffers) { CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers)); }
getStats实现如下,获取解码帧数、输入输出的丢帧数目等:
sp<AMessage> NuPlayer::Decoder::getStats() const { mStats->setInt64("frames-total", mNumFramesTotal); mStats->setInt64("frames-dropped-input", mNumInputFramesDropped); mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped); return mStats; }
5 Decoder解码流程分析
前面是以接口为界进行代码功能分析,实际上最为主要的解码流程并没在这里。实际解码过程中,无外乎获取输入的压缩数据,MediaCodec解码,返回解码之后的数据,渲染。
在Part 4中介绍onConfigure函数实现时有下面代码:
sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); mCodec->setCallback(reply);
这里就是将MediaCodec的消息发送给当前类Decoder消息队列中,然后在onMessageReceived中处理。
实际的解码开始是从setRenderer开始,Part 4中对这个函数的实现也有介绍,循环解码的逻辑来在于onRequestInputBuffers函数,其中会调用doRequestBuffers,其实现如下:
// returns true if we should request more data bool NuPlayer::Decoder::doRequestBuffers() { // mRenderer is only NULL if we have a legacy widevine source that // is not yet ready. In this case we must not fetch input. if (isDiscontinuityPending() || mRenderer == NULL) { return false; } status_t err = OK; while (err == OK && !mDequeuedInputBuffers.empty()) { size_t bufferIx = *mDequeuedInputBuffers.begin(); sp<AMessage> msg = new AMessage(); msg->setSize("buffer-ix", bufferIx); err = fetchInputData(msg); // 取一个输入缓冲 if (err != OK && err != ERROR_END_OF_STREAM) { // if EOS, need to queue EOS buffer break; } mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin()); if (!mPendingInputMessages.empty() || !onInputBufferFetched(msg)) { mPendingInputMessages.push_back(msg); // 实际取出的数据放到这个缓冲消息队列中 } } return err == -EWOULDBLOCK && mSource->feedMoreTSData() == OK; }
如果doRequestBuffers返回true的话,kWhatRequestInputBuffers会循环发送kWhatRequestInputBuffers消息,驱动正常解码逻辑。
下面是关于MediaCodec返回消息的处理逻辑,代码主要集中在onMessageReceived中,如下:
case kWhatCodecNotify: { int32_t cbID; CHECK(msg->findInt32("callbackID", &cbID)); if (mPaused) { break; } switch (cbID) { case MediaCodec::CB_INPUT_AVAILABLE: {// 可以填充输入数据了 int32_t index; CHECK(msg->findInt32("index", &index)); handleAnInputBuffer(index); break; } case MediaCodec::CB_OUTPUT_AVAILABLE: {// 解码成功的消息,输出的数据在这里 int32_t index; size_t offset; size_t size; int64_t timeUs; int32_t flags; CHECK(msg->findInt32("index", &index)); CHECK(msg->findSize("offset", &offset)); CHECK(msg->findSize("size", &size)); CHECK(msg->findInt64("timeUs", &timeUs)); CHECK(msg->findInt32("flags", &flags)); handleAnOutputBuffer(index, offset, size, timeUs, flags); break; } case MediaCodec::CB_OUTPUT_FORMAT_CHANGED: { // 通知输出格式变化 sp<AMessage> format; CHECK(msg->findMessage("format", &format)); handleOutputFormatChange(format); break; } case MediaCodec::CB_ERROR: {// 发生未知的错误,需要处理下 status_t err; CHECK(msg->findInt32("err", &err)); handleError(err); break; } default: {// 其他消息不作处理 TRESPASS(); break; } } break; }
先看如何向MediaCodec输入数据。代码如下:
bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) { if (isDiscontinuityPending()) { return false; } sp<ABuffer> buffer; mCodec->getInputBuffer(index, &buffer); // 先从MediaCodec中获取一个可用的输入缓冲 if (buffer == NULL) { handleError(UNKNOWN_ERROR); return false; } if (index >= mInputBuffers.size()) { for (size_t i = mInputBuffers.size(); i <= index; ++i) { mInputBuffers.add(); mMediaBuffers.add(); mInputBufferIsDequeued.add(); mMediaBuffers.editItemAt(i) = NULL; mInputBufferIsDequeued.editItemAt(i) = false; } } mInputBuffers.editItemAt(index) = buffer; //CHECK_LT(bufferIx, mInputBuffers.size()); if (mMediaBuffers[index] != NULL) { mMediaBuffers[index]->release(); mMediaBuffers.editItemAt(index) = NULL; } mInputBufferIsDequeued.editItemAt(index) = true; if (!mCSDsToSubmit.isEmpty()) { sp<AMessage> msg = new AMessage(); msg->setSize("buffer-ix", index); sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0); ALOGI("[%s] resubmitting CSD", mComponentName.c_str()); msg->setBuffer("buffer", buffer); mCSDsToSubmit.removeAt(0); if (!onInputBufferFetched(msg)) { handleError(UNKNOWN_ERROR); return false; } return true; } while (!mPendingInputMessages.empty()) { sp<AMessage> msg = *mPendingInputMessages.begin(); if (!onInputBufferFetched(msg)) { // 这里是完成数据填充的地方 break; } mPendingInputMessages.erase(mPendingInputMessages.begin()); } if (!mInputBufferIsDequeued.editItemAt(index)) { return true; } mDequeuedInputBuffers.push_back(index); onRequestInputBuffers(); return true; }
那么看看onInputBufferFetched的实现,这里会操作MediaCodec:
bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) { size_t bufferIx; CHECK(msg->findSize("buffer-ix", &bufferIx)); CHECK_LT(bufferIx, mInputBuffers.size()); sp<ABuffer> codecBuffer = mInputBuffers[bufferIx]; sp<ABuffer> buffer; bool hasBuffer = msg->findBuffer("buffer", &buffer); if (buffer == NULL /* includes !hasBuffer */) { int32_t streamErr = ERROR_END_OF_STREAM; CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); CHECK(streamErr != OK); // attempt to queue EOS,通知MediaCodec结束标志EOS status_t err = mCodec->queueInputBuffer( bufferIx, 0, 0, 0, MediaCodec::BUFFER_FLAG_EOS); if (err == OK) { mInputBufferIsDequeued.editItemAt(bufferIx) = false; } else if (streamErr == ERROR_END_OF_STREAM) { streamErr = err; // err will not be ERROR_END_OF_STREAM } if (streamErr != ERROR_END_OF_STREAM) { handleError(streamErr); } } else { sp<AMessage> extra; if (buffer->meta()->findMessage("extra", &extra) && extra != NULL) { int64_t resumeAtMediaTimeUs; if (extra->findInt64("resume-at-mediaTimeUs", &resumeAtMediaTimeUs)) { mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs; } } int64_t timeUs = 0; uint32_t flags = 0; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); int32_t eos, csd; // we do not expect SYNCFRAME for decoder if (buffer->meta()->findInt32("eos", &eos) && eos) { flags |= MediaCodec::BUFFER_FLAG_EOS; } else if (buffer->meta()->findInt32("csd", &csd) && csd) { flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG; } // copy into codec buffer if (buffer != codecBuffer) { if (buffer->size() > codecBuffer->capacity()) { handleError(ERROR_BUFFER_TOO_SMALL); mDequeuedInputBuffers.push_back(bufferIx); return false; } codecBuffer->setRange(0, buffer->size()); memcpy(codecBuffer->data(), buffer->data(), buffer->size()); // 实际复制数据的地方 } // 实际交给解码器解码的位置 status_t err = mCodec->queueInputBuffer( bufferIx, codecBuffer->offset(), codecBuffer->size(), timeUs, flags); if (err != OK) { if (mediaBuffer != NULL) { mediaBuffer->release(); } handleError(err); } else { mInputBufferIsDequeued.editItemAt(bufferIx) = false; if (mediaBuffer != NULL) { CHECK(mMediaBuffers[bufferIx] == NULL); mMediaBuffers.editItemAt(bufferIx) = mediaBuffer; } } } return true; }
这样解码前的数据准备就完成了。
解码后的数据处理函数是handleAnOutputBuffer,响应MediaCodec::CB_OUTPUT_AVAILABLE消息,代码如下:
bool NuPlayer::Decoder::handleAnOutputBuffer(size_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags) { sp<ABuffer> buffer; mCodec->getOutputBuffer(index, &buffer); if (index >= mOutputBuffers.size()) { for (size_t i = mOutputBuffers.size(); i <= index; ++i) { mOutputBuffers.add(); } } mOutputBuffers.editItemAt(index) = buffer; buffer->setRange(offset, size); buffer->meta()->clear(); buffer->meta()->setInt64("timeUs", timeUs); bool eos = flags & MediaCodec::BUFFER_FLAG_EOS; // we do not expect CODECCONFIG or SYNCFRAME for decoder sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this); reply->setSize("buffer-ix", index); reply->setInt32("generation", mBufferGeneration); if (eos) { buffer->meta()->setInt32("eos", true); reply->setInt32("eos", true); } else if (mSkipRenderingUntilMediaTimeUs >= 0) { if (timeUs < mSkipRenderingUntilMediaTimeUs) { reply->post(); return true; } mSkipRenderingUntilMediaTimeUs = -1; } mNumFramesTotal += !mIsAudio; // wait until 1st frame comes out to signal resume complete notifyResumeCompleteIfNecessary(); if (mRenderer != NULL) { // send the buffer to renderer. 这里是将缓冲区给Renderer处理 mRenderer->queueBuffer(mIsAudio, buffer, reply); if (eos && !isDiscontinuityPending()) { mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM); } } return true; }
还有最后一个消息MediaCodec::CB_OUTPUT_AVAILABLE,处理函数是handleOutputFormatChange,代码如下:
void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) { if (!mIsAudio) { int32_t width, height; if (format->findInt32("width", &width) && format->findInt32("height", &height)) { mStats->setInt32("width", width); mStats->setInt32("height", height); } sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatVideoSizeChanged); notify->setMessage("format", format); notify->post();// 通知NuPlayer视频分辨率改变 } else if (mRenderer != NULL) { // 音频的情况,重置AudioSink uint32_t flags; int64_t durationUs; bool hasVideo = (mSource->getFormat(false /* audio */) != NULL); if (getAudioDeepBufferSetting() // override regardless of source duration || (!hasVideo && mSource->getDuration(&durationUs) == OK && durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US)) { flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; } else { flags = AUDIO_OUTPUT_FLAG_NONE; } status_t err = mRenderer->openAudioSink( format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */); if (err != OK) { handleError(err); } } }
还剩最后一部分,显示是如何处理的。那就是handleAnOutputBuffer发送的一个kWhatRenderBuffer消息的处理,代码如下:
case kWhatRenderBuffer: { if (!isStaleReply(msg)) { onRenderBuffer(msg); } break; } void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) { status_t err; int32_t render; size_t bufferIx; int32_t eos; CHECK(msg->findSize("buffer-ix", &bufferIx)); if (!mIsAudio) { int64_t timeUs; sp<ABuffer> buffer = mOutputBuffers[bufferIx]; buffer->meta()->findInt64("timeUs", &timeUs); } // 显示和释放缓冲 if (msg->findInt32("render", &render) && render) { int64_t timestampNs; CHECK(msg->findInt64("timestampNs", ×tampNs)); err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs); } else { mNumOutputFramesDropped += !mIsAudio; err = mCodec->releaseOutputBuffer(bufferIx); } if (err != OK) { handleError(err); } if (msg->findInt32("eos", &eos) && eos && isDiscontinuityPending()) { finishHandleDiscontinuity(true /* flushOnTimeChange */); } }
至此整个流程分析完毕。
6 总结
回顾下,本文主要介绍了NuPlayer::DecoderBase及其子类NuPlayer::Decoder的实现代码,从demuxer之后的数据,到通过MediaCodec解码,得到原始数据,之后渲染。作为后续学习NuPlayer代码的参考部分。
相关文章推荐
- ⑥NuPlayer播放源码分析之DecoderBase分析 NuPlayer播放源码分析之DecoderBase分析
- ⑤NuPlayer播放框架之GenericSource源码分析
- ⑤NuPlayer播放框架之GenericSource源码分析
- ④NuPlayer播放框架之Renderer源码分析
- ④NuPlayer播放框架之Renderer源码分析
- muduo_base 源码分析:Timestamp
- [libevent源码分析] event_base_dispatch
- Yii2.0源码分析之——YiiBase自动加载类、引用文件(autoload)
- Chromium源码--视频播放流程分析(拨开云雾)
- MySQL源码分析:Innobase字典管理及索引
- Netty 源码分析之ByteToMessageDecoder
- Android base-adapter-helper 源码分析与扩展
- base源码分析(一):客户端数据入库
- BaseHTTPServer与CGIHTTPServer源码分析 - 技术手札
- Android万能适配器base-adapter-helper的源码分析
- Libevent源码分析-event_base
- Android base-adapter-helper 源码分析与扩展
- Netty源码分析之handler decoder
- Jamendo开源在线音乐播放器源码分析播放界面布局
- Libevent源码分析-----配置event_base