Android 11(R) MultiMedia(十六)MediaCodec异步模式实现一个简易播放器

MyDecoderBase.h

#ifndef __MY_DECODERBASE_H__
#define __MY_DECODERBASE_H__

#include <media/stagefright/foundation/AHandler.h>

namespace android {
    
struct AMessage;

class MyDecoderBase : public AHandler {
    
public:
    MyDecoderBase();
    
    status_t setDataSource(const char* path);
    status_t prepare();
    status_t start();
    status_t stop();
    status_t reset();
    
    class MyDecoderCBHandler : public AHandler
    {
    public:
        MyDecoderCBHandler(sp<MyDecoderBase> decoder);
    protected:
        ~MyDecoderCBHandler();
        virtual void onMessageReceived(const sp<AMessage> &msg);
    private:
        wp<MyDecoderBase> mDecoder;
    };
    
protected:

    enum State{
        UNINITIALIZED,
        UNPREPARED,
        STOPPED,
        STARTED,
    };    
    
    enum {
        kWhatSetDataSource = 0,
        kWhatSetSurface,
        kWhatPrepare,
        kWhatStart,
        kWhatStop,
        kWhatReset,
    };    
        
    enum {
        kWhatCodecNotify,
    };

    virtual ~MyDecoderBase();
    virtual void onMessageReceived(const sp<AMessage> &msg);
    
    virtual status_t onSetDataSource(const char* path) = 0;
    virtual status_t onPrepare() = 0;
    virtual status_t onStart() = 0;
    virtual status_t onStop() = 0;
    virtual status_t onReset() = 0;

    State mState;

    friend class DecoderCBHandler;
    // 声明友元让callback可以调用处理函数
    virtual void handleInputBuffer(int32_t index) = 0;
    virtual void handleOutputBuffer(int32_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags) = 0;
    virtual void handleOutputFormatChange(const sp<AMessage> &format) = 0;
    
private:
    
};

}

#endif

 

MyDecoderBase.cpp

//#define LOG_NODEBUG 0
#define LOG_TAG "MyAsyncPlayer"

#include "MyDecoderBase.h"
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/foundation/ADebug.h>

namespace android {

MyDecoderBase::MyDecoderCBHandler::MyDecoderCBHandler(sp<MyDecoderBase> decoder)
{
    // callback中的decoder对象是一个弱引用
    mDecoder = decoder; 
}

MyDecoderBase::MyDecoderCBHandler::~MyDecoderCBHandler()
{
    mDecoder = NULL;
}

// 处理MediaCodec的回调消息
void MyDecoderBase::MyDecoderCBHandler::onMessageReceived(const sp<AMessage> &msg)
{
    switch(msg->what())
    {
        case kWhatCodecNotify:
        {
            int32_t callbackID;
            CHECK(msg->findInt32("callbackID", &callbackID));
            // 回调消息通过callback id来区分具体回调内容
            switch(callbackID)
            {
                case MediaCodec::CB_INPUT_AVAILABLE:
                {
                    int32_t index;
                    CHECK(msg->findInt32("index", &index));
                    sp<MyDecoderBase> decoder = mDecoder.promote();
                    // 调用子类实现 handleInputBuffer 处理InputBuffer
                    if(decoder.get() != NULL)
                        decoder->handleInputBuffer(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));
                    sp<MyDecoderBase> decoder = mDecoder.promote();
                    // 调用子类实现 handleOutputBuffer 处理InputBuffer
                    if(decoder.get() != NULL)
                        decoder->handleOutputBuffer(index, offset, size, timeUs, flags);
                    break;
                }
                case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
                {
                    sp<AMessage> format;
                    CHECK(msg->findMessage("format", &format));
                    sp<MyDecoderBase> decoder = mDecoder.promote();
                    // 调用子类的handleOutputFormatChange来处理格式变化
                    if(decoder.get() != NULL)
                        decoder->handleOutputFormatChange(format);
                    break;                    
                }
                default:
                    break;
            }
            break;
        }
        default:
            break;
    }
}


void MyDecoderBase::onMessageReceived(const sp<AMessage> &msg)
{
    switch(msg->what())
    {
        case kWhatSetDataSource:
        {
            CHECK_EQ(mState, UNINITIALIZED);
            AString path;
            CHECK(msg->findString("path", &path));
            onSetDataSource(path.c_str());
            mState = UNPREPARED;            
            break;
        }
        case kWhatPrepare:
        {
            CHECK_EQ(mState, UNPREPARED);
            onPrepare();
            mState = STOPPED;
            break;
        }
        case kWhatStart:
        {
            CHECK_EQ(mState, STOPPED);
            onStart();
            mState = STARTED;
            break;
        }
        case kWhatStop:
        {
            CHECK_EQ(mState, STARTED);
            onStop();
            mState = STOPPED;
            break;
        }
        case kWhatReset:
        {
            status_t err = OK;
            // 状态为开始,需要先stop再reset
            if(mState == STARTED)
            {
                err = onStop();
                CHECK_EQ(err, OK);
                err = onReset();
                CHECK_EQ(err, OK);
                mState = UNINITIALIZED;
            }
            else if(mState == STOPPED)
            {
                // 如果是在停止状态,直接reset就行了
                err = onReset();
                CHECK_EQ(err, OK);
                mState = UNINITIALIZED;
            }
            break;
        }
        default:
            break;        
    } 
}

MyDecoderBase::MyDecoderBase()
    :mState(UNINITIALIZED)
{
    
}

MyDecoderBase::~MyDecoderBase()
{
    
}

status_t MyDecoderBase::setDataSource(const char* path)
{
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
    msg->setString("path", path);
    msg->post();
    return OK;    
}

status_t MyDecoderBase::prepare()
{
    sp<AMessage> msg = new AMessage(kWhatPrepare, this);
    msg->post();
    return OK;
}
status_t MyDecoderBase::start()
{
    sp<AMessage> msg = new AMessage(kWhatStart, this);
    msg->post();
    return OK;    
}

status_t MyDecoderBase::stop()
{
    sp<AMessage> msg = new AMessage(kWhatStop, this);
    msg->post();
    return OK;    
}

status_t MyDecoderBase::reset()
{
    sp<AMessage> msg = new AMessage(kWhatReset, this);
    msg->post();
    return OK;
}

}

 

MyVideoDecoder.h

#ifndef __MY_VIDEODECODER_H__
#define __MY_VIDEODECODER_H__

#include "MyDecoderBase.h"

namespace android {
    
class IGraphicBufferProducer;
class Surface;
struct NuMediaExtractor;
struct ALooper;
struct MediaCodec;
struct ABuffer;

class MyVideoDecoder : public MyDecoderBase {
    
public:
    MyVideoDecoder();
    
    status_t setSurface(const sp<IGraphicBufferProducer> &bufferProducer);
    
protected:
    virtual ~MyVideoDecoder();
    virtual void onMessageReceived(const sp<AMessage> &msg);
    virtual status_t onSetDataSource(const char* path);
    virtual status_t onPrepare();
    virtual status_t onStart();
    virtual status_t onStop();
    virtual status_t onReset();
    virtual void handleInputBuffer(int32_t index);
    virtual void handleOutputBuffer(int32_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags);
    virtual void handleOutputFormatChange(const sp<AMessage> &format);
    
private:
    
    sp<Surface> mSurface;

    AString mPath;
    sp<NuMediaExtractor> mExtractor;
    sp<MediaCodec> mCodec;
    sp<ALooper> mCodecLooper;
    Vector<sp<ABuffer>> mCSD;        
    
    sp<AMessage> mCallback;
    sp<MyDecoderCBHandler> mDecoderCB;
    sp<ALooper> mDecoderCBLooper;
    
    bool mReachToEos;
    int64_t mStartTimeUs;
    
};

}

#endif

 

MyVideoDecoder.cpp

//#define LOG_NODEBUG 0
#define LOG_TAG "MyVideoDecoder"

#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/NuMediaExtractor.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/MediaCodecBuffer.h>
#include <gui/Surface.h>
#include <media/IMediaHTTPService.h>
#include <mediadrm/ICrypto.h>

#include "MyVideoDecoder.h"

namespace android {

// 初始化列表需要和头文件中的顺序相同
MyVideoDecoder::MyVideoDecoder():
    mSurface(NULL),
    mPath(NULL),
    mExtractor(NULL),
    mCodec(NULL),
    mCodecLooper(NULL),
    mCallback(NULL),
    mDecoderCB(NULL),
    mDecoderCBLooper(NULL),
    mReachToEos(false),
    mStartTimeUs(0)
{
    
}

MyVideoDecoder::~MyVideoDecoder()
{
    
}

void MyVideoDecoder::onMessageReceived(const sp<AMessage> &msg)
{
    switch(msg->what())
    {
        case kWhatSetSurface:
        {
            CHECK_EQ(mState, UNPREPARED);
            sp<RefBase> object;
            CHECK(msg->findObject("surface", &object));
            mSurface = static_cast<Surface*>(object.get()); 
            break;
        }
        default:
            // 除了setSurface,其他的消息都交由父类的onMessageReceived处理
            MyDecoderBase::onMessageReceived(msg);
            break;
        
    }
}

status_t MyVideoDecoder::setSurface(const sp<IGraphicBufferProducer> &bufferProducer)
{
    ALOGD("setSurface");
    sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
    
    sp<Surface> surface;
    if(bufferProducer != NULL)
        surface = new Surface(bufferProducer);
    
    msg->setObject("surface", surface);
    msg->post();
    return OK;
}

status_t MyVideoDecoder::onSetDataSource(const char* path)
{
    mPath = path;
    ALOGD("onSetDataSource path = %s", mPath.c_str());
    return OK;
}

status_t MyVideoDecoder::onPrepare()
{
    ALOGD("onPrepare");
    mExtractor = new NuMediaExtractor();
    
    status_t err = mExtractor->setDataSource(NULL, mPath.c_str());
    
    CHECK_EQ(err, OK);
    
    
    bool haveVideo = false;
    for(size_t i = 0; i < mExtractor->countTracks(); i++)
    {
        sp<AMessage> format;
        err = mExtractor->getTrackFormat(i, &format);
        CHECK_EQ(err, OK);
        
        AString mime;
        CHECK(format->findString("mime", &mime));
        
        // 只查找一个video track
        if(!haveVideo && !strncasecmp(mime.c_str(), "video/", 6))
            haveVideo = true;
        else
            continue;
        
        // 选择video track
        err = mExtractor->selectTrack(i);
        CHECK_EQ(err, OK);
        
        mCodecLooper = new ALooper;
        mCodecLooper->start();
        mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false);
        CHECK(mCodec != NULL);
        
        // 创建callback handler
        mDecoderCB = new MyDecoderCBHandler(this);
        // 创建callback looper
        mDecoderCBLooper = new ALooper;
        mDecoderCBLooper->start();
        mDecoderCBLooper->registerHandler(mDecoderCB);
        // 创建callback message,what为kWhatCodecNotify(这个名字可以任意)
        mCallback = new AMessage(kWhatCodecNotify, mDecoderCB);
        // 给mediacodec注册callback,开启异步模式
        err = mCodec->setCallback(mCallback);
        CHECK_EQ(err, OK);
        
        err = mCodec->configure(format, mSurface, NULL, 0);
        CHECK_EQ(err, OK);
        
        size_t j = 0;
        sp<ABuffer> buffer;
        // csd buffer暂时没有送给decoder
        while(format->findBuffer(AStringPrintf("csd-%d", j).c_str(), &buffer))
        {
            mCSD.push_back(buffer);
            j++;
        }
    }
    
    CHECK_EQ(haveVideo, true);
    return OK;
}

status_t MyVideoDecoder::onStart()
{
    // 开始解码
    status_t err = mCodec->start();
    CHECK_EQ(err, OK);
    return OK;
}

status_t MyVideoDecoder::onStop()
{
    // 停止MediaCodec的回调消息处理
    status_t err = mDecoderCBLooper->stop();
    // 调用mediacodec 的 stop方法,关闭codec,
    if(err == OK)
        err = mCodec->stop();
    else
        err = mCodec->reset();
    // mediacodec的reset方法会先调用release
    CHECK_EQ(err, OK);
    return OK;
}

status_t MyVideoDecoder::onReset()
{
    status_t err = OK;
    if(mCodec != NULL)
    {
        err = mCodec->release();
        CHECK_EQ(err, OK);
        // clear方法来减少强引用计数
        mCodec.clear();
        mCodec = NULL;
    }
    if(mExtractor != NULL)
    {
        mExtractor.clear();
        mExtractor = NULL;
    }
    if(mCodecLooper != NULL)
    {
        mCodecLooper.clear();
        mCodecLooper = NULL;
    }
    if(mDecoderCBLooper != NULL)
    {
        if(mDecoderCB != NULL)
        {
            mDecoderCBLooper->unregisterHandler(mDecoderCB->id());
            mDecoderCB.clear();
            mDecoderCB = NULL;
        }
        mDecoderCBLooper.clear();
        mDecoderCBLooper = NULL;
    }
    if(mSurface != NULL)
    {
        mSurface.clear();
        mSurface = NULL;
    }
    
    mReachToEos = false;
    mStartTimeUs = 0ll;
    
    return OK;
}


void MyVideoDecoder::handleInputBuffer(int32_t index)
{
    if(mState == STARTED && !mReachToEos)
    {
        sp<MediaCodecBuffer> buffer;
        status_t err = mCodec->getInputBuffer(index, &buffer);
        CHECK_EQ(err, OK);
        CHECK(buffer != NULL);
        
        sp<ABuffer> abuffer = new ABuffer(buffer->base(), buffer->capacity());
        
        err = mExtractor->readSampleData(abuffer);
        if(err == ERROR_END_OF_STREAM)
        {
            err = mCodec->queueInputBuffer(index, 0, 0, 0, MediaCodec::BUFFER_FLAG_EOS);
            CHECK_EQ(err, OK);
            mReachToEos = true;
        }
        
        buffer->setRange(abuffer->offset(), abuffer->size());
        
        int64_t timeUs;
        err = mExtractor->getSampleTime(&timeUs);
        CHECK_EQ(err, OK);
        
        err = mCodec->queueInputBuffer(index, buffer->offset(), buffer->size(), timeUs, 0);
        
        CHECK_EQ(err, OK);
        err = mExtractor->advance();
        if(err == ERROR_END_OF_STREAM)
            mReachToEos = true;
    }
}

void MyVideoDecoder::handleOutputBuffer(int32_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags)
{
    if(mState == STARTED)
    {
        if(flags == MediaCodec::BUFFER_FLAG_EOS)
        {
            // 收到EOS则停止Looper的工作
            //mState = STOPPED;
            ALOGD("stop looper");
            looper()->stop();
        }
            
        if(mStartTimeUs == 0)
            mStartTimeUs = ALooper::GetNowUs();
        
        // 做一个简单的sync
        int64_t realUs = mStartTimeUs + timeUs;
        int64_t lateUs = ALooper::GetNowUs() - realUs;
        
        if(lateUs > 30000ll)
        {
            ALOGD("buffer late by %lld us, dropped", lateUs);
            mCodec->releaseOutputBuffer(index);
        }
        else if(lateUs < 30000ll && lateUs > -5000ll){
            mCodec->renderOutputBufferAndRelease(index);
        }
        else
            mCodec->renderOutputBufferAndRelease(index, abs(lateUs));
    }
    
}

void MyVideoDecoder::handleOutputFormatChange(const sp<AMessage> &format)
{
    ALOGD("video format changed");
}

}

 

TestAsyncPlayer.cpp

//#define LOG_NDEBUG 0
#define LOG_TAG "TestPlayer"


#include <gui/SurfaceControl.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/Surface.h>
#include <ui/DisplayConfig.h>
#include <media/stagefright/foundation/ALooper.h>
#include <binder/IBinder.h>
#include <media/stagefright/foundation/ADebug.h>

#include "MyVideoDecoder.h"

using namespace android;

sp<MyVideoDecoder> player = new MyVideoDecoder();

// 测试Reset方法
void ResetTest(int signum)
{
    player->reset();
}

int main(int argc, char** argv)
{
    using namespace android;
    
    signal(SIGINT, ResetTest);
    
    //sp<MyVideoDecoder> player = new MyVideoDecoder();
    sp<android::ALooper> looper =  new android::ALooper();    
    // 创建looper处理MyPlayer的消息
    looper->registerHandler(player);
    // 运行在新的线程上
    //looper->start();
    
    
    // 创建Surface
    sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
    CHECK_EQ(composerClient->initCheck(), (status_t)OK);
    
    const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
    CHECK(display != NULL);
    
    DisplayConfig config;
    CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR);
    
    const ui::Size &resolution = config.resolution;
    const ssize_t displayWidth = resolution.getWidth();
    const ssize_t displayHeight = resolution.getHeight();
    ALOGD("display is %d x %d\n", displayWidth, displayHeight);
    
    sp<SurfaceControl> control = composerClient->createSurface(String8("A Surface"), displayWidth/2, displayHeight/2, PIXEL_FORMAT_RGB_565, 0);
    CHECK(control != NULL);
    CHECK(control->isValid());
    
    SurfaceComposerClient::Transaction{}.setLayer(control, INT_MAX).show(control).apply();
    sp<Surface> surface = control->getSurface();
    CHECK(surface != NULL);
    
    // 开始播放
    player->setDataSource(argv[1]);
    player->setSurface(surface->getIGraphicBufferProducer());
    player->prepare();
    player->start();
    
    // sleep 60s,等待播放60s
    //sleep(60);
    // 这里和之前的同步demo不一样,looper执行在工作线程上,会阻塞工作线程
    looper->start(true);
    composerClient->dispose();
    
    looper->stop();
    
    return 0;
    
    
}

 

Android.bp

cc_binary {
    name: "MyAsyncPlayer",
    
    srcs: [
        "TestAsyncPlayer.cpp",
        "MyVideoDecoder.cpp",
        "MyDecoderBase.cpp"
    ],
    
    local_include_dirs: [
        "include"
    ],

    shared_libs: [
        "libstagefright",
        "libmedia",
        "libstagefright_foundation",
        "libgui",
        "libaudioclient",
        "liblog",
        "libutils",
        "libcutils",
        "libmedia_omx",
        "libui",
    ],

    /*export_include_dirs: [
        "include"
    ],*/
    
    export_shared_lib_headers:[
        "libstagefright",
        "libmedia",
    ],
    
    header_libs: [
        "libmediametrics_headers",
        "libmediadrm_headers",
        "libaudioclient_headers"
    ],
    
}

 

编译tips:

1、在头文件中引入class MediaCodec时,在cpp文件中做头文件引用,但是仍然出现Member access into incomplete type,这时候把我们自己的头文件加在头文件最后就可以编译通过了

2、class Surface在引入时没有加入到namespace android当中,造成和test中的surface发生冲突

3、还有一个遗忘的语法问题

sp<MyDecoderBase> player = new MyVideoDecoder();

父类指针指向子类实例,但是并不能调用到setSurface方法,因为父类对象并没有这个方法,要使用可以用两种方法,父类中添加虚函数,实例化时用派生类指针指向实例,要么就直接使用子类指针指向子类示例

posted @ 2022-05-16 13:29  青山渺渺  阅读(576)  评论(0编辑  收藏  举报