Android MediaCodec(二)SimpleDecoder

从这一章开始,我们会基于同步SimplePlayer改造出一个简单的异步播放器(AsyncSimplePlayer),通过一步步的代码实现,我们能够更清晰地学习NuPlayer的实现方式。我们将从简单的框架实现入手,根据需求逐步分析并完善我们的AsyncSimplePlayer。在本节中,我们会先简要介绍播放器所需的模块及其作用,然后开始实现简单解码器模块。

1、简单框架

一个Player可以看作由四部分组成,分别是:

  • Decoder:封装MediaCodec API,向Player提供编解码功能;
  • Renderer:提供Avsync以及输出数据渲染的功能;
  • Source:提供数据读取,demux功能;
  • Controller:AsyncSimplePlayer主体,向上层提供API,用于控制Decoder、Renderer、Source协同工作,完成播放功能。

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主体内容进行填充。

2、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

扫描下方二维码,关注公众号《青山渺渺》阅读音视频开发内容。

你可能感兴趣的:(Android,Media框架剖析,android,MediaCodec,音视频)