PVPlayer的实现方式

PVPlayer的实现方式关于opencore下多媒体播放,在mediaserver进程里面仅仅有一行代码:MediaPlayerService::instantiate();这行代码的作用是初始化一个MediaPlayerS

大家好,又见面了,我是你们的朋友全栈君。

关于opencore下多媒体播放,在mediaserver进程里面仅仅有一行代码:

MediaPlayerService::instantiate();

这行代码的作用是初始化一个MediaPlayerService类的实例,并接把他增加到系统的serveceManager中。

MediaPlayerService的详细实如今目录frameworks/base/media/libmediaplayerservice中。

在涉及到要播放一个详细的媒体文件时,调用的函数是:

sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)
{
    int32_t connId = android_atomic_inc(&mNextConnId);
    sp<Client> c = new Client(this, pid, connId, client);
    LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);
    if (NO_ERROR != c->setDataSource(url))
    {
        c.clear();
        return c;
    }
    wp<Client> w = c;
    Mutex::Autolock lock(mLock);
    mClients.add(w);
    return c;
}

这个new 了一个Client 而且函数将它返回为sp<IMediaPlayer>。

Client对象什么在文件MediaPlayerService.h中,而且是private类,说明它仅仅被MediaPlayerService对象使用。Client对象继承自BnMediaPlayer,而BnMediaPlaye又继承自BnInterface<IMediaPlayer>,看来是用来响应binder的IPC的函数。

而IMediaPlayer又是何许东东。

IMediaPlayer.cpp在目录frameworks/base/media/中,而IMediaPlayer.h 在目录frameworks/base/include/media中。IMediaPlayer.h中声明了一个IMediaPlayer的类,而它的函数又都是virtual,一看就是用来申明接口的。

MediaPlayerService::create函数调用以后,立即调用Client:setDataSource,事实上如今MediaPlayerService.cpp中,

status_t MediaPlayerService::Client::setDataSource(const char *url)
{
    if (strncmp(url, "content://", 10) == 0) {      //不太明确,留着以后在研究吧
        // get a filedescriptor for the content Uri and
        // pass it to the setDataSource(fd) method
        String16 url16(url);
        int fd = android::openContentProviderFile(url16);
        if (fd < 0)
        {
            LOGE("Couldn't open fd for %s", url);
            return UNKNOWN_ERROR;
        }
        setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
        close(fd);
        return mStatus;
    } else {
        player_type playerType = getPlayerType(url);  //通过url来取得playertype,比如对于midi,就用SONIVOX_PLAYER,mp3,mp4等就用PVPLAYER
        LOGV("player type = %d", playerType);
        // create the right type of player
        sp<MediaPlayerBase> p = createPlayer(playerType);  //依据不同的playertype来创建不同的player实例
        if (p == NULL) return NO_INIT;
        if (!p->hardwareOutput()) {
            mAudioOutput = new AudioOutput();
            static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
        }
        // now set data source
        LOGV(" setDataSource");
        mStatus = p->setDataSource(url);    
        if (mStatus == NO_ERROR) {
            mPlayer = p;
        } else {
            LOGE("  error: %d", mStatus);
        }
        return mStatus;
    }
}

注意这行代码:sp<MediaPlayerBase> p = createPlayer(playerType);

他的作用是依据不同的playerType来创建player实例,我们这里主要关注PVPlayer。说了这么多,最终到达opencore那一层了。有时候会认为android这种设计实在太复杂了,调用起来太麻烦,直接实现一个IMediaPlayer的类不就完了吗。可是细致一样,androide的这种设计方式事实上有它在扩展性和可维护性上才这样做的。说究竟就是一句话,减少模块间的耦合性。

涉及到详细的操作,都是通过实现一个接口类来实现,这样详细的实例在创建的时候就能够通过工厂模式来简单的进行扩展。比如上面所提到的createPlayer(playerType)这个函数,当你须要加入自己的特殊格式的播放器的时候,就不用来改它本来的代码,而仅仅用在createPlayer(playerType)的实现以下几行代码:

case XXX_PLAYER:
     LOGV(" create XXXFile");
     p = new XXXPlayer();
     break;

很方便,而这样的扩展性和是由接口和实现的分离带来的,createPlayer 返回的是sp<MediaPlayerBase>类,而去看这个类的代码,发现这个类都是由virtual函数组成的,当你要实现详细实现时候,你能够继承它,然后写好这些virtual函数的实现。

扯远了。设计模式的厉害,可能须要我花整个的职业生涯来体会。

废话不多说,让我们来看PVPlayer的实现,我刚才说过,PVPlayer才是opencore真正的内容。

PVPlayer的申明在frameworks/base/include/media/PVPlayer.h中,而实如今external/opencore/android/playerdriver.cpp。

为什么要这样做?我不懂,我推測还是为了实现和接口的分离,仅仅只是这次的分离就仅仅能简单的通过把头文件和实现文件放到不同目录下来实现。

OK,那么让我们来看看PVPlayer是干嘛的。

PVPlayer继承自MediaPlayerInterface,而MediaPlayerInterface是Opencore对媒体播放的抽象接口的声明类。

那么我们去看看PVPlayer的各个接口的实现吧。PVPlayer的非常多借口都是通过向它的成员mPlayerDriver发送命令来实现,而mPlayerDriver通过调用mPVPlayer的sendEvent函数来告诉PVPlayer,命令是否运行成功了。PlayerDriver是PVPlayer的一个内部成员,它是PVPlayer的命令运行者。

sendEvent的实现例如以下:

void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }

MediaPlayerBase::sendEvent的实现例如以下:

virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }  

mNotify是MediaPlayerBase的一个成员变量,它是一个函数指针,原型例如以下:

typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2);

这个成员变量在调用createPlayer的时候就调用setNotifyCallback来赋值的。

所以,能够看到,底层的事件会一层一层的往上调用,直至返回给用户层。

这里涉及到2个设计模式:

命令模式和观察者模式。

这两个模式都是设计模式中的基本模式之中的一个,功能强大,逻辑清晰。详细的内容不是本文的重点,在此略过。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/155305.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java导出pdf模板_java模板导出PDF[通俗易懂]

    java导出pdf模板_java模板导出PDF[通俗易懂]本次完善综合特点:一对一,点对点的给对应的地方写值,比如模板里面放了个name标识,在程序里把“张三”赋给name,那么输出的pdf里面name的地方就变成了张三,准确方便快捷支持中文,可以使用自己下载的字体。支持图片:图片的大小范围可以在模板随意调,生成出来的图片不会超过范围。而且不需要根据坐标去算,程序里面自动计算的。支持多页模板,即使是好几页的模板,只要每个变量对应的范围确定好了,生成出来的…

    2022年5月10日
  • C语言中的LARGE_INTEGER

    C语言中的LARGE_INTEGERLARGE_INTEGER是一个union:用于表示一个有符号整数值。结构定义如下typedefunion_LARGE_INTEGER{    struct{             DWORDLowPart,            LONG HighPart             };   LONGLONGQuadPart}LARGE_INTEGE

    2022年7月25日
  • tensorflow2.0手写数字识别_tensorflow手写数字识别

    tensorflow2.0手写数字识别_tensorflow手写数字识别本节笔记作为Tensorflow的HelloWorld,用MNIST手写数字识别来探索Tensorflow。笔记的内容来自Tensorflow中文社区和黄文坚的《Tensorflow实战》,只作为自己复习总结。

    2022年9月14日
  • 小菜鸟 学MQ(二)

    小菜鸟 学MQ(二)

    2021年8月25日
  • 华为私有云的搭建方案_如何搭建私有云

    华为私有云的搭建方案_如何搭建私有云最近有个Team共享文档的需求。包括大文件,有个access文件超过500m。开始用百度网盘,发现只能分享,不能共享。试了几个其它网盘,广告超级多,但是都没有共享这个功能。基于此,找了几个私有云程序。最后选择了可道云。完美解决云共享。分享一下自己采坑经验:1.NextCloud支持Windows,Mac,Linux,iOS,Android平台,是OwnCloud的衍生版本,很不错的产…

    2022年6月16日
  • SVPWM分析、各个扇区详细计算以及Matlab仿真「建议收藏」

    SVPWM分析、各个扇区详细计算以及Matlab仿真「建议收藏」SVPWM分析以及各个扇区详细计算以及Matlab仿真说明目的两电平逆变器拓扑空间矢量扇区判断扇区内如何发波?矢量作用时间计算矢量切换时间的计算Simulink仿真/基于Matlab-R2014a版本说明第一次发文章,不太会用Markdown,word编辑的公式复制过来有些是乱码,因此都贴图了。另外,本文公式和图片比较多,编辑工作量比较大,比较辛苦,转载的话,请注明出处,谢谢。目的由于在…

    2022年6月4日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号