用android实现GB28181监控客户端app

      武汉一直笼罩在新冠状病毒的疫情中,我不喜欢在朋友圈拿疫情去炒作,但我知道,如果真的智能AI智能识别,智能机器人,自动驾驶,远程分级诊疗等热门技术如果只要有一样能够落地能够应用,武汉的疫情不会如此被动。所以一切不以应用为目的的技术炒作都是耍流氓。

      待在家里无法出门,解决了食物的储备后,无聊至极,于是想着干脆开发GB28181的客户端手机版APP以度过闲暇时光。下面开始言归正传。

      我有过做android解码器的经历,那是在上家公司,当时从0开始,那是android版本主流是4.4.2,刚开始用ffmpeg解码,opengles显示渲染,音频用opensl,native c++实现,后来要多路解码,支持H265解码,使用硬解。公司做军品,硬件固定型号,为了追求低延时直接从源码上修改了编译,基本是针对指定硬件的方案,兼容性差。

      而本次GB28181移动客户端的开发,解码和渲染是少不了的。对于这种手机APP,我首先考虑的是性能和兼容性。而且现在手机版本都到android 10了,所以兼容性十分重要。

      GB28181协议这一块因为之前PC客户端已实现,基于esosip和osp库,用c++开发的,所以直接移植到android版即可,在android上关键需要实现解码和音视频的渲染播放。

      当然我们还是得选择硬解码,android如果硬解码和渲染是一个管线式的流程,可以理解为通过解码后数据块在GPU的显存,通过指针直接丢向ANativeWindow显示,ANativeWindow是natvie c++层的对象,其实它与android java层的surfaceview是关联的,通过如下代码:

private SurfaceView  sfvideoview;
SurfaceHolder sfholder;
long     natviewindow = 0;

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sfvideoview = (SurfaceView) findViewById(R.id.videoView);
        sfholder = sfvideoview.getHolder();
        sfholder.addCallback(new SurfaceHolder.Callback()
        {
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }

            public void surfaceCreated(SurfaceHolder holder) {
                if(natviewindow == 0)
                  natviewindow = GbtClientJni.gbtclient_createnativewindow                       
                  (sfholder.getSurface());
            }
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format,
                                       int width, int height) {
            }
        });
    }    

GbtClientJni.gbtclient_createnativewindow函数就是传的surfaceview的surface,该函数必须在surfaceCreated中,因为该函数属于在glrender线程中,渲染相关流程必须要oepngles的rander线程中。我们把surface传入到native层,通过ANativeWindow_fromSurface函数可以得到ANativeWindow,而在底层,ANativeWindow就是渲染解码数据的载体。

ANativeWindow  *anw = NULL;
anw = ANativeWindow_fromSurface(env, jsurface);

这样就完成了视频渲染的上下层衔接。

     对于目前android音频的播放,好像还是两种方案,audiotrack和opensl,我依然选择使用opensl,因为audiotrack靠近底层了,担心其兼容性。对于接口都很简单,无非都是回调函数,然后往里面塞数据。首先取出解码后的音频数据,可以用ffmpeg解码,然后调用opensl的接口:

SLresult result = (*bq)->Enqueue(bq, m_pAudioBuffer, m_nAudioBufferConsumed);

    基于有PC版GB28181客户端的基础,解决了移动端设备相关的差异性后,GB28181移动客户端就开发完毕了,如下图:

更多信息

e-mail: [email protected]

tel: 13971177602

web:www.founu.com

发布了5 篇原创文章 · 获赞 0 · 访问量 874

猜你喜欢

转载自blog.csdn.net/wangjie_jack/article/details/104634054