前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android平台实现RTSP|RTMP转GB28181网关接入

Android平台实现RTSP|RTMP转GB28181网关接入

原创
作者头像
音视频牛哥
发布于 2022-04-19 12:44:29
发布于 2022-04-19 12:44:29
74900
代码可运行
举报
运行总次数:0
代码可运行

背景

在事先Android平台RTSP、RTMP转GB28181网关之前,我们已经实现了Android平台GB28181的接入,可实现Android平台采集到的音视频数据,编码后,打包按需发到GB28181服务平台。此外,拉流端,我们已经有了成熟的RTSP和RTMP拉流播放方案。

今天,我们要做的是,把外部的RTSP或RTMP流,拉过来,然后对接到GB28181国标平台,实现媒体流数据的按需播放

和我们之前实现的轻量级RTSP服务网关模块类似,我们要做的是,实现RTSP或RTMP流,按需打包对接到GB28181服务平台。

简单来说,Android平台RTSP、RTMP转GB28181网关平台,是GB28181设备接入模块的一个扩展,由拉流端、GB28181接入端两个模块组成。

轻量级RTSP服务模块、RTSP|RTMP转GB28181网关模块和内置RTSP网关模块的区别和联系:

内置轻量级RTSP服务模块和内置RTSP网关模块,核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,数据汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

RTSP|RTMP转GB28181网关模块,实现的是音视频数据的转发,类似于RTSP|RTMP转RTMP推送模块,把本地数据源,对接到GB28181服务平台或RTMP服务平台。

三者不同点:数据来源不同

1. 内置轻量级RTSP服务模块,数据源来自摄像头、屏幕、麦克风等编码前数据,或者本地编码后的对接数据,这点和GB28181的设备接入模块类似。

2. 内置RTSP网关模块,实际上是RTSP/RTMP拉流模块+内置轻量级RTSP服务模块组合出来的。数据源来自RTSP或RTMP网络流,拉流模块完成编码后的音视频数据回调,然后,汇聚到内置轻量级RTSP服务模块。RTSP|RTMP转GB28181网关模块,和内置RTSP网关模块数据源接入一样。

技术实现

本文以之前Android平台RTSP|RTMP转发demo为例,在这个基础上,加GB28181网关扩展。

拉流端音频数据回调,拉流端获取到编码后是数据,回调上来,通过SmartPublisherPostAudioEncodedData()发送到推送模块。

代码语言:javascript
代码运行次数:0
运行
复制
class PlayerAudioDataCallback implements NTAudioDataCallback
  {
    private int audio_buffer_size = 0;
    private int param_info_size = 0;

    private ByteBuffer audio_buffer_ = null;
    private ByteBuffer parameter_info_ = null;

    @Override
    public ByteBuffer getAudioByteBuffer(int size)
    {
      //Log.i("getAudioByteBuffer", "size: " + size);

      if( size < 1 )
      {
        return null;
      }

      if ( size <= audio_buffer_size && audio_buffer_ != null )
      {
        return audio_buffer_;
      }

      audio_buffer_size = size + 512;
      audio_buffer_size = (audio_buffer_size+0xf) & (~0xf);

      audio_buffer_ = ByteBuffer.allocateDirect(audio_buffer_size);

      // Log.i("getAudioByteBuffer", "size: " + size + " buffer_size:" + audio_buffer_size);

      return audio_buffer_;
    }

    @Override
    public ByteBuffer getAudioParameterInfo(int size)
    {
      //Log.i("getAudioParameterInfo", "size: " + size);

      if(size < 1)
      {
        return null;
      }

      if ( size <= param_info_size &&  parameter_info_ != null )
      {
        return  parameter_info_;
      }

      param_info_size = size + 32;
      param_info_size = (param_info_size+0xf) & (~0xf);

      parameter_info_ = ByteBuffer.allocateDirect(param_info_size);

      //Log.i("getAudioParameterInfo", "size: " + size + " buffer_size:" + param_info_size);

      return parameter_info_;
    }

    public void onAudioDataCallback(int ret, int audio_codec_id, int sample_size, int is_key_frame, long timestamp, int sample_rate, int channel, int parameter_info_size, long reserve)
    {
      //Log.i("onAudioDataCallback", "ret: " + ret + ", audio_codec_id: " + audio_codec_id + ", sample_size: " + sample_size + ", timestamp: " + timestamp +
      //    ",sample_rate:" + sample_rate);

      if ( audio_buffer_ == null)
        return;

      audio_buffer_.rewind();

      if ( ret == 0 && (isPushing || isRTSPPublisherRunning || isGB28181StreamRunning)) {
        libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle, audio_codec_id, audio_buffer_, sample_size, is_key_frame, timestamp, parameter_info_, parameter_info_size);
      }
      // test

        /*
        byte[] test_buffer = new byte[16];
        pcm_buffer_.get(test_buffer);

        Log.i(TAG, "onGetPcmFrame data:" + bytesToHexString(test_buffer));
        */
    }
  }

拉流端视频数据回调,拉流端获取到编码后是数据,回调上来,通过SmartPublisherPostVideoEncodedData()发送到推送模块。

代码语言:javascript
代码运行次数:0
运行
复制
class PlayerVideoDataCallback implements NTVideoDataCallback
  {
    private int video_buffer_size = 0;

    private ByteBuffer video_buffer_ = null;

    @Override
    public ByteBuffer getVideoByteBuffer(int size)
    {
      //Log.i("getVideoByteBuffer", "size: " + size);

      if( size < 1 )
      {
        return null;
      }

      if ( size <= video_buffer_size &&  video_buffer_ != null )
      {
        return  video_buffer_;
      }

      video_buffer_size = size + 1024;
      video_buffer_size = (video_buffer_size+0xf) & (~0xf);

      video_buffer_ = ByteBuffer.allocateDirect(video_buffer_size);

      // Log.i("getVideoByteBuffer", "size: " + size + " buffer_size:" + video_buffer_size);

      return video_buffer_;
    }

    public void onVideoDataCallback(int ret, int video_codec_id, int sample_size, int is_key_frame, long timestamp, int width, int height, long presentation_timestamp)
    {
      //Log.i("onVideoDataCallback", "ret: " + ret + ", video_codec_id: " + video_codec_id + ", sample_size: " + sample_size + ", is_key_frame: "+ is_key_frame +  ", timestamp: " + timestamp +
      //    ",presentation_timestamp:" + presentation_timestamp);

      if ( video_buffer_ == null)
        return;

      video_buffer_.rewind();

      if ( ret == 0 &&  (isPushing || isRTSPPublisherRunning || isGB28181StreamRunning) ) {

        libPublisher.SmartPublisherPostVideoEncodedData(publisherHandle, video_codec_id, video_buffer_, sample_size, is_key_frame, timestamp, presentation_timestamp);
      }
    }
  }

GB28181网关模块,初始化:

代码语言:javascript
代码运行次数:0
运行
复制
/*
   * gb28181 agent初始化
   * Github: https://github.com/daniulive/SmarterStreaming
   */
 private boolean initGB28181Agent() {
      if ( gb28181_agent_ != null )
         return  true;

      getLocation(myContext);

      String local_ip_addr = IPAddrUtils.getIpAddress(myContext);
      Log.i(TAG, "initGB28181Agent local ip addr: " + local_ip_addr);

      if ( local_ip_addr == null || local_ip_addr.isEmpty() ) {
         Log.e(TAG, "initGB28181Agent local ip is empty");
         return  false;
      }

      gb28181_agent_ = GBSIPAgentFactory.getInstance().create();
      if ( gb28181_agent_ == null ) {
         Log.e(TAG, "initGB28181Agent create agent failed");
         return false;
      }

      gb28181_agent_.addListener(this);

      // 必填信息
      gb28181_agent_.setLocalAddressInfo(local_ip_addr, gb28181_sip_local_port_);
      gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_server_domain_);
      gb28181_agent_.setUserInfo(gb28181_sip_username_, gb28181_sip_password_);

      // 可选参数
      gb28181_agent_.setUserAgent(gb28181_sip_user_agent_filed_);
      gb28181_agent_.setTransportProtocol(gb28181_sip_trans_protocol_==0?"UDP":"TCP");

      // GB28181配置
      gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);

      com.gb28181.ntsignalling.Device gb_device = new com.gb28181.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
            "宇宙","火星1","火星", true);

      if (mLongitude != null && mLatitude != null) {
         com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();

         device_pos.setTime(mLocationTime);
         device_pos.setLongitude(mLongitude);
         device_pos.setLatitude(mLatitude);
         gb_device.setPosition(device_pos);

         gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报
      }

      gb28181_agent_.addDevice(gb_device);

      if (!gb28181_agent_.initialize()) {
         gb28181_agent_ = null;
         Log.e(TAG, "initGB28181Agent gb28181_agent_.initialize failed.");
         return  false;
      }

      return true;
   }

GB28181网关模块按钮相关代码实现,整体和GB28181设备接入一致:

代码语言:javascript
代码运行次数:0
运行
复制
class ButtonGB28181AgentListener implements OnClickListener {
    public void onClick(View v) {
      stopGB28181Stream();
      destoryRTPSender();

      if (null == gb28181_agent_ ) {
        if( !initGB28181Agent() )
          return;
      }

      if (gb28181_agent_.isRunning()) {
        gb28181_agent_.terminateAllPlays(true);// 目前测试下来,发送BYE之后,有些服务器会立即发送INVITE,是否发送BYE根据实际情况看
        gb28181_agent_.stop();
        btnGB28181Agent.setText("启动GB28181");
      }
      else {
        if ( gb28181_agent_.start() ) {
          btnGB28181Agent.setText("停止GB28181");
        }
      }
    }
  }

  //停止GB28181 媒体流
  private void stopGB28181Stream() {
    if(!isGB28181StreamRunning)
      return;

    if (libPublisher != null) {
      libPublisher.StopGB28181MediaStream(publisherHandle);
    }

    if (!isRecording && !isRTSPPublisherRunning && !isPushing) {
      if (publisherHandle != 0) {
        if (libPublisher != null) {
          libPublisher.SmartPublisherClose(publisherHandle);
          publisherHandle = 0;
        }
      }
    }

    isGB28181StreamRunning = false;
  }

  private void destoryRTPSender() {
    if (gb28181_rtp_sender_handle_ != 0) {
      libPublisher.DestoryRTPSender(gb28181_rtp_sender_handle_);
      gb28181_rtp_sender_handle_ = 0;
    }
  }
GB28181网关模块,事件回调处理:

@Override
  public void ntsRegisterOK(String dateString) {
    Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));
  }

  @Override
  public void ntsRegisterTimeout() {
    Log.e(TAG, "ntsRegisterTimeout");
  }

  @Override
  public void ntsRegisterTransportError(String errorInfo) {
    Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));
  }

  @Override
  public void ntsOnHeartBeatException(int exceptionCount,  String lastExceptionInfo) {
    Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:" + exceptionCount+
        ", exception info:" + (lastExceptionInfo!=null?lastExceptionInfo:""));

    // 10毫秒后,停止信令, 然后重启
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG, "gb28281_heart_beart_timeout");
        stopGB28181Stream();
        destoryRTPSender();

        if (gb28181_agent_ != null) {
          Log.i(TAG, "gb28281_heart_beart_timeout sip stop");
          gb28181_agent_.stop();

          Log.i(TAG, "gb28281_heart_beart_timeout sip start");
          gb28181_agent_.start();
        }
      }

    },10);
  }

  @Override
  public void ntsOnInvitePlay(String deviceId, InvitePlaySessionDescription session_des) {
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG,"ntsInviteReceived, device_id:" +device_id_+", is_tcp:" + session_des_.isRTPOverTCP()
            + " rtp_port:" + session_des_.getMediaPort() + " ssrc:" + session_des_.getSSRC()
            + " address_type:" + session_des_.getAddressType() + " address:" + session_des_.getAddress());

        // 可以先给信令服务器发送临时振铃响应
        //sip_stack_android.respondPlayInvite(180, device_id_);

        long rtp_sender_handle = libPublisher.CreateRTPSender(0);
        if ( rtp_sender_handle == 0 ) {
          gb28181_agent_.respondPlayInvite(488, device_id_);
          Log.i(TAG, "ntsInviteReceived CreateRTPSender failed, response 488, device_id:" + device_id_);
          return;
        }

        gb28181_rtp_payload_type_ = session_des_.getPSRtpMapAttribute().getPayloadType();

        libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, session_des_.isRTPOverUDP()?0:1);
        libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, session_des_.isIPv4()?0:1);
        libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
        libPublisher.SetRTPSenderSSRC(rtp_sender_handle, session_des_.getSSRC());
        libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2M
        libPublisher.SetRTPSenderClockRate(rtp_sender_handle, session_des_.getPSRtpMapAttribute().getClockRate());
        libPublisher.SetRTPSenderDestination(rtp_sender_handle, session_des_.getAddress(), session_des_.getMediaPort());

        if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
          gb28181_agent_.respondPlayInvite(488, device_id_);
          libPublisher.DestoryRTPSender(rtp_sender_handle);
          return;
        }

        int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);
        if (local_port == 0) {
          gb28181_agent_.respondPlayInvite(488, device_id_);
          libPublisher.DestoryRTPSender(rtp_sender_handle);
          return;
        }

        Log.i(TAG,"get local_port:" + local_port);

        String local_ip_addr = IPAddrUtils.getIpAddress(myContext);
        gb28181_agent_.respondPlayInviteOK(device_id_,local_ip_addr, local_port);

        gb28181_rtp_sender_handle_ = rtp_sender_handle;
      }

      private String device_id_;
      private InvitePlaySessionDescription session_des_;

      public Runnable set(String device_id, InvitePlaySessionDescription session_des) {
        this.device_id_ = device_id;
        this.session_des_ = session_des;
        return this;
      }
    }.set(deviceId, session_des),0);
  }

  @Override
  public void ntsOnCancelPlay(String deviceId) {
    // 这里取消Play会话
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG, "ntsOnCancelPlay, deviceId=" + device_id_);

        destoryRTPSender();
      }

      private String device_id_;

      public Runnable set(String device_id) {
        this.device_id_ = device_id;
        return this;
      }

    }.set(deviceId),0);
  }

  @Override
  public void ntsOnAckPlay(String deviceId) {
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);

        if (!isRecording && !isRTSPPublisherRunning && !isPushing) {
          OpenPushHandle();
        }

        libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_);
        int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);
        if (startRet != 0) {

          if (!isRecording && !isRTSPPublisherRunning && !isPushing) {
            if (publisherHandle != 0) {
              libPublisher.SmartPublisherClose(publisherHandle);
              publisherHandle = 0;
            }
          }

          destoryRTPSender();

          Log.e(TAG, "Failed to start GB28181 service..");
          return;
        }

        isGB28181StreamRunning = true;
      }

      private String device_id_;

      public Runnable set(String device_id) {
        this.device_id_ = device_id;
        return this;
      }

    }.set(deviceId),0);
  }

  @Override
  public void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {
    // 这里要释放掉响应的资源
    Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + deviceId + " statusCode=" +statusCode
        + " errorInfo:" + errorInfo);

    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + device_id_);

        destoryRTPSender();
      }

      private String device_id_;

      public Runnable set(String device_id) {
        this.device_id_ = device_id;
        return this;
      }

    }.set(deviceId),0);
  }

  @Override
  public void ntsOnByePlay(String deviceId) {
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG, "ntsOnByePlay, stop GB28181 media stream, deviceId=" + device_id_);

        stopGB28181Stream();
        destoryRTPSender();
      }

      private String device_id_;

      public Runnable set(String device_id) {
        this.device_id_ = device_id;
        return this;
      }

    }.set(deviceId),0);
  }

  @Override
  public void ntsOnPlayDialogTerminated(String deviceId) {
        /*
        Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发
        收到这个请做相关清理处理
        */
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG, "ntsOnPlayDialogTerminated, deviceId=" + device_id_);

        stopGB28181Stream();
        destoryRTPSender();
      }

      private String device_id_;

      public Runnable set(String device_id) {
        this.device_id_ = device_id;
        return this;
      }

    }.set(deviceId),0);
  }

  @Override
  public void ntsOnDevicePositionRequest(String deviceId, int interval) {
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        getLocation(myContext);

        Log.v(TAG, "ntsOnDevicePositionRequest, deviceId:" + this.device_id_ + ", Longitude:" + mLongitude
            + ", Latitude:" + mLatitude + ", Time:" + mLocationTime);


        if (mLongitude != null && mLatitude != null) {
          com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();

          device_pos.setTime(mLocationTime);
          device_pos.setLongitude(mLongitude);
          device_pos.setLatitude(mLatitude);

          if (gb28181_agent_ != null ) {
            gb28181_agent_.updateDevicePosition(device_id_, device_pos);
          }
        }
      }

      private String device_id_;
      private int interval_;

      public Runnable set(String device_id, int interval) {
        this.device_id_ = device_id;
        this.interval_ = interval;
        return this;
      }

    }.set(deviceId, interval),0);
  }

OnDestroy(),停掉GB28181 stream,释放资源。

代码语言:javascript
代码运行次数:0
运行
复制
@Override
  protected void onDestroy() {
    Log.i(TAG, "Run into activity destory++");

    if (gb28181_agent_ != null ) {
      if (gb28181_agent_.isRunning()) {
        gb28181_agent_.terminateAllPlays(false);
        gb28181_agent_.stop();
      }

      Log.i(TAG, " gb28181_agent_.unInitialize++");
      gb28181_agent_.unInitialize();
      Log.i(TAG, " gb28181_agent_.unInitialize--");

      gb28181_agent_ = null;
    }

    stopGB28181Stream();
    destoryRTPSender();

    super.onDestroy();
    finish();
    System.exit(0);
  }

以上是大概流程,感兴趣的开发者,可酌情参考。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
Android平台实现mp4文件实时推送RTMP|轻量级RTSP服务|GB28181平台
好多开发者有这样的诉求,想把本地录制的MP4文件,以实时流数据的形式,推送到RTMP服务器,注入轻量级RTSP服务,或者对接到GB28181平台,这块前几年我们就有对接。
音视频牛哥
2022/09/29
4240
Android平台如何实现RTSP转GB28181
实际上,在做Android平台GB28181设备接入模块的时候,我们已经有了非常好的技术积累,比如RTMP推送、轻量级RTSP服务、一对一互动模块、业内几乎最好的RTMP|RTSP低延迟播放器。
音视频牛哥
2024/01/31
3600
Android平台如何实现RTSP转GB28181
如何实现Android平台GB28181前端设备接入
在实现Android平台GB28181前端设备接入之前,我们几年前就有了非常成熟的RTMP推送、RTSP推送和轻量级RTSP服务等模块,特别是RTMP推送,行业内应用非常广泛,好多开发者可能会问,既然有了以上模块,干嘛还要实现GB28181的前端接入呢?
音视频牛哥
2022/02/27
1.4K0
如何实现Android平台GB28181设备对接Camera2数据
在写如何实现Android平台GB28181设备对接Camera2数据说明之前,我在前两年的blog就有针对camera2的RTMP直播推送模块做过技术分享:
音视频牛哥
2022/09/27
7360
如何实现Android平台GB28181设备对接Camera2数据
Android平台GB28181设备接入端如何降低资源占用和性能消耗
我们在做GB28181设备接入模块的时候,考虑到好多设备性能一般,我们一般的设计思路是,先注册设备到平台侧,平台侧发calalog过来,获取设备信息,然后,设备侧和国标平台侧维持心跳,如果有位置订阅信息,按照订阅时间间隔,实时上报设备位置信息。
音视频牛哥
2023/08/06
2850
Android平台GB28181设备接入端如何降低资源占用和性能消耗
Android平台GB28181设备接入端如何实现多视频通道接入?
我们在设计Android平台GB28181设备接入模块的时候,有这样的场景诉求,一个设备可能需要多个通道,常见的场景,比如车载终端,一台设备,可能需要接入多个摄像头,那么这台车载终端设备可以作为主设备,然后,主设备下,配置多个通道,听起来是不是有点儿类似于DVR或NVR?
音视频牛哥
2023/08/08
3700
Android平台GB28181设备接入端如何实现多视频通道接入?
GB28181平台如何接入无人机实现智能巡检?
大家都知道,无人机-巡检系统,有效解决了传统巡查工作空间和时间局限问题,降低人力工作成本,有效替代人工巡检工作模式。智能巡检系统通过人工智能技术和机械智能技术完美结合,在工业等场景下,应用非常广泛。本文旨在讲如何实现无人机(如大疆无人机)数据到GB28181平台(如海康、大华、宇视等国标平台)。
音视频牛哥
2022/10/08
8940
GB28181平台如何接入无人机实现智能巡检?
Android平台GB28181实时回传流程和技术实现
GB28181 中的 “INVITE” 是会话初始协议(SIP)中的一种请求方法,主要用于邀请一个或多个参与者加入特定的会话。在 GB28181 标准中,“INVITE” 请求通常用于发起媒体流的传输请求。当一个设备想要接收来自另一个设备的媒体流时,它会向目标设备发送一个 “INVITE” 请求,其中包含了关于会话的描述信息,如媒体类型、编码格式、传输协议等。
音视频牛哥
2024/09/30
1840
Android平台GB28181实时回传流程和技术实现
Android平台GB28181设备接入模块实现后台service按需回传摄像头数据到国标平台侧
我们在做Android平台GB28181设备对接模块的时候,遇到这样的技术需求,开发者希望能以后台服务的形式运行程序,国标平台侧没有视频回传请求的时候,仅保持信令链接,有发起视频回传请求或语音广播时,打开摄像头,并实时回传音视频数据或接收处理国标平台侧发过来的语音广播数据。
音视频牛哥
2024/02/06
1850
Android平台GB28181设备接入模块实现后台service按需回传摄像头数据到国标平台侧
如何实现Android视音频数据对接到GB28181平台(SmartGBD)
在做Android平台GB28181接入模块之前,我们在RTMP推送播放、RTSP轻量级服务、转发、播放这块,已经有很多年的经验,这意味着,我们不需要重复造轮子,已有屏幕、摄像头或编码前(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型)或编码后(H.264/HEVC)数据,只需要实现GB28181的信令交互,和媒体处理,即可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务。
音视频牛哥
2023/10/26
4950
如何实现Android视音频数据对接到GB28181平台(SmartGBD)
如何快速实现Android平台前端设备接入能力
SIP(会话初始化协议)是在 IP网络上进行多媒体通信的应用层控制协议,以几种RFC的形式提供,其中最重要的是包含核心协议规范的RFC3261。该协议用于创建,修改和终止与一个或多个参与者的会话。通过会话,我们了解了一组进行通信的发送方和接收方,以及在通信过程中这些发送方和接收方保持的状态。会话的示例可以包括Internet电话呼叫,多媒体分发,多媒体会议,分布式计算机游戏等。
音视频牛哥
2022/03/09
5570
Android平台GB28181设备接入侧如何实现按需打开视音频采集传输
GB/T28181是中国国家标准,全称为《安全防范视频监控联网系统信息传输、交换、控制技术要求》,该标准规定了城市安全防范监控系统中视频监控联网系统的一般要求和架构,以及信息传输、交换、控制的技术要求。它主要应用于安防领域,为各种视频监控系统提供了一致的接口规范,使得不同厂商生产的视频监控设备可以相互兼容。规范规定了公共安全视频监控联网系统(以下简称“联网系统”)的互联结构,传输、交换、控制的基本要求和安全性要求,以及控制、传输流程和协议接口等技术要求。适用于公共安全视频监控联网系统的方案设计、系统检测、验收以及与之相关的设备研发生产。其他视频监控联网系统可参照执行。目前已更新至GB/T28181-2022版。
音视频牛哥
2023/09/15
2990
Android平台GB28181设备接入侧如何实现按需打开视音频采集传输
GB/T28181联网系统通信协议结构和技术实现
联网系统有关设备之间会话建立过程的会话协商和媒体协商应采用IETF RFC 4566协议描述,主要内容包括会话描述、媒体信息描述、时间信息描述。
音视频牛哥
2022/09/05
4350
GB/T28181联网系统通信协议结构和技术实现
Android平台GB28181设备接入端如何支持跨网段语音对讲?
如果你是音视频开发者亦或寻求这块技术方案的公司,在探讨这个问题之前,你可能网上看了太多关于语音广播和语音对讲相关的资料,大多文章认为语音对讲和语音广播无本质区别,实现思路也大同小异。
音视频牛哥
2022/12/13
7970
Android平台GB28181设备接入端如何支持跨网段语音对讲?
Android平台GB28181设备接入模块如何实现实时视频和本地录像双码流编码
我们在做Android平台GB28181设备接入模块的时候,遇到这样的场景,比如执法记录仪或智慧工地等场景下,由于GB28181设备接入模块,注册到国标平台后,平时只是心跳保持,或还有实时位置订阅,查看视频的时候,是按需看,而且有时候,网络环境并不是太好,所以,催生了这样一个诉求:部分开发者希望能本地录像的时候,录制高分辨率(比如1920*1080),国标平台侧发起实时视频查看请求的时候,上传低分辨率(如1280*720)数据,有点类似于IPC的主码流和子码流。
音视频牛哥
2023/05/23
5190
Android平台GB28181设备接入模块如何实现实时视频和本地录像双码流编码
GB/T28181-2016基于RTP的视音频数据封装和技术实现
基于RTP的 PS封装首先按照ISO/IEC13818-1:2000将视音频流封装成PS包,再将PS包以负载的方式封装成 RTP包。
音视频牛哥
2022/09/25
1.3K0
如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
实际上,我在年前的blog,已经写过Android平台GB28181后台service模式启动摄像头按需回传数据了,此次版本,是上个demo的迭代版,目的是平台侧如果不发起回传请求的话,摄像头不打开。
音视频牛哥
2024/02/19
2900
如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
国网B接口调阅实时视频规范解读和代码示例分析
调阅实时视频包括信令接口和媒体流接口,采用标准的SIP INVITE+SDP流程,媒体传输使用RTP/RTCP。
音视频牛哥
2023/04/14
6390
国网B接口调阅实时视频规范解读和代码示例分析
GB28181基于TCP协议的视音频媒体传输探究及实现
实时视频点播、历史视频回放与下载的 TCP媒体传输应支持基于RTP封装的视音频PS流,封装格式参照IETFRFC4571。
音视频牛哥
2022/10/30
8750
GB28181基于TCP协议的视音频媒体传输探究及实现
GB28181-2022相对2016版"基于TCP协议的视音频媒体传输要求"调整
GB28181-2022针对“基于TCP协议的视音频媒体传输”实时点播、历史视频回放与下载中,TCP媒体传输重连机制,做了说明。
音视频牛哥
2023/02/16
5830
GB28181-2022相对2016版"基于TCP协议的视音频媒体传输要求"调整
推荐阅读
相关推荐
Android平台实现mp4文件实时推送RTMP|轻量级RTSP服务|GB28181平台
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验