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方法,因为父类对象并没有这个方法,要使用可以用两种方法,父类中添加虚函数,实例化时用派生类指针指向实例,要么就直接使用子类指针指向子类示例