Android平台实现VR头显Unity下音视频数据RTMP推送

背景

随着技术发展的日新月异,虚拟现实产业已经从过去的探索期,自2020年起,慢慢过渡到高速发展期,随着5G时代的到来,大带宽高可靠低延迟网络环境,为虚拟现实产业提供了很好的网络保障,虚拟现实在越来越多的场景下有了应用价值,典型场景如工业互联网、虚拟仿真、文旅文博、智慧交通、智慧能源、智慧医疗、智慧校园、智慧农业等。同事,行业也对清晰度、流畅性和交互感也提出了更高的要求。本文从Android平台的采集推送为例,介绍下基于头显或类似终端的低延迟解决方案。

实现

大多数头显设备,基于Android平台,本文以Unity环境下的窗体采集、麦克风、和Unity内部音频采集为例,介绍下具体实现思路,其中,音频采集可分为:采集麦克风、采集Unity音频、麦克风和Unity音频混音、2路Unity音频混音。采集到的音视频原始数据,分别投递到Android原生封装的模块,进行编码、打包,通过RTMP传输到服务端,实现毫秒级延迟的RTMP直播方案。

其中音频这块,分单独采集和混音,如需采集麦克风,记得动态获取麦克风权限,由于仅限于功能展示,页面页面比较粗糙:

首先是音频采集类型定义,我们把音频分为以下几类: 

/*定义Audio源选项*/public enum PB_AUDIO_OPTION : uint{AUDIO_OPTION_CAPTURE_MIC = 0x0,             /*采集麦克风音频*/AUDIO_OPTION_EXTERNAL_PCM_DATA = 0x1,       /*外部PCM数据*/AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER = 0x2,  /*麦克风+外部PCM数据混音*/AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER = 0x3,  /* 两路外部PCM数据混音*/
}

1. 基础初始化

基础初始化,主要完成和Android封装层的拉通和Audio权限动态检测。

private void Start(){game_object_ = this.gameObject.name;    //获取GameObject NameAndroidJavaClass android_class = new AndroidJavaClass("com.unity3d.player.UnityPlayer");java_obj_cur_activity_ = android_class.GetStatic<AndroidJavaObject>("currentActivity");pusher_obj_ = new AndroidJavaObject("com.daniulive.smartpublisher.SmartPublisherUnity3d");NT_PB_U3D_Init();//NT_U3D_SetSDKClientKey("", "", 0);audioOptionSel.onValueChanged.AddListener(SelAudioPushType);btn_encode_mode_.onClick.AddListener(OnEncodeModeBtnClicked);btn_pusher_.onClick.AddListener(OnPusherBtnClicked);btn_mute_.onClick.AddListener(OnMuteBtnClicked);
#if PLATFORM_ANDROIDif (!Permission.HasUserAuthorizedPermission(Permission.Microphone)){Permission.RequestUserPermission(Permission.Microphone);permission_dialog_ = new GameObject();}
#endif
}

2.OpenPusher实现

OpenPusher主要是调用底层模块的Open接口,创建推送实例,并返回推送句柄,如只需要推送纯音频或纯视频,也可通过NT_PB_U3D_Open()接口,做相关设定。

private void OpenPusher(){if ( java_obj_cur_activity_ == null ){Debug.LogError("getApplicationContext is null");return;}int audio_opt = 1;int video_opt = 1;pusher_handle_ = NT_PB_U3D_Open(audio_opt, video_opt, video_width_, video_height_);if (pusher_handle_ != 0)Debug.Log("NT_PB_U3D_Open success");elseDebug.LogError("NT_PB_U3D_Open fail");}

3.InitAndSetConfig具体实现

InitAndSetConfig主要完成SDK的一些参数设定工作,如软、硬编码设定、码率设定、音频采集类型、视频帧率、码率设置等。

private void InitAndSetConfig(){if (is_hw_encode_){int h264HWKbps = setHardwareEncoderKbps(true, video_width_, video_height_);Debug.Log("h264HWKbps: " + h264HWKbps);int isSupportH264HWEncoder = NT_PB_U3D_SetVideoHWEncoder(pusher_handle_, h264HWKbps);if (isSupportH264HWEncoder == 0){Debug.Log("Great, it supports h.264 hardware encoder!");}}else {if (is_sw_vbr_mode_) //H.264 software encoder{int is_enable_vbr = 1;int video_quality = CalVideoQuality(video_width_, video_height_, true);int vbr_max_bitrate = CalVbrMaxKBitRate(video_width_, video_height_);NT_PB_U3D_SetSwVBRMode(pusher_handle_, is_enable_vbr, video_quality, vbr_max_bitrate);//NT_PB_U3D_SetSWVideoEncoderSpeed(pusher_handle_, 2);}}NT_PB_U3D_SetAudioCodecType(pusher_handle_, 1);NT_PB_U3D_SetFPS(pusher_handle_, 25);NT_PB_U3D_SetGopInterval(pusher_handle_, 25*2);//NT_PB_U3D_SetSWVideoBitRate(pusher_handle_, 600, 1200);if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER){NT_PB_U3D_SetAudioMix(pusher_handle_, 1);}else{NT_PB_U3D_SetAudioMix(pusher_handle_, 0);}
}

4.Push()封装

获取到推送实例句柄后,设置推送参数和RTMP URL,采集音视频数据,推送到RTMP服务,如需推送麦克风,启动麦克风,并设定采样率和通道数,如需混音:

public void Push(){if (is_running_){Debug.Log("已推送..");   return;}video_width_ = Screen.width;video_height_ = Screen.height;//获取输入框的urlstring url = input_url_.text.Trim();if (!url.StartsWith("rtmp://")){push_url_ = "rtmp://192.168.2.154:1935/hls/stream1";}else{push_url_ = url;}OpenPusher();if (pusher_handle_ == 0)return;NT_PB_U3D_Set_Game_Object(pusher_handle_, game_object_);/* ++ 推送前参数配置可加在此处 ++ */InitAndSetConfig();NT_PB_U3D_SetPushUrl(pusher_handle_, push_url_);/* -- 推送前参数配置可加在此处 -- */int flag = NT_PB_U3D_StartPublisher(pusher_handle_);if (flag  == DANIULIVE_RETURN_OK){Debug.Log("推送成功..");is_running_ = true;if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_EXTERNAL_PCM_DATA|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER){PostUnityAudioClipData();}if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_CAPTURE_MIC|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER){// 注意多路或者调用了其他StartXXX的情况NT_PB_U3D_StartAudioRecord(44100, 1);NT_PB_U3D_EnableAudioRecordCapture(pusher_handle_, true);}}else{Debug.LogError("推送失败..");}}

2. 停止推送

停止推送之前,如采集AudioSource或麦克风数据,先停掉后再调用NT_PB_U3D_StopPublisher()即可,如无其他录像之类操作,接着调用NT_PB_U3D_Close()和NT_PB_U3D_UnInit()接口,并置空推送实例。

private void ClosePusher(){if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_EXTERNAL_PCM_DATA|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER){StopAudioSource();}if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_CAPTURE_MIC|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER){NT_PB_U3D_EnableAudioRecordCapture(pusher_handle_, false);// 注意多路或者调用了其他StartXXX的情况NT_PB_U3D_StopAudioRecord();}if (texture_ != null){UnityEngine.Object.Destroy(texture_);texture_ = null;}int flag = NT_PB_U3D_StopPublisher(pusher_handle_);if (flag == DANIULIVE_RETURN_OK){Debug.Log("停止成功..");}else{Debug.LogError("停止失败..");}flag = NT_PB_U3D_Close(pusher_handle_);if (flag == DANIULIVE_RETURN_OK){Debug.Log("关闭成功..");}else{Debug.LogError("关闭失败..");}pusher_handle_ = 0;NT_PB_U3D_UnInit();is_running_ = false;}

6.数据采集

摄像头和屏幕的数据采集,还是调用Android原生封装的接口,本文不再赘述,如果需要采集Unity窗体的数据,可以用参考以下代码:

if (texture_ == null || video_width_ != Screen.width || video_height_ != Screen.height){if (texture_ !=  null){UnityEngine.Object.Destroy(texture_);texture_ = null;}video_width_ = Screen.width;video_height_ = Screen.height;scale_width_ = (video_width_ + 1) / 2;scale_height_ = (video_height_ + 1) / 2;if (scale_width_ % 2 != 0){scale_width_ = scale_width_ + 1;}if (scale_height_ % 2 != 0){scale_height_ = scale_height_ + 1;}texture_ = new Texture2D(video_width_, video_height_, TextureFormat.RGBA32, false);Debug.Log("OnPostRender screen changed--");return;}texture_.ReadPixels(new Rect(0, 0, video_width_, video_height_), 0, 0, false);//texture_.ReadPixels(new Rect(0, Screen.height * 0.5f, video_width_, video_height_), 0, 0, false); //半屏测试texture_.Apply();

从 texture里面,获取到原始数据。

如需采集Unity的AudioClip数据:

  /// <summary>/// 获取AudioClip数据/// </summary>private void PostUnityAudioClipData(){//重新初始化TimerManager.UnRegister(this);if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_EXTERNAL_PCM_DATA|| audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER){audio_clip_info_ = new AudioClipInfo();audio_clip_info_.audio_source_ = GameObject.Find("Canvas/Panel/AudioSource").GetComponent<AudioSource>();if (audio_clip_info_.audio_source_ == null){Debug.LogError("audio source is null..");return;}if (audio_clip_info_.audio_source_.clip == null){Debug.LogError("audio clip is null..");return;}audio_clip_info_.audio_clip_ = audio_clip_info_.audio_source_.clip;audio_clip_info_.audio_clip_offset_ = 0;audio_clip_info_.audio_clip_length_ = audio_clip_info_.audio_clip_.samples * audio_clip_info_.audio_clip_.channels;}TimerManager.Register(this, 0.039999f, (PostPCMData), false);}

7. 数据对接

Unity的视频数据,获取到Texture数据后,通过调用 NT_PB_U3D_OnCaptureVideoRGBA32Data()接口,传递给SDK层。

如果是Unity的AudioClip采集的数据,调用NT_PB_U3D_OnPCMFloatArray()传递给封装模块。

NT_PB_U3D_OnPCMFloatArray(pusher_handle_, pcm_data.data_,0, pcm_data.size_,pcm_data.sample_rate_,pcm_data.channels_,pcm_data.per_channel_sample_number_);

8. 相关event回调处理

/// <summary>
/// android 传递过来 code
/// </summary>
/// <param name="code"></param>
public void onNTSmartEvent(string param)
{if (!param.Contains(",")){Debug.Log("[onNTSmartEvent] android传递参数错误");return;}string[] strs = param.Split(',');string player_handle =strs[0];string code = strs[1];string param1 = strs[2];string param2 = strs[3];string param3 = strs[4];string param4 = strs[5];Debug.Log("[onNTSmartEvent] code: 0x" + Convert.ToString(Convert.ToInt32(code), 16));String publisher_event = "";switch (Convert.ToInt32(code)){case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:publisher_event = "开始..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:publisher_event = "连接中..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:publisher_event = "连接失败..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:publisher_event = "连接成功..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:publisher_event = "连接断开..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:publisher_event = "关闭..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:publisher_event = "开始一个新的录像文件 : " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:publisher_event = "已生成一个录像文件 : " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:publisher_event = "发送时延: " + param1 + " 帧数:" + param2;break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:publisher_event = "快照: " + param1 + " 路径:" + param3;if (Convert.ToInt32(param1) == 0){publisher_event = publisher_event + "截取快照成功..";}else{publisher_event = publisher_event + "截取快照失败..";}break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:publisher_event = "RTSP服务URL: " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:publisher_event = "RTSP status code received, codeID: " + param1 + ", RTSP URL: " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:publisher_event = "服务器不支持RTSP推送, 推送的RTSP URL: " + param3;break;}Debug.Log(publisher_event);
}

总结

如果需要头显端采集实时数据,可以参考上述写法,获取到Texture数据和AudioClip数据,直接把数据投递到Android封装的底层模块,底层模块实现数据的编码打包按协议规范发送即可。感兴趣的开发者,也可以参考我们针对Android模块做的二次接口封装,然后自行参考尝试即可。

/** WebSite: daniusdk.com * SmartPublisherAndroidMono* Android原生模块和Unity交互接口封装*/
1.【最先调用】NT_PB_U3D_Init,推送实例初始化,目前预留;
2.【最后调用】NT_PB_U3D_UnInit,UnInit推送SDK,最后调用。
3.【启动麦克风】NT_PB_U3D_StartAudioRecord,请确保sample_rate有效,当前只支持{44100, 8000, 16000, 24000, 32000, 48000}, 推荐44100,channels,当前通道支持单通道(1)和双通道(2),推荐单通道(1),如只需要采集Unity音频,无需启用麦克风采集,如需采集麦克风音频,可在Unity动态获取麦克风采集权限。
4.【停止麦克风】NT_PB_U3D_StopAudioRecord,如启动了麦克风,调用停止推送相关操作之前,把麦克风采集停掉。
5.【获得推送实例句柄】NT_PB_U3D_Open,设置上下文信息,返回推送实例句柄;
6.【是否启用麦克风采集】NT_PB_U3D_EnableAudioRecordCapture,设置是否使用麦克风采集的音频,is_enable_audio_record_capture为true时启用。
7.【设置GameObject】NT_PB_U3D_Set_Game_Object,注册Game Object,用于消息传递;
8.【设置H.264硬编码】NT_PB_U3D_SetVideoHWEncoder,设置特定机型H.264硬编码;
9.【设置H.265硬编码】NT_PB_U3D_SetVideoHevcHWEncoder,设置特定机型H.265硬编码;
10.【设置软编码可变码率编码】NT_PB_U3D_SetSwVBRMode,设置软编码可变码率编码模式;
11.【设置关键帧间隔】NT_PB_U3D_SetGopInterval,设置关键帧间隔,一般来说,关键帧间隔可以设置到帧率的2-4倍;
12.【软编码编码码率】NT_PB_U3D_SetSWVideoBitRate,设置软编码编码码率;
13.【设置帧率】NT_PB_U3D_SetFPS,设置视频帧率;
14.【设置编码profile】NT_PB_U3D_SetSWVideoEncoderProfile,设置软编码编码profile;
15.【设置软编码编码速度】NT_PB_U3D_SetSWVideoEncoderSpeed,设置软编码编码速度;
16.【设置是否混音】NT_PB_U3D_SetAudioMix,设置混音,is_mix: 1混音, 0不混音, 默认不混音,目前支持两路音频混音;
17.【实时静音】NT_PB_U3D_SetMute,设置推送过程中,音频实时静音;
18.【输入音量调节】NT_PB_U3D_SetInputAudioVolume,设置输入音量, 这个接口一般不建议调用, index: 一般是0和1, 如果没有混音的只用0, 有混音的话, 0,1分别设置音量,在一些特殊情况下可能会用, 一般不建议放大音量;
19.【设置音频编码类型】NT_PB_U3D_SetAudioCodecType,设置音频编码类型,默认AAC编码;
20.【设置音频编码码率】NT_PB_U3D_SetAudioBitRate,设置音频编码码率, 当前只对AAC编码有效;
21.【设置speex音频编码质量】NT_PB_U3D_SetSpeexEncoderQuality,设置speex音频编码质量;
22.【设置音频噪音抑制】NT_PB_U3D_SetNoiseSuppression,设置音频噪音抑制;
23.【设置音频自动增益控制】NT_PB_U3D_SetAGC,设置音频自动增益控制;
24.【设置快照flag】NT_PB_U3D_SetSaveImageFlag,设置是否需要在推流或录像过程中快照;
25.【实时快照】NT_PB_U3D_SaveCurImage,推流或录像过程中快照;
26.【录像音频控制】NT_PB_U3D_SetRecorderAudio,音频录制开关, 目的是为了更细粒度的去控制录像, 一般不需要调用这个接口, 这个接口使用场景比如同时推送音视频,但只想录制视频,可以调用这个接口关闭音频录制;
27.【录像视频控制】NT_PB_U3D_SetRecorderVideo,视频录制开关, 目的是为了更细粒度的去控制录像, 一般不需要调用这个接口, 这个接口使用场景比如同时推送音视频,但只想录制音频,可以调用这个接口关闭视频录制;
28.【录像】NT_PB_U3D_CreateFileDirectory,创建录像存储路径;
29.【录像】NT_PB_U3D_SetRecorderDirectory,设置录像存储路径;
30.【录像】NT_PB_U3D_SetRecorderFileMaxSize,设置单个录像文件大小;
31.【数据投递】NT_PB_U3D_OnCaptureVideoI420Data,实时投递YUV数据;
32.【数据投递】NT_PB_U3D_OnCaptureVideoRGB24Data,实时投递RGB24数据;
33.【数据投递】NT_PB_U3D_OnCaptureVideoRGBA32Data,实时投递RGBA数据;
34.【数据投递】NT_PB_U3D_OnPCMData,实时投递PCM数据,数据类型,byte数组;
35.【数据投递】NT_PB_U3D_OnPCMShortArray,实时投递PCM数据,数据类型short数组;
36.【数据投递】NT_PB_U3D_OnPCMFloatArray,实时投递PCM数据,数据类型float数组;
37.【混音数据投递】NT_PB_U3D_OnMixPCMShortArray,传递PCM混音音频数据给SDK, 每10ms音频数据传入一次,数据类型short数组;
38.【混音数据投递】NT_PB_U3D_OnMixPCMFloatArray,传递PCM混音音频数据给SDK, 每10ms音频数据传入一次,数据类型float数组;
39.【推送URL】NT_PB_U3D_SetPushUrl,设置推送的RTMP URL;
40.【开始RTMP推流】NT_PB_U3D_StartPublisher,开始RTMP推流;
41.【停止RTMP推流】NT_PB_U3D_StopPublisher,停止RTMP推流;
42.【开始录像】NT_PB_U3D_StartRecorder,开始录像;
43.【暂停录像】NT_PB_U3D_PauseRecorder,暂停录像;
44.【停止录像】NT_PB_U3D_StopRecorder,停止录像;
45.【关闭】NT_U3D_Close, 关闭推送实例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++11特性之std:call_once介绍

std:call_once是C11引入的新特性&#xff0c;如需使用&#xff0c;只需要#include <mutex>即可&#xff0c;简单来说std:call_once的作用&#xff0c;确保函数或代码片段在多线程环境下&#xff0c;只需要执行一次&#xff0c;常用的场景如Init()操作或一些系统参数的获取…

2022年了,该学C++还是Java?

最近好多朋友私信我&#xff0c;C好不好学&#xff1f;学C好还是Java好&#xff1f; 我的回答是&#xff1a;C不好学&#xff0c;但你觉得C不好学的话&#xff0c;Java也不好学。因为C难是难在语言本身&#xff0c;java难是难在各种框架和库。 C学习进阶比较陡, 对新手不友好&…

Android平台音视频RTMP推送|GB28181对接之动态水印设计

技术背景 随着移动单兵、智能车载、智慧安防、智能家居、工业仿真、GB28281技术对接等行业的发展&#xff0c;现场已经不再限于采集到视频数据编码打包发送或对接到流媒体服务端&#xff0c;大多场景对视频水印的要求越来越高&#xff0c;从之前的固定位置静态文字水印、png水…

探究C++11智能指针之std::unique_ptr

背景 谈起C&#xff0c;它被公认为最难学的编程语言之一&#xff0c;不仅语法知识点广泛&#xff0c;细节内容之多&#xff0c;学习难度和学习周期也长&#xff0c;导致好多新入行的开发者对C“敬而远之”&#xff0c;甚至“从入门到放弃”。自C11开始&#xff0c;好多C程序员…

C++17新特性之try_emplace与insert_or_assign

由于std::map中&#xff0c;元素的key是唯一的&#xff0c;我们经常遇到这样的场景&#xff0c;向map中插入元素时&#xff0c;先检测map指定的key是否存在&#xff0c;不存在时才做插入操作&#xff0c;如果存在&#xff0c;直接取出来使用&#xff0c;或者key不存在时&#x…

Unity环境下实现Camera高帧率RTMP推送

Unity下RTMP直播背景方面不再赘述&#xff0c;今天主要讨论的是&#xff0c;Unity环境下&#xff0c;如何实现Camera高帧率RTMP推送&#xff0c;这里提到的高帧率&#xff0c;不再局限于常规环境下的30帧&#xff0c;以VR头显为例&#xff0c;更高的帧率&#xff08;比如50帧&a…

如何在Android平台GB28181接入终端实现语音广播和语音对讲

技术背景 在之前的blog&#xff0c;我们以Android平台国标接入终端为例&#xff0c;分别介绍了一些常规的功能&#xff0c;比如REGISTER、CATALOG、INVITE、Keepalive、SUBSCRIBE、NOTIFY等常规操作&#xff0c;今天主要介绍下语音广播和语音对讲这部分。 GB28181平台广播和对…

Android GB28181设备接入端语音广播和语音对讲技术实现探究

上篇文章提到Android端GB28181接入端的语音广播和语音对讲的实现&#xff0c;从spec角度大概介绍了下流程和简单的接口设计&#xff0c;好多开发者私信我&#xff0c;希望展开说一下。其实这块难度不大&#xff0c;只是广播和对讲涉及到双向实现&#xff0c;如果之前没有相关的…

Android native层实现MediaCodec编码H264/HEVC

Android平台在上层实现mediacodec的编码&#xff0c;资料泛滥&#xff0c;已经不再是难事&#xff0c;今天给大家介绍下&#xff0c;如何在Android native层实现MediaCodec编码H264/HEVC&#xff0c;网上千篇一律的接口说明&#xff0c;这里不再赘述&#xff0c;本文主要介绍下…

GB/T 28181联网系统通信协议结构和技术实现

技术回顾 在本文开头&#xff0c;我们先一起回顾下GB/T28181联网系统通信协议结构&#xff1a; 联网系统在进行视音频传输及控制时应建立两个传输通道&#xff1a;会话通道和媒体流通道。 会话通道用于在设备之间建立会话并传输系统控制命令&#xff1b;媒体流通道用于传输视…

Android平台GB28181设备接入端对接编码前后音视频源类型浅析

前言 今天主要对Android平台GB28181设备接入模块支持的接入数据类型&#xff0c;做个简单的汇总&#xff1a; 编码前数据&#xff08;目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型&#xff09;&#xff0c;其中&#xff0c;Android平台前后摄像头数据&…

C++学习之-析构函数必须为虚函数吗?

今天讨论个比较有意思的话题&#xff1a;析构函数是不是必须要为虚函数&#xff1f; 先说答案&#xff1a; 析构函数可以是虚函数&#xff0c;也可以不是虚函数。 再说原因&#xff1a; 析构函数为虚函数的情况&#xff1a;继承 当父类指针释放子类对象时&#xff0c;如果…

如何实现RTMP或RTSP播放端回调YUV/RGB数据?

今天某乎收到个问题推荐&#xff0c;如何实现RTSP回调YUV数据&#xff0c;用于二次处理&#xff1f; 正好前些年我们做RTSP和RTMP直播播放的时候&#xff0c;实现过相关的需求&#xff0c;本文就以Android为例&#xff0c;大概说说具体实现吧。 先说回调yuv或rgb这块意义吧&a…

GB28181控制、传输流程和协议接口之注册|注销和技术实现

注册和注销基本要求 SIP客户端、网关、SIP设备、联网系统等 SIP代理(SIP UA)使用IETFRFC3261中定义的方法 15 GB/T28181—2016Register进行注册和注销。 注册和注销时应进行认证&#xff0c;认证方式应支持数字摘要认证方式&#xff0c;高安全级别的宜支持数字证书的认证方式&…

GB28181状态信息报送解读及Android端国标设备接入技术实现

今天主要聊聊GB/T28181状态信息报送这块&#xff0c;先回顾下协议规范相关细节&#xff0c;然后再针对代码实现&#xff0c;做个简单的说明。 状态消息报送基本要求 当源设备(包括网关、SIP设备、SIP客户端或联网系统)发现工作异常时,应立即向本 SIP监控域 的SIP服务器发送状…

GB28181设备接入端如何实现校时?

在探讨这个问题之前&#xff0c;我们先看看GB/T28181-2016官方文档怎么说的&#xff0c;9.10.1章节校时基本要求提到&#xff1a; 联网内设备支持基于SIP方式或 NTP方式的网络校时功能&#xff0c;标准时间为北京时间。 SIP方式校时见本节具体描述&#xff1b;NTP(见IETFRFC2…

如何在Unity下采集音视频实现轻量级RTSP服务(类似于IPC)

好多开发者在做虚拟仿真、VR教育等场景的时候&#xff0c;遇到个问题&#xff0c;想把头显里面的画面在内网环境下低延迟的同步出来&#xff0c;又不想单独部署流媒体服务器。为此&#xff0c;我们在Unity下&#xff0c;添加了轻量级RTSP服务模块&#xff0c;通过头显端启动个轻…

【Datawhale 大模型基础】第十一章 环境影响

第十一章 环境影响 This blog is based on datawhale files and a paper. The initial consideration revolves around the potential positive or negative direct impact on the environment. Other transformative technological advancements, like the metaverse, are li…

Android GB28181接入端实时位置订阅和上报之-如何获取当前经纬度

我们在做Android平台GB28181的时候&#xff0c;其中实时位置(MobilePosition)订阅和上报这块&#xff0c;涉及到实时经纬度的获取&#xff0c;特别是执法记录、车载系统的那个等场景&#xff0c;几乎就是标配。 今天主要是分享一段实时获取位置的代码&#xff1a; /** Camera…

如何实现Android平台GB28181设备对接Camera2数据

技术背景 在写如何实现Android平台GB28181设备对接Camera2数据说明之前&#xff0c;我在前两年的blog就有针对camera2的RTMP直播推送模块做过技术分享&#xff1a; 在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用…