GB28181协议实现源码Android源码

一、GB28181规范

尽管在国标GB28181中并没有对“平台”进行明确的定义,但在规范中却多次提到“系统平台”、“管理平台”等词汇,在具体项目中、网络上的交流学习中,平台概念也是无处不在。笔者认为,GB28181平台就是视频联网系统中的上级平台、中间平台或下级平台,用于实现信令、音视频数据的接收和转发。因此,在一个GB28181平台中至少应包含一个SIP信令服务器和一个流媒体服务器,这两个(类)服务器和摄像头、播放终端等设备一起组成一个监控域。对于绝大多数项目,平台中还应包括针对具体业务的业务平台,如指挥控制中心等,我这里只重点将信令服务器和流媒体服务器。

二、SIP信令服务器

SIP信令服务器负责对平台中的控制指令进行接收和转发,具有向SIP客户端、SIP设备、媒体服务器和网关等提供注册、路由选择以及逻辑控制等功能。具体指令包括注册、实时视频播放、历史视频播放、设备控制、信息查询、报警事件通知等。具体的实现遵循SIP协议规范,会话控制采用IETFRFC3261规定的Register、Invite等请求和响应方法实现,历史视音频回放控制采用SIP扩展协议IETFRFC2976规定的INFO 方法实现,前端设备控制、信息查询、报警事件通知和分发等应用的会话控制采用SIP扩展协议IETFRFC3428规定的Message方法实现。SIP消息的传输支持采用UDP和TCP两种传输协议。

三、流媒体服务器

流媒体服务器的作用是接收和转发音视频流,包括接收监控设备发送携带音视频的数据包,向播放终端或其他平台发送音视频包等。视音频传输协议要求采用RTP协议,视频推荐使用H264编码,音频G711/G723。传输层协议同样最好同时支持UDP协议和TCP协议,UDP协议带来更好的实时性和更低的延迟,TCP协议则提供更可靠的传输。

尽管标准对视频的上行和下行(播放)都明确定义要RTP协议,但是在实际应用中存在向各种终端(手机、浏览器)、各种业务平台转发音视频的需求,因此流媒体服务器的实现应当提供更多的灵活性,在采用RTP协议接收和转发音视频的同时,应当支持更多播放协议,以支持更广泛的终端集成播放需求。一些流媒体服务器平台,如云视睿博的NTV Media Server G3和SRS项目,都在支持RTP协议的同时提供了更多的播出协议支持。

四、GB28181协议在Android上的实现

笔者将相关实现代码封装到了一个单独的SDK中,此SDK包含信令通信、流媒体传输的实现接口,调用SDK的相关功能即可实现。下面的示例代码AvcEncoder就是一个调用示例,我们在App设置界面配置好参数,然后打开视频通话,接着App会注册到平台上,在平台上点击“播放实时”,就可以实时预览摄像头的画面了。

示例代码如下(具体实现代码可私聊我要哦!)


public class AvcEncoder {
    public static final String VIDEO_MIME_TYPE = "video/avc"; // h264
    private Context context;
    private MediaCodec mediaCodec;
    private MediaFormat mediaFormat;

    private byte[] netPpsSps = null;
    private byte[] outData = null;
    private byte[] yv12data = null;

    private int m_width;
    private int m_height;
    private byte[] m_info = null;
    private byte[] yuv420 = null;

    private byte csd0[] = new byte[]{0, 0, 0, 1, 0x67, 0x64, 0, 0x29,
            (byte) 0xac, 0x1b, 0x1a, (byte) 0x80, 0x78, 0x02, 0x27,
            (byte) 0xe5, (byte) 0x80, 0x78, 0x44, 0x22, (byte) 0x9c};
    private byte csd1[] = new byte[]{0, 0, 0, 1, 0x68, (byte) 0xEA, 0X43, (byte) 0XCB};

    private MediaCodecInfo selectCodecInfo() {
        int numCodecs = MediaCodecList.getCodecCount();
        for (int i = 0; i < numCodecs; i++) {
            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
            if (!codecInfo.isEncoder()) {
                continue;
            }
            String[] types = codecInfo.getSupportedTypes();
            for (int j = 0; j < types.length; j++) {
                if (types[j].equalsIgnoreCase(VIDEO_MIME_TYPE)) {
                    return codecInfo;
                }
            }
        }
        return null;
    }

    public AvcEncoder(Context context, int width, int height, int frameRate, int bitrate) {
        this.context = context;
        m_width = width;
        m_height = height;
        yuv420 = new byte[width * height * 3 / 2];

        MediaCodecInfo mcif = selectCodecInfo();
        try {
            if (mcif != null) {
                mediaCodec = MediaCodec.createByCodecName(mcif.getName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
        // mediaFormat.setInteger(MediaFormat.KEY_ROTATION, 270);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
        mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(csd0));
        mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(csd1));

        try {
            if (mediaCodec != null) {
                mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                mediaCodec.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void yuvNV21ToYV12(byte[] NV21,int w,int h,byte[] YV12) {
        System.arraycopy(NV21,0,YV12,0,w * h);

        for(int i = 0, j = 0; i < w * h / 4; i++, j += 2) {
            System.arraycopy(NV21, w*h+j ,YV12,w * h + i + w * h / 4,1);
            System.arraycopy(NV21,w * h + j + 1,YV12,w * h + i,1);
        }
    }

    public int offerEncoder(final byte[] input) {
        int pos = 0;

        if (mediaCodec == null) {
            return pos;
        }

        if (yv12data == null || yv12data.length != input.length) {
            yv12data = new byte[input.length];
        }

        yuvNV21ToYV12(input,m_width,m_height,yv12data);
        long time = System.nanoTime();

        long t1 = System.currentTimeMillis();
        try {
            ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
            ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(10000000);
            ByteBuffer inputBuffer;
            if (inputBufferIndex >= 0) {
                inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                inputBuffer.put(yv12data);
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, yv12data.length, time, 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);

            if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                final MediaFormat vFormat = mediaCodec.getOutputFormat(); // API >= 16
            }

            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    netPpsSps = new byte[bufferInfo.size];
                    outputBuffer.get(netPpsSps,0,bufferInfo.size);
                    outputBuffer.clear();

                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
                    continue;
                }

                if((bufferInfo.flags &  MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { // I Frame
                    if (outData == null || outData.length < bufferInfo.size + netPpsSps.length) {
                        outData = new byte[bufferInfo.size + netPpsSps.length];
                    }

                    System.arraycopy(netPpsSps,0, outData,0,netPpsSps.length);
                    outputBuffer.get(outData,netPpsSps.length,bufferInfo.size);
                    GBlib.getInstance().PushAVData(0,GBlib.FRAME_TYPE_I, outData,
                            bufferInfo.size + netPpsSps.length,
                            System.currentTimeMillis(),(int)System.currentTimeMillis() / 1000);
                } else {
                    if (outData == null || outData.length < bufferInfo.size) {
                        outData = new byte[bufferInfo.size ];
                    }
                    outputBuffer.get(outData,0,bufferInfo.size);
                    GBlib.getInstance().PushAVData(0,GBlib.FRAME_TYPE_P, outData,
                            bufferInfo.size,
                            System.currentTimeMillis(),(int)System.currentTimeMillis() / 1000);
                }

                outputBuffer.clear();
                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return pos;
    }

    public void close() {
        try {
            if (mediaCodec != null) {
                mediaCodec.stop();
                mediaCodec.release();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1、App的参数设置界面

2、App的摄像头预览界面

3、平台播放实时效果

具体实现代码可私聊我要哦!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
GB28181协议是中国监控领域的一种视频监控设备间通信协议,旨在实现设备之间的互通互联。GB28181协议实现源码对于开发者来说非常重要,可以帮助他们理解和实现协议相关功能。 针对GB28181协议Android源码实现,主要包括以下几个方面的内容: 1. SIP协议处理:GB28181协议使用SIP协议作为信令传输协议Android源码实现需要涉及到SIP协议的处理。包括SIP消息的解析、组装和传输等。 2. RTP媒体传输:GB28181协议使用RTP协议传输视频和音频数据,Android源码实现需要包括RTP协议的解析和组装,以及相应的数据传输功能。 3. 设备管理功能:GB28181协议实现源码还需要包括设备管理的相关功能,例如设备注册、心跳、查询设备列表等。这些功能可以通过Android源码实现,提供给开发者进行二次开发和定制。 4. 实时监控功能:GB28181协议主要用于实现视频监控设备之间的实时监控功能,Android源码实现需要包括视频流的接收、解码和显示等功能。 实现GB28181协议Android源码可以通过使用Android网络通信框架,如SIP协议实现可以使用Jain-SIP库,RTP协议实现可以使用Android的MediaCodec等相关API。 需要指出的是,GB28181协议实现源码是非常复杂的,其中涉及到的技术细节较多,需要开发者具备相应的网络通信和视频处理的知识。因此,在使用和修改GB28181实现源码时,开发者需要谨慎并且具备相应的专业知识,以确保功能的正确性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Storm-Shadow

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值