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

Android 11(R) MultiMedia(十六)MediaCodec异步模式实现一个简易播放器的更多相关文章

  1. Android 11(R) Power HAL AIDL简析 -- 基本接口

    Android 11(R) Power HAL AIDL将分三篇文章来介绍: Android 11(R) Power HAL AIDL简析 -- 基本接口 Android 11(R) Power HA ...

  2. Android学习总结(十六) ———— MediaPlayer播放音频与视频

    一.基本概念 本文主要介绍的是Android中很重要也最为复杂的媒体播放器(MediaPlayer)部分的架构.Android的MediaPlayer包含了Audio和video的播放功能,在Andr ...

  3. Android学习笔记(十二)——实战:制作一个聊天界面

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 运用简单的布局知识,我们可以来尝试制作一个聊天界面. 一.制作 Nine-Patch 图片 : Nine-Pa ...

  4. 从零开始学习PYTHON3讲义(十四)写一个mp3播放器

    <从零开始PYTHON3>第十四讲 通常来说,Python解释执行,运行速度慢,并不适合完整的开发游戏.随着电脑速度的快速提高,这种情况有所好转,但开发游戏仍然不是Python的重点工作. ...

  5. Android笔记(四十六) Android中的数据存储——XML(二)PULL解析

    PULL 的工作原理: XML pull提供了开始元素和结束元素.当某个元素开始时,可以调用parser.nextText()从XML文档中提取所有字符数据.当解析到一个文档结束时,自动生成EndDo ...

  6. Android笔记(二十六) Android中的广播——BroadcastReceiver

    为了方便进行系统级别的消息通知,Android有一套类似广播的消息机制,每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收自己所关心的广播内容,这些广播可能是来自于系统,也可能是来自于 ...

  7. 第十六章:异步处理之AsyncTask的应用

    前言 我们知道Android的UI主线程主要负责处理用户的按键事件.用户的触屏事件以及屏幕绘图事件等:既然UI老人家都这么忙了,我们这些开发者肯定不能不识趣的去添乱阻塞UI线程什么的,否则UI界面万一 ...

  8. Android笔记(三十六) AsyncTask是如何执行的?

    在上一个例子中,我们是在LoadImage的onPostExecute中修改的UI,不是说只允许在主线程中修改UI吗?我们看一下源代码是如何操作的. MainActicity.java package ...

  9. Android学习笔记(十六)——数据库操作(上)

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! Android 为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, ...

  10. Android项目实战(十六):QQ空间实现(一)—— 展示说说中的评论内容并有相应点击事件

    大家都玩QQ空间客户端,对于每一个说说,我们都可以评论,那么,对于某一条评论: 白雪公主 回复 小矮人 : 你们好啊~ 我们来分析一下: .QQ空间允许我们 点击 回复人和被回复人的名字就可以进入对于 ...

随机推荐

  1. 单机运行环境搭建之 --CentOS-6.4安装MySQL 5.6.10并修改MySQL的root用户密码

    单机运行环境搭建之 --CentOS-6.4安装MySQL 5.6.10并修改MySQL的root用户密码 Mysql 5.5以后使用了CMake进行安装,参考与以前的区别请参考: http://ww ...

  2. redis 简单整理——内存的管理[二十六]

    前言 redis 是一个内存型数据库,那么就需要重点关注一下内存了. 正文 理解Redis内存,首先需要掌握Redis内存消耗在哪些方面.有些内存消 耗是必不可少的,而有些可以通过参数调整和合理使用来 ...

  3. sql 语句系列(每个季度的开始日期和结束日期)[八百章之第二十二章]

    前言 基本上统计财务一定会用到. mysql select QUARTER(ADDDATE(y.dy,-1)) QTR, DATE_ADD(y.dy,INTERVAL -3 MONTH) Q_star ...

  4. docker 应用篇————docker 的文件系统[十]

    前言 简单介绍一下docker的文件系统. 正文 docker 容器启动就是一个文件系统的启动. 在docker中,每一层镜像都具备一些文件. 比如说,有一个centos的镜像. 里面就是一个微小版的 ...

  5. EVA: Visual Representation Fantasies from BAAI

    ​本文做个简单总结,博主不是做自监督领域的,如果错误,欢迎指正. 链接 Code:​ Official:baaivision/EVA MMpretrain:open-mmlab/mmpretrain/ ...

  6. 力扣599(java&python)- 两个列表的最小索引总和(简单)

    题目: 假设 Andy 和 Doris 想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示. 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅. 如果答案不止一 ...

  7. 涨姿势 | 一文读懂备受大厂青睐的ClickHouse高性能列存核心原理

    简介: 本文尝试解读ClickHouse存储层的设计与实现,剖析它的性能奥妙 作者:和君 引言 ClickHouse是近年来备受关注的开源列式数据库,主要用于数据分析(OLAP)领域.目前国内各个大厂 ...

  8. [FAQ] swagger-php 支持 Authorization Bearer token 校验的用法

    @OA\SecurityScheme 可以是 Controller 层面也可以是 Action 层面. 类型 type="apiKey". in="header" ...

  9. 修复 Debian 安装 dotnet 失败 depends on ca-certificates

    本文记录我在 Debian 安装 dotnet 失败,报错信息是 packages-microsoft-prod depends on ca-certificates; however: Packag ...

  10. WPF 界面打不开提示 System.ArithmeticException Overflow or underflow in the arithmetic operation 异常

    本文告诉大家如何解决界面打不开,抛出 System.ArithmeticException: Overflow or underflow in the arithmetic operation 异常的 ...