网站制作价格明细,微信朋友圈营销技巧,嘉兴网站建设嘉兴网站推广,成都装修建材网站建设技术诉求
我们在做GB28181设备对接模块和RTMP直播推送模块的时候#xff0c;遇到这样的技术需求#xff0c;设备#xff08;如执法记录仪#xff09;侧除了采集传统的摄像头外#xff0c;还需要对接比如大疆等第三方数据源#xff0c;确保按照GB28181规范和RTMP协议规范…技术诉求
我们在做GB28181设备对接模块和RTMP直播推送模块的时候遇到这样的技术需求设备如执法记录仪侧除了采集传统的摄像头外还需要对接比如大疆等第三方数据源确保按照GB28181规范和RTMP协议规范接入到国标平台侧和RTMP服务除了正常的接入需求外还需要对第三方数据源回调过来的编码后视频、音频数据实时预览和播放。
接口设计思路
本文以Android平台为例我们需要兼容的数据格式如下H.264、H.265audio的话需要兼容AAC、PCMA、PCMU数据接口。 先说视频数据接口H.264/H.265投递接口设计如下 // SmartPlayerJniV2.java// Author: daniusdk.com/*** 投递视频包给外部Live Source** param codec_id: 编码id, 当前仅支持H264和H265, 1:H264, 2:H265** param packet: 视频数据, ByteBuffer必须是DirectBuffer, 包格式请参考H264/H265 Annex B Byte stream format, 例如:* 0x00000001 nal_unit 0x00000001 ...* H264 IDR: 0x00000001 sps 0x00000001 pps 0x00000001 IDR_nal_unit .... 或 0x00000001 IDR_nal_unit ....* H265 IDR: 0x00000001 vps 0x00000001 sps 0x00000001 pps 0x00000001 IDR_nal_unit .... 或 0x00000001 IDR_nal_unit ....** param offset: 偏移量* param size: packet size* param timestamp_ms: 时间戳, 单位毫秒* param is_timestamp_discontinuity: 是否时间戳间断0:未间断1:间断* param is_key: 是否是关键帧, 0:非关键帧, 1:关键帧* param extra_data: 可选参数可传null, 对于H264关键帧包, 如果packet不含sps和pps, 可传0x00000001 sps 0x00000001 pps* ,对于H265关键帧包, 如果packet不含vps,sps和pps, 可传0x00000001 vps 0x00000001 sps 0x00000001 pps* param extra_data_size: extra_data size* param width: 图像宽, 可传0* param height: 图像高, 可传0** return {0} if successful*/public native int PostVideoPacketByteBuffer(long handle, int codec_id,java.nio.ByteBuffer packet, int offset, int size, long timestamp_ms, int is_timestamp_discontinuity, int is_key,byte[] extra_data, int extra_data_size, int width, int height);/** 请参考 PostVideoPacketByteBuffer说明*/public native int PostVideoPacketByteArray(long handle, int codec_id,byte[] packet, int offset, int size, long timestamp_ms, int is_timestamp_discontinuity, int is_key,byte[] extra_data, int extra_data_size, int width, int height);比如codec_id区分H.264还是H.265类型packet的话我们设计了ByteBuffer和byte数组两种类型的数据接口方便对接此外传递数据的时候确保packet按照规范来还有packet的sizetimestamp是不是关键帧视频宽高等。
PostVideoPacketByteBuffer()和PostVideoPacketByteArray()接口设计基本类似唯一的区别在于一个数据类型是ByteBuffer一个是byte数组。
packet视频数据需要注意的是ByteBuffer必须是DirectBuffer, 包格式请参考H264/H265 Annex B Byte stream format, 例如: 0x00000001 nal_unit 0x00000001 ... H264 IDR: 0x00000001 sps 0x00000001 pps 0x00000001 IDR_nal_unit .... 或 0x00000001 IDR_nal_unit .... H265 IDR: 0x00000001 vps 0x00000001 sps 0x00000001 pps 0x00000001 IDR_nal_unit .... 或 0x00000001 IDR_nal_unit .... extra_data: 可选参数可传null, 对于H264关键帧包如果packet不含sps和pps可传0x00000001 sps 0x00000001 pps对于H265关键帧包如果packet不含vps,sps和pps, 可传0x00000001 vps 0x00000001 sps 0x00000001 pps。
音频数据接口 /*** 投递音频包给外部Live source, 注意ByteBuffer对象必须是DirectBuffer** param handle: return value from SmartPlayerOpen()** param codec_id: 编码id, 当前支持PCMA、PCMU和AAC, 65536:PCMA, 65537:PCMU, 65538:AAC* param packet: 音频数据* param offsetpacket偏移量* param size: packet size* param pts_ms: 时间戳, 单位毫秒* param is_pts_discontinuity: 是否时间戳间断false:未间断true:间断* param extra_data: 如果是AAC的话需要传 Audio Specific Configuration* param extra_data_offset: extra_data 偏移量* param extra_data_size: extra_data size* param sample_rate: 采样率* param channels: 通道数** return {0} if successful*/public native int PostAudioPacket(long handle, int codec_id,java.nio.ByteBuffer packet, int offset, int size, long pts_ms, boolean is_pts_discontinuity,java.nio.ByteBuffer extra_data, int extra_data_offset, int extra_data_size, int sample_rate, int channels);/** 投递音频包给外部Live source, byte数组版本, 具体请参考PostAudioPacket** param is_pts_discontinuity: 是否时间戳间断0:未间断1:间断* return {0} if successful*/public native int PostAudioPacketByteArray(long handle, int codec_id,byte[] packet, int offset, int size, long pts_ms, int is_pts_discontinuity,byte[] extra_data, int extra_data_size, int sample_rate, int channels);
音频数据接口和视频的大同小异codec_id描述支持的codec类型比如AAC、PCMA、PCMU此外还有extra_data如果是aac的话记得传下audio specific configurationsample_rate和channels无需赘述。
数据类型同样设计了ByteBuffer和byte数组的。
调用逻辑
调用demo基于大牛直播SDK的RTSP|RTMP转RTMP推送demo个简单的展示拉取到RTSP或RTMP的流数据然后把拉取到的H.264/H.265/AAC/PCMA/PCMU数据回调上来调用我们外部live source数据接口投递到底层实现实时数据的播放如果外部数据可以忽略拉流这块直接在数据回调的地方调live source数据投递接口即可。 视频数据投递 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 (0 ret ex_live_src_player_handle_ !0) {libPlayer.PostVideoPacketByteBuffer(ex_live_src_player_handle_, video_codec_id, video_buffer_, 0, sample_size, timestamp, 0, is_key_frame, null,0, 0, 0);}}
音频数据投递 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 (0 ret !ex_live_src_player_mute_) {if (ex_live_src_player_handle_ ! 0) {if (ex_live_src_player_read_lock_.tryLock()) {try {if (ex_live_src_player_handle_ ! 0) {libPlayer.PostAudioPacket(ex_live_src_player_handle_, audio_codec_id, audio_buffer_, 0, sample_size,timestamp, false,parameter_info_,0, parameter_info_size, sample_rate, channel);}}finally {ex_live_src_player_read_lock_.unlock();}}}}if ( ret 0 (isPushing || isRTSPPublisherRunning)) {libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle, audio_codec_id, audio_buffer_, sample_size, is_key_frame, timestamp, parameter_info_, parameter_info_size);}}
启动外部数据播放
可以看到外部数据可以用软解码或硬解码播放如果分辨率很大可以考虑特定机型硬解码外部数据播放依然可以设置铺满或按比例显示。如果需要针对数据做二次处理也可以把设置RGB或YUV数据回调对回调后的数据做二次处理甚至二次编码如做视频分析、实时水印等。 private long start_ex_live_src_player(SmartPlayerJniV2 lib_player, Context context, SurfaceView surface_view, boolean is_mute, boolean is_hardware_decoder) {if (null lib_player || null context || null surface_view)return 0;long handle lib_player.SmartPlayerOpen(context);if (0 handle) {Log.e(TAG, start_ex_live_src_player open player failed);return 0;}// 设置0, 尽可能降低预览延时lib_player.SmartPlayerSetBuffer(handle, 0);lib_player.SmartPlayerSetUrl(handle, ntexternal://livesource/implemention0);lib_player.SmartPlayerSetSurface(handle, surface_view);// 图像等比例缩放或铺满viewlib_player.SmartPlayerSetRenderScaleMode(handle, 1);lib_player.SmartPlayerSetFastStartup(handle, 1);lib_player.SmartPlayerSetAudioOutputType(handle, 1);// 不要播放音频静音就好lib_player.SmartPlayerSetMute(handle, is_mute?1:0);// 大分辨率可能需要硬解小分辨率推荐软解硬解延时可能大些if (is_hardware_decoder) {lib_player.SetSmartPlayerVideoHevcHWDecoder(handle, 1);lib_player.SetSmartPlayerVideoHWDecoder(handle, 1);}// 有些场景可能需要解码出来的图像用来做分析或重新编码// 这里可以设置yuv或rgb callback, 把图像给Caller// lib_player.SmartPlayerSetExternalRender(handle, new RGBAExternalRender());// lib_player.SmartPlayerSetExternalRender(handle, new I420ExternalRender());if (0 lib_player.SmartPlayerStartPlay(handle))return handle;lib_player.SmartPlayerClose(handle);return 0;}
停止外部数据播放 private void stop_ex_live_src_play(SmartPlayerJniV2 lib_player, long handle) {if (null lib_player)return;if (0 handle)return;lib_player.SmartPlayerStopPlay(handle);lib_player.SmartPlayerClose(handle);}
总结
Android平台外部编码后H.264/H.265/AAC/PCMA/PCMU数据实时预览播放非常必要除了可以预览回调过来的数据外还可以针对外部数据做二次视频分析、二次编辑投递实时水印、字符叠加等感兴趣的开发者可以试试看。