Android国标接入端如何播放GB28181平台端语音广播数据

简介: GB28181语音广播这块,我们依据GB/T28181-2016针对流程和实例代码,做过详细的描述,本次主要是探讨下,广播数据过来后,如何处理。

GB28181语音广播这块,我们依据GB/T28181-2016针对流程和实例代码,做过详细的描述,本次主要是探讨下,广播数据过来后,如何处理。


鉴于我们之前有非常成熟的RTMP|RTSP低延迟播放模块,语音广播数据过来后,调用startAudioPlay(),ntsOnInviteAudioBroadcastResponse()处理如下:

@Override
public void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int statusCode, PlaySessionDescription sessionDescription) {
  handler_.postDelayed(new Runnable() {
    @Override
    public void run() {
      Log.i(TAG, "ntsOnInviteAudioBroadcastResponse, statusCode:" + status_code_ +" sourceID:" + source_id_ + ", targetID:" + target_id_);
      boolean is_need_destory_rtp = true;
      if (gb28181_agent_ != null ) {
        boolean is_need_bye = 200==status_code_;
        if (200 == status_code_ && session_description_ != null && rtp_receiver_handle_ != 0 ) {
          MediaSessionDescription audio_des = session_description_.getAudioDescription();
          SDPRtpMapAttribute audio_attr = null;
          if (audio_des != null && audio_des.getRtpMapAttributes() != null && !audio_des.getRtpMapAttributes().isEmpty() )
            audio_attr = audio_des.getRtpMapAttributes().get(0);
          if ( audio_des != null && audio_attr != null ) {
            lib_player_.SetRTPReceiverSSRC(rtp_receiver_handle_, audio_des.getSSRC());
            lib_player_.SetRTPReceiverPayloadType(rtp_receiver_handle_, audio_attr.getPayloadType(),
                audio_attr.getEncodingName(), 2, audio_attr.getClockRate());
            // 如果是PCMA, SDK会默认填 采样率8000, 通道1, 其他音频编码需要手动填入
            // lib_player_.SetRTPReceiverAudioSamplingRate(rtp_receiver_handle_, 8000);
            // lib_player_.SetRTPReceiverAudioChannels(rtp_receiver_handle_, 1);
            lib_player_.SetRTPReceiverRemoteAddress(rtp_receiver_handle_, audio_des.getAddress(), audio_des.getPort());
            lib_player_.InitRTPReceiver(rtp_receiver_handle_);
            if (startAudioPlay()) {
              is_need_bye = false;
              is_need_destory_rtp = false;
              gb_broadcast_source_id_ = source_id_;
              gb_broadcast_target_id_ = target_id_;
              btnGB28181AudioBroadcast.setText("终止GB28181语音广播");
              btnGB28181AudioBroadcast.setEnabled(true);
            }
          }
        } else {
          btnGB28181AudioBroadcast.setText("GB28181语音广播");
        }
        if (is_need_bye)
          gb28181_agent_.byeAudioBroadcast(source_id_, target_id_);
      }
      if (is_need_destory_rtp)
        destoryRTPReceiver();
    }
    private String source_id_;
    private String target_id_;
    private int status_code_;
    private PlaySessionDescription session_description_;
    public Runnable set(String source_id, String target_id, int status_code, PlaySessionDescription session_description) {
      this.source_id_ = source_id;
      this.target_id_ = target_id;
      this.status_code_ = status_code;
      this.session_description_ = session_description;
      return this;
    }
  }.set(sourceID, targetID, statusCode, sessionDescription),0);
}

startAudioPlay()初始化实例后,为了保证低延迟,拉流端设置0 buffer,处于调试方便,设置download speed回调2-5秒一次(可以看到是不是有音频数据过来),由于只需要播放音频,不需要视频,所以不要设置surface下去,然后设置拉流数据回调,需要注意的是,拉到的audio数据,不要转aac输出:

private boolean startAudioPlay() {
  if (player_handle_ != 0 )
    return false;
  player_handle_ = lib_player_.SmartPlayerOpen(context_);
  if (player_handle_ == 0)
    return false;
  lib_player_.SetSmartPlayerEventCallbackV2(player_handle_,new EventHandePlayerV2());
  // 缓存大小可以调整
  lib_player_.SmartPlayerSetBuffer(player_handle_, 0);
  // lib_player_.SmartPlayerSetFastStartup(player_handle_, 0);
  // set report download speed(默认2秒一次回调 用户可自行调整report间隔)
  lib_player_.SmartPlayerSetReportDownloadSpeed(player_handle_, 1, 20);
  lib_player_.SmartPlayerClearRtpReceivers(player_handle_);
  lib_player_.SmartPlayerAddRtpReceiver(player_handle_, rtp_receiver_handle_);
  lib_player_.SmartPlayerSetSurface(player_handle_, null);
  // lib_player_.SmartPlayerSetRenderScaleMode(player_handle_, 1);
  lib_player_.SmartPlayerSetAudioOutputType(player_handle_, 1);
  lib_player_.SmartPlayerSetMute(player_handle_, 0);
  lib_player_.SmartPlayerSetAudioVolume(player_handle_, 100);
  lib_player_.SmartPlayerSetExternalAudioOutput(player_handle_, new PlayerExternalPCMOutput());
  lib_player_.SmartPlayerSetUrl(player_handle_, "rtp://ntinternal/rtpreceiver/implemention0");
  if (0 != lib_player_.SmartPlayerStartPlay(player_handle_)) {
    lib_player_.SmartPlayerClose(player_handle_);
    player_handle_ = 0;
    Log.e(TAG,  "start audio paly failed");
    return false;
  }
  lib_player_.SmartPlayerSetAudioDataCallback(player_handle_, new PlayerAudioDataOutput());
  lib_player_.SmartPlayerSetPullStreamAudioTranscodeAAC(player_handle_, 0);
  if (0 ==lib_player_.SmartPlayerStartPullStream(player_handle_) ) {
    // 启动定时器,长时间收不到音频数据,则停止播放,发送BYE
    last_received_audio_data_time_.set(SystemClock.elapsedRealtime());
    handler_.postDelayed(new AudioPlayerPCMTimer(player_handle_), AudioPlayerPCMTimer.INTERVAL_MS);
  }
  return true;
}

调用StartPlay后,拿到的audio数据,塞到publisher端,做回音消除处理:

class PlayerExternalPCMOutput implements NTExternalAudioOutput {
  private int buffer_size_ = 0;
  private ByteBuffer pcm_buffer_ = null;
  @Override
  public ByteBuffer getPcmByteBuffer(int size)  {
    //Log.i("getPcmByteBuffer", "size: " + size);
    if(size < 1)
      return null;
    if(buffer_size_ != size) {
      buffer_size_ = size;
      pcm_buffer_ = ByteBuffer.allocateDirect(buffer_size_);
    }
    return pcm_buffer_;
  }
  public void onGetPcmFrame(int ret, int sampleRate, int channel, int sampleSize, int is_low_latency) {
    /*Log.i("onGetPcmFrame", "ret: " + ret + ", sampleRate: " + sampleRate + ", channel: " + channel + ", sampleSize: " + sampleSize +
        ",is_low_latency:" + is_low_latency + " buffer_size:" + buffer_size);*/
    if (null == pcm_buffer_)
      return;
    pcm_buffer_.rewind();
    if (ret == 0 && isGB28181StreamRunning && publisherHandle != 0 )
      libPublisher.SmartPublisherOnFarEndPCMData(publisherHandle, pcm_buffer_, sampleRate, channel, sampleSize, is_low_latency);
  }
}
private static int align(int d, int a) { return (d + (a - 1)) & ~(a - 1); }
class PlayerAudioDataOutput implements NTAudioDataCallback {
  private int buffer_size_ = 0;
  private int param_info_size_ = 0;
  private ByteBuffer 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 <= buffer_size_ && buffer_ != null )
      return buffer_;
    buffer_size_ = align(size + 256, 16);
    buffer_ = ByteBuffer.allocateDirect(buffer_size_);
    // Log.i("getAudioByteBuffer", "size: " + size + " buffer_size:" + audio_buffer_size);
    return 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_ = align(size + 32, 16);
    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);
     */
    last_received_audio_data_time_.set(SystemClock.elapsedRealtime());
  }
}


如果长时间收不到数据,主动断掉音频广播:

class AudioPlayerPCMTimer implements Runnable {
  public static final int THRESHOLD_MS = 60*1000; // 暂时设置到1分钟
  public static final int INTERVAL_MS = 10*1000; // 十秒一次, 太频繁影响主线程
  public AudioPlayerPCMTimer(long handle) {
    handle_ = handle;
  }
  @Override
  public void run() {
    if (0 == handle_)
      return;
    if (handle_ != player_handle_) {
      Log.i(TAG, "AudioPlayerPCMTimer handle changed, will stop this timer, handle:" + handle_ + " new handle:" + player_handle_);
      return;
    }
    long last_update_time = last_received_audio_data_time_.get();
    long cur_time = SystemClock.elapsedRealtime();
    // Log.i(TAG, "AudioPlayerPCMTimer last_update_time:" + last_update_time + " cur_time:" + cur_time);
    if ( (last_update_time + this.THRESHOLD_MS) >  cur_time) {
      // 继续定时器
      handler_.postDelayed(new AudioPlayerPCMTimer(this.handle_), this.INTERVAL_MS);
      //  Log.i(TAG, "AudioPlayerPCMTimer running.");
    }
    else {
      Log.i(TAG, "AudioPlayerPCMTimer,trigger threshold, bye audio, stop player.");
      if (gb_broadcast_source_id_ != null && gb_broadcast_target_id_ != null) {
        if (gb28181_agent_ != null)
          gb28181_agent_.byeAudioBroadcast(gb_broadcast_source_id_, gb_broadcast_target_id_);
      }
      gb_broadcast_source_id_ = null;
      gb_broadcast_target_id_ = null;
      stopAudioPlayer();
      destoryRTPReceiver();
      btnGB28181AudioBroadcast.setText("GB28181语音广播");
      btnGB28181AudioBroadcast.setEnabled(false);
    }
  }
  private long handle_;
}


停止广播数据播放:

private void stopAudioPlayer() {
  if (player_handle_ != 0 ) {
    lib_player_.SmartPlayerStopPullStream(player_handle_);
    lib_player_.SmartPlayerStopPlay(player_handle_);
    lib_player_.SmartPlayerClose(player_handle_);
    player_handle_ = 0;
  }
}


销毁RTPReceiver:

private void destoryRTPReceiver() {
  if (rtp_receiver_handle_ != 0) {
    lib_player_.UnInitRTPReceiver(rtp_receiver_handle_);
    lib_player_.DestoryRTPReceiverSession(rtp_receiver_handle_);
    lib_player_.DestoryRTPReceiver(rtp_receiver_handle_);
    rtp_receiver_handle_ = 0;
  }
}


以上是针对GB28181平台端音频广播播放的一点说明,感兴趣的开发者,可以酌情参考,也可以和我探讨Android平台GB28181接入模块的测试。

相关文章
|
8天前
|
消息中间件 网络协议 Java
Android 开发中实现数据传递:广播和Handler
Android 开发中实现数据传递:广播和Handler
13 1
|
9天前
|
Android开发
Android MediaTek 平台增加UART接口的红外模块支持,支持NEC红外遥控
Android MediaTek 平台增加UART接口的红外模块支持,支持NEC红外遥控
11 0
|
29天前
|
Android开发
Android实现语音播报的两种方式
Android实现语音播报的两种方式
45 0
|
2月前
|
API 开发工具 Android开发
iOS 和 Android 平台的开发有哪些主要区别?
iOS与Android开发区别:iOS用Objective-C/Swift,App Store唯一下载渠道;Android用Java/Kotlin,多商店发布(如Google Play、华为市场)。设计上,iOS简洁一致,Android灵活可定制。开发工具,iOS用Xcode,Android用Android Studio。硬件和系统多样性,iOS统一,Android复杂。权限管理、审核流程及API各有特点,开发者需依据目标平台特性进行选择。
30 3
|
8天前
|
Android开发 内存技术
Android 通过tinyalsa调试解决录制和播放音频问题
Android 通过tinyalsa调试解决录制和播放音频问题
25 1
|
8天前
|
存储 Linux Android开发
Android存储分区与Rockchip平台的分区命名及U-Boot配置
Android存储分区与Rockchip平台的分区命名及U-Boot配置
13 0
|
9天前
|
存储 安全 Ubuntu
Android 生成平台应用签名keystore文件
Android 生成平台应用签名keystore文件
8 0
|
15天前
|
Android开发 开发者
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
Android&#39;s AsyncTask simplifies asynchronous tasks for brief background work, bridging UI and worker threads. It involves execute() for starting tasks, doInBackground() for background execution, publishProgress() for progress updates, and onPostExecute() for returning results to the main thread.
11 0
|
15天前
|
网络协议 安全 API
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
HTTP和HTTPS是网络数据传输协议,HTTP基于TCP/IP,简单快速,HTTPS则是加密的HTTP,确保数据安全。在Android中,过去常用HttpURLConnection和HttpClient,但HttpClient自Android 6.0起被移除。现在推荐使用支持TLS、流式上传下载、超时配置等特性的HttpsURLConnection进行网络请求。
11 0
|
23天前
|
JSON 安全 Java
Android网络部分-----网络数据请求、解析
Android网络部分-----网络数据请求、解析
Android网络部分-----网络数据请求、解析