从这一章开始,我们会基于同步SimplePlayer改造出一个简单的异步播放器(AsyncSimplePlayer),通过一步步的代码实现,我们能够更清晰地学习NuPlayer的实现方式。我们将从简单的框架实现入手,根据需求逐步分析并完善我们的AsyncSimplePlayer。在本节中,我们会先简要介绍播放器所需的模块及其作用,然后开始实现简单解码器模块。
一个Player可以看作由四部分组成,分别是:
Decoder、Renderer和Source都是在Controller中创建,但是它们不是独立工作的,它们的工作会有耦合,比方说Decoder送回的数据会直接送到Renderer做处理,Decoder会从Source中读取input data。
异步Decoder是通过MediaCodec的异步模式来实现的,以下是一个简单的框架:
// SimpleDecoder.h
#ifndef SIMPLE_DECODER_H
#define SIMPLE_DECODER_H
#include
namespace android {
class SimpleDecoder : public AHandler {
public:
protected:
enum {
kWhatCodecNotify = 'nofy',
};
virtual void onMessageReceived(const sp<AMessage> &msg);
void handleAnInputBuffer(int32_t index);
void handleAnOutputBuffer(int32_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags);
private:
};
}
#endif
从头文件我们可以看到SimpleDecoder继承于AHandler,MediaCodec送回的callback消息最终会在SimpleDecoder实现的onMessageReceived方法中被处理。注册给MediaCodec的消息名为kWhatCodecNotify,callback送回的input buffer会用handleAnInputBuffer来处理,output buffer会由handleAnOutputBuffer处理。
以下是SimpleDecoder.cpp实现的简单消息处理框架:
// SimpleDecoder.cpp
#define LOG_TAG "SimpleDecoder"
#include "SimpleDecoder.h"
namespace android {
void SimpleDecoder::onMessageReceived(const sp<AMessage> &msg) {
switch(msg->what()) {
case kWhatCodecNotify:
int callbackID;
CHECK(msg->findInt32("callbackID", &callbackID));
switch(callbackID) {
case MediaCodec::CB_INPUT_AVAILABLE:
{
int32_t index;
CHECK(msg->findInt32("index", &index));
// write input data
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->findInt64("timeUs", &timeUs));
CHECK(msg->findSize("size", &size));
CHECK(msg->findSize("offset", &offset));
CHECK(msg->findInt32("flags", &flags));
// try render
handleAnOutputBuffer(index, offset, size, timeUs, flags);
break;
}
case MediaCodec::CB_ERROR:
{
// handle error
break;
}
case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
{
// handle output format change
break;
}
default:
break;
}
break;
default:
break;
}
}
SimpleDecoder收到MediaCodec回传的kWhatCodecNotify消息后,要读取AMessage中存储的callbackID,根据id执行对应的操作。
接下来我们会在简单消息处理框架的基础上对SimpleDecoder主体内容进行填充。
我们先给SimpleDecoder实现运行所必要的configure、start和stop方法,其他方法我们后续再做补充和分析。
下面是填充后的SimpleDecoder.h:
// SimpleDecoder.h
#ifndef SIMPLE_DECODER_H
#define SIMPLE_DECODER_H
#include
namespace android {
struct MediaCodec;
struct NuMediaExtractor;
class Surface;
class SimpleDecoder : public AHandler {
public:
SimpleDecoder(const sp<NuMediaExtractor> &extractor, size_t trackIndex);
// init must be called after SimplerDecoder created
void init();
status_t configure(const sp<AMessage> &format, const sp<Surface> &surface);
status_t start();
status_t stop();
protected:
virtual ~SimpleDecoder();
enum {
kWhatCodecNotify = 'nofy',
kWhatConfigure = 'cfig',
kWhatStart = 'strt',
kWhatStop = 'stop',
};
virtual void onMessageReceived(const sp<AMessage> &msg);
status_t onConfigure(const sp<AMessage> &format, const sp<Surface> &surface);
status_t onStart();
status_t onStop();
void handleAnInputBuffer(int32_t index);
void handleAnOutputBuffer(int32_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags);
private:
sp<ALooper> mLooper;
sp<ALooper> mCodecLooper;
sp<MediaCodec> mCodec;
sp<NuMediaExtractor> mExtractor;
bool mVideo;
size_t mTrackIndex;
bool mInputEOS;
bool mStop;
};
}
#endif
下面是填充后的SimpleDecoder.cpp:
// SimpleDecoder.cpp
#define LOG_TAG "SimpleDecoder"
#include "SimpleDecoder.h"
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG
#define DEBUG_PRINT ALOGD
#else
#define DEBUG_PRINT
#endif
namespace android {
static status_t PostAndAwaitResponse(const sp<AMessage> &msg, sp<AMessage> *response) {
status_t err = msg->postAndAwaitResponse(response);
if(err != OK) {
return err;
}
// check response
// if not find err in response, return OK
if(!(*response)->findInt32("err", &err)) {
err = OK;
}
return err;
}
static void PostReplyWithError(const sp<AReplyToken> &replyID, status_t err = OK) {
sp<AMessage> response = new AMessage();
response->setInt32("err", err);
response->postReply(replyID);
}
SimpleDecoder::SimpleDecoder(const sp<NuMediaExtractor> &extractor, size_t trackIndex) {
mLooper = new ALooper();
mLooper->setName("SimpleDecoderLP");
mLooper->start();
mCodecLooper = NULL;
mCodec = NULL;
mExtractor = extractor;
mVideo = false;
mTrackIndex = trackIndex;
mInputEOS = false;
mStop = false;
}
SimpleDecoder::~SimpleDecoder() {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
mLooper->unregisterHandler(id());
mLooper->stop();
}
void SimpleDecoder::init() {
mLooper->registerHandler(this);
}
status_t SimpleDecoder::configure(const sp<AMessage> &format, const sp<Surface> &surface) {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
sp<AMessage> msg = new AMessage(kWhatConfigure, this);
msg->setMessage("format", format);
if(surface == NULL) {
ALOGD("configure surface is NULL");
} else {
ALOGD("configure surface is not NULL");
}
msg->setObject("surface", surface);
sp<AMessage> response;
status_t err = PostAndAwaitResponse(msg, &response);
return err;
}
status_t SimpleDecoder::start() {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
sp<AMessage> msg = new AMessage(kWhatStart, this);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
status_t SimpleDecoder::stop() {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
sp<AMessage> msg = new AMessage(kWhatStop, this);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
void SimpleDecoder::onMessageReceived(const sp<AMessage> &msg) {
switch(msg->what()) {
case kWhatCodecNotify:
if(mStop)
break;
int callbackID;
CHECK(msg->findInt32("callbackID", &callbackID));
switch(callbackID) {
case MediaCodec::CB_INPUT_AVAILABLE:
{
int32_t index;
CHECK(msg->findInt32("index", &index));
// write input data
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->findInt64("timeUs", &timeUs));
CHECK(msg->findSize("size", &size));
CHECK(msg->findSize("offset", &offset));
CHECK(msg->findInt32("flags", &flags));
// try render
handleAnOutputBuffer(index, offset, size, timeUs, flags);
break;
}
case MediaCodec::CB_ERROR:
{
// handle error
break;
}
case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
{
// handle output format change
break;
}
default:
break;
}
break;
case kWhatConfigure:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
sp<RefBase> obj;
msg->findObject("surface", &obj);
sp<Surface> surface = static_cast<Surface *>(obj.get());
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
status_t err = onConfigure(format, surface);
PostReplyWithError(replyID, err);
break;
}
case kWhatStart:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
status_t err = onStart();
PostReplyWithError(replyID, err);
break;
}
case kWhatStop:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
status_t err = onStop();
PostReplyWithError(replyID, err);
break;
}
default:
break;
}
}
status_t SimpleDecoder::onConfigure(const sp<AMessage> &format, const sp<Surface> &surface) {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
ALOGD("format is %s", format->debugString().c_str());
status_t err;
AString mime;
CHECK(format->findString("mime", &mime));
mVideo = !strncasecmp(mime.c_str(), "video/", 6);
mCodecLooper = new ALooper();
mCodecLooper->setName("CodecLooper");
mCodecLooper->start();
mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false);
CHECK(mCodec != NULL);
sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
mCodec->setCallback(reply);
err = mCodec->configure(format, surface, NULL, 0);
CHECK_EQ(err, OK);
return err;
}
status_t SimpleDecoder::onStart() {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
status_t err = mCodec->start();
CHECK_EQ(err, OK);
return err;
}
status_t SimpleDecoder::onStop() {
ALOGD("[%s %d]", __FUNCTION__, __LINE__);
status_t err = mCodec->release();
mCodecLooper->unregisterHandler(mCodec->id());
mCodecLooper->stop();
mCodecLooper.clear();
return err;
}
void SimpleDecoder::handleAnInputBuffer(int32_t index) {
DEBUG_PRINT("[%s %d] index:%d", __FUNCTION__, __LINE__, index);
size_t trackIndex;
status_t err;
while((err = mExtractor->getSampleTrackIndex(&trackIndex)) || trackIndex != mTrackIndex) {
if(err != OK) {
ALOGD("[%s %d] get input eos", __FUNCTION__, __LINE__);
mInputEOS = true;
break;
}
mExtractor->advance();
}
sp<MediaCodecBuffer> codecBuffer;
err = mCodec->getInputBuffer(index, &codecBuffer);
sp<ABuffer> buffer = new ABuffer(codecBuffer->base(), codecBuffer->capacity());
err = mExtractor->readSampleData(buffer);
CHECK_EQ(err, OK);
codecBuffer->setRange(buffer->offset(), buffer->size());
int64_t timeUs;
CHECK_EQ(mExtractor->getSampleTime(&timeUs), OK);
err = mCodec->queueInputBuffer(index, codecBuffer->offset(), codecBuffer->size(), timeUs, 0);
CHECK_EQ(err, OK);
err = mExtractor->advance();
CHECK_EQ(err, OK);
}
void SimpleDecoder::handleAnOutputBuffer(int32_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags) {
DEBUG_PRINT("[%s %d] index:%d", __FUNCTION__, __LINE__, index);
mCodec->renderOutputBufferAndRelease(index);
}
}
原文阅读:
Android MediaCodec(二)SimpleDecoder
扫描下方二维码,关注公众号《青山渺渺》阅读音视频开发内容。