Android平台轻量级RTSP服务模块二次封装版调用说明

技术背景

在前面的blog,我们发布了Android平台轻量级RTSP服务模块的技术对接说明,好多开发者希望,更黑盒的对接轻量级RTSP服务这块,专注于自身业务逻辑。为此,我们针对Android平台轻量级RTSP服务模块,做了更进一步的封装(LibPublisherWrapper.java)。

技术对接

本文还是以大牛直播SDK Android平台Camera2Demo为例,如果需要测试轻量级RTSP服务模块,分两步:首先启动RTSP服务,RTSP服务启动后,发布RTSP即可,在此之前,可选择设置视频分辨率、软硬编码类型,音频编码类型等,如需关闭轻量级RTSP服务,先停止发布RTSP流,再停止RTSP服务。

onCreate()的时候,调用LibPublisherWrapper的initialize_sdk():

/** MainActivity.java* Author: daniusdk.com*/
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);...context_ = this.getApplicationContext();libPublisher = new SmartPublisherJniV2();LibPublisherWrapper.RTSPServer.initialize_sdk(libPublisher, context_);
}

对应封装代码如下:

public static boolean initialize_sdk(SmartPublisherJniV2 lib_publisher, android.content.Context context) {return sdk_context_.initialize(lib_publisher, context);
}

initalize()具体实现如下:

/** LibPublisherWrapper.java* Author: daniusdk.com*/
public boolean initialize(SmartPublisherJniV2 lib_publisher, android.content.Context context) {if (initialized_)return initialized_result_;if (null == lib_publisher)return false;if (null == context)return false;synchronized (this) {if (initialized_)return initialized_result_;try {int sdk_ret = lib_publisher.InitRtspServer(context);if (0 == sdk_ret)initialized_result_ = true;else {initialized_result_ = false;Log.e(TAG, "call sdk InitRtspServer failed, ret:" + sdk_ret);}} catch (Exception e) {initialized_result_ = false;Log.e(TAG, "call sdk InitRtspServer Exception:", e);}initialized_ = true;return initialized_result_;}
}

启动、停止RTSP服务:

//启动/停止RTSP服务
class ButtonRtspServiceListener implements View.OnClickListener {public void onClick(View v) {if (!rtsp_server_.empty()) {rtsp_server_.reset();btnRtspService.setText("启动RTSP服务");btnRtspPublisher.setEnabled(false);return;}Log.i(TAG, "onClick start rtsp service..");int port = 8554;String user_name = null;String password = null;LibPublisherWrapper.RTSPServer.Handle server_handle = LibPublisherWrapper.RTSPServer.create_and_start_server(libPublisher,port, user_name, password);if (null == server_handle) {Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");return;}rtsp_server_.reset(server_handle);btnRtspService.setText("停止RTSP服务");btnRtspPublisher.setEnabled(true);}
}

发布、停止RTSP流:

//发布/停止RTSP流
class ButtonRtspPublisherListener implements View.OnClickListener {public void onClick(View v) {if (stream_publisher_.is_rtsp_publishing()) {stopRtspPublisher();btnRtspPublisher.setText("发布RTSP流");btnGetRtspSessionNumbers.setEnabled(false);btnRtspService.setEnabled(true);return;}Log.i(TAG, "onClick start rtsp publisher..");InitAndSetConfig();String rtsp_stream_name = "stream1";stream_publisher_.SetRtspStreamName(rtsp_stream_name);stream_publisher_.ClearRtspStreamServer();stream_publisher_.AddRtspStreamServer(rtsp_server_.get_native());if (!stream_publisher_.StartRtspStream()) {stream_publisher_.try_release();Log.e(TAG, "调用发布rtsp流接口失败!");return;}startAudioRecorder();startLayerPostThread();btnRtspPublisher.setText("停止RTSP流");btnGetRtspSessionNumbers.setEnabled(true);btnRtspService.setEnabled(false);}
}

stopRtspPublisher()实现如下:

//停止发布RTSP流
private void stopRtspPublisher() {stream_publisher_.StopRtspStream();stream_publisher_.try_release();if (!stream_publisher_.is_publishing())stopAudioRecorder();
}

其中,InitAndSetConfig()实现如下,通过调研SmartPublisherOpen()接口,生成推送实例句柄。

/** MainActivity.java* Author: daniusdk.com*/
private void InitAndSetConfig() {if (null == libPublisher)return;if (!stream_publisher_.empty())return;Log.i(TAG, "InitAndSetConfig video width: " + video_width_ + ", height" + video_height_ + " imageRotationDegree:" + cameraImageRotationDegree_);int audio_opt = 1;long handle = libPublisher.SmartPublisherOpen(context_, audio_opt, 3,  video_width_, video_height_);if (0==handle) {Log.e(TAG, "sdk open failed!");return;}Log.i(TAG, "publisherHandle=" + handle);int fps = 25;int gop = fps * 3;initialize_publisher(libPublisher, handle, video_width_, video_height_, fps, gop);stream_publisher_.set(libPublisher, handle);
}

对应的initialize_publisher()实现如下,设置软硬编码、帧率、关键帧间隔等。

private boolean initialize_publisher(SmartPublisherJniV2 lib_publisher, long handle, int width, int height, int fps, int gop) {if (null == lib_publisher) {Log.e(TAG, "initialize_publisher lib_publisher is null");return false;}if (0 == handle) {Log.e(TAG, "initialize_publisher handle is 0");return false;}if (videoEncodeType == 1) {int kbps = LibPublisherWrapper.estimate_video_hardware_kbps(width, height, fps, true);Log.i(TAG, "h264HWKbps: " + kbps);int isSupportH264HWEncoder = lib_publisher.SetSmartPublisherVideoHWEncoder(handle, kbps);if (isSupportH264HWEncoder == 0) {lib_publisher.SetNativeMediaNDK(handle, 0);lib_publisher.SetVideoHWEncoderBitrateMode(handle, 1); // 0:CQ, 1:VBR, 2:CBRlib_publisher.SetVideoHWEncoderQuality(handle, 39);lib_publisher.SetAVCHWEncoderProfile(handle, 0x08); // 0x01: Baseline, 0x02: Main, 0x08: High// lib_publisher.SetAVCHWEncoderLevel(handle, 0x200); // Level 3.1// lib_publisher.SetAVCHWEncoderLevel(handle, 0x400); // Level 3.2// lib_publisher.SetAVCHWEncoderLevel(handle, 0x800); // Level 4lib_publisher.SetAVCHWEncoderLevel(handle, 0x1000); // Level 4.1 多数情况下,这个够用了//lib_publisher.SetAVCHWEncoderLevel(handle, 0x2000); // Level 4.2// lib_publisher.SetVideoHWEncoderMaxBitrate(handle, ((long)h264HWKbps)*1300);Log.i(TAG, "Great, it supports h.264 hardware encoder!");}} else if (videoEncodeType == 2) {int kbps = LibPublisherWrapper.estimate_video_hardware_kbps(width, height, fps, false);Log.i(TAG, "hevcHWKbps: " + kbps);int isSupportHevcHWEncoder = lib_publisher.SetSmartPublisherVideoHevcHWEncoder(handle, kbps);if (isSupportHevcHWEncoder == 0) {lib_publisher.SetNativeMediaNDK(handle, 0);lib_publisher.SetVideoHWEncoderBitrateMode(handle, 1); // 0:CQ, 1:VBR, 2:CBRlib_publisher.SetVideoHWEncoderQuality(handle, 39);// libPublisher.SetVideoHWEncoderMaxBitrate(handle, ((long)hevcHWKbps)*1200);Log.i(TAG, "Great, it supports hevc hardware encoder!");}}boolean is_sw_vbr_mode = true;//H.264 software encoderif (is_sw_vbr_mode) {int is_enable_vbr = 1;int video_quality = LibPublisherWrapper.estimate_video_software_quality(width, height, true);int vbr_max_kbps = LibPublisherWrapper.estimate_video_vbr_max_kbps(width, height, fps);lib_publisher.SmartPublisherSetSwVBRMode(handle, is_enable_vbr, video_quality, vbr_max_kbps);}if (is_pcma_) {lib_publisher.SmartPublisherSetAudioCodecType(handle, 3);} else {lib_publisher.SmartPublisherSetAudioCodecType(handle, 1);}lib_publisher.SetSmartPublisherEventCallbackV2(handle, new EventHandlerPublisherV2().set(handler_, record_executor_));lib_publisher.SmartPublisherSetSWVideoEncoderProfile(handle, 3);lib_publisher.SmartPublisherSetSWVideoEncoderSpeed(handle, 2);lib_publisher.SmartPublisherSetGopInterval(handle, gop);lib_publisher.SmartPublisherSetFPS(handle, fps);// lib_publisher.SmartPublisherSetSWVideoBitRate(handle, 600, 1200);boolean is_noise_suppression = true;lib_publisher.SmartPublisherSetNoiseSuppression(handle, is_noise_suppression ? 1 : 0);boolean is_agc = false;lib_publisher.SmartPublisherSetAGC(handle, is_agc ? 1 : 0);int echo_cancel_delay = 0;lib_publisher.SmartPublisherSetEchoCancellation(handle, 1, echo_cancel_delay);return true;
}

发布RTSP流成功后,会回调上来可供拉流的RTSP URL:

private static class EventHandlerPublisherV2 implements NTSmartEventCallbackV2 {@Overridepublic void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {switch (id) {...case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:publisher_event = "RTSP服务URL: " + param3;break;}}
}

获取RTSP Session会话数:

//获取RTSP会话数
class ButtonGetRtspSessionNumbersListener implements View.OnClickListener {public void onClick(View v) {if (rtsp_server_.is_running()) {int session_numbers = rtsp_server_.get_client_session_number();Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);PopRtspSessionNumberDialog(session_numbers);}}
}

对应的Wrapper封装代码如下:

public int get_client_session_number() {if (!is_running())return 0;if (null == lib_publisher_)return 0;long handle = native_handle_.get();if (0 == handle)return 0;try {int ret = lib_publisher_.GetRtspServerClientSessionNumbers(handle);return ret;} catch (Exception e) {Log.e(TAG, "RTSPServer.Handle.get_client_session_number Exception:", e);return 0;}
}

数据投递如下(以Camera2采集为例,如果是其他视频格式,也可以正常对接):

@Override
public void onCameraImageData(Image image) {....for (LibPublisherWrapper i : publisher_array_)i.PostLayerImageYUV420888ByteBuffer(0, 0, 0,planes[0].getBuffer(), y_offset, planes[0].getRowStride(),planes[1].getBuffer(), u_offset, planes[1].getRowStride(),planes[2].getBuffer(), v_offset, planes[2].getRowStride(), planes[1].getPixelStride(),w, h, 0, 0,scale_w, scale_h, scale_filter_mode, rotation_degree);}

音频采集投递设计如下:

void startAudioRecorder() {if (audio_recorder_ != null)return;audio_recorder_ = new NTAudioRecordV2(this);Log.i(TAG, "startAudioRecorder call audio_recorder_.start()+++...");audio_recorder_callback_ = new NTAudioRecordV2CallbackImpl(stream_publisher_, null);audio_recorder_.AddCallback(audio_recorder_callback_);if (!audio_recorder_.Start(is_pcma_ ? 8000 : 44100, 1) ) {audio_recorder_.RemoveCallback(audio_recorder_callback_);audio_recorder_callback_ = null;audio_recorder_ = null;Log.e(TAG, "startAudioRecorder start failed.");}else {Log.i(TAG, "startAudioRecorder call audio_recorder_.start() OK---...");}
}void stopAudioRecorder() {if (null == audio_recorder_)return;Log.i(TAG, "stopAudioRecorder+++");audio_recorder_.Stop();if (audio_recorder_callback_ != null) {audio_recorder_.RemoveCallback(audio_recorder_callback_);audio_recorder_callback_ = null;}audio_recorder_ = null;Log.i(TAG, "stopAudioRecorder---");
}

回调Audio数据的地方,直接投递出去:

private static class NTAudioRecordV2CallbackImpl implements NTAudioRecordV2Callback {private WeakReference<LibPublisherWrapper> publisher_0_;private WeakReference<LibPublisherWrapper> publisher_1_;public NTAudioRecordV2CallbackImpl(LibPublisherWrapper publisher_0) {if (publisher_0 != null)publisher_0_ = new WeakReference<>(publisher_0);}private final LibPublisherWrapper get_publisher_0() {if (publisher_0_ !=null)return publisher_0_.get();return null;}@Overridepublic void onNTAudioRecordV2Frame(ByteBuffer data, int size, int sampleRate, int channel, int per_channel_sample_number) {LibPublisherWrapper publisher_0 = get_publisher_0();if (publisher_0 != null)publisher_0.OnPCMData(data, size, sampleRate, channel, per_channel_sample_number);}
}

onDestroy() 的时候,调用deinitialize_sdk()即可:

@Override
protected void onDestroy() {Log.i(TAG, "activity destory!");stopAudioRecorder();stopRtspPublisher();stream_publisher_.release();rtsp_server_.reset();//如已启用内置服务功能(InitRtspServer),调用UnInitRtspServer, 注意,即便是启动多个RTSP服务,也只需调用UnInitRtspServer一次LibPublisherWrapper.RTSPServer.deinitialize_sdk(libPublisher);stopLayerPostThread();if (camera2Helper != null) {camera2Helper.release();}super.onDestroy();
}

总结

Android平台轻量级RTSP服务模块二次封装版,相对接口版,对接更方便。开发者无需关注底层实现,可以更方便的对接到自己的业务系统。感兴趣的开发者,可以单独跟我们探讨。

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

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

相关文章

解析capl文件生成XML Test Module对应的xml工具

之前一直用的CAPL Test Module来写代码&#xff0c;所有的控制都是在MainTest()函数来实现的&#xff0c;但是有一次&#xff0c;代码都写完了&#xff0c;突然需要用xml的这种方式来实现&#xff0c;很突然&#xff0c;之前也没研究过&#xff0c;整理这个xml整的一身汗&#…

vue3前端开发-小兔鲜项目-登录组件的开发表单验证

vue3前端开发-小兔鲜项目-登录组件的开发表单验证&#xff01;现在开始写登录页面的内容。首先这一次完成基础的首页按钮点击跳转&#xff0c;以及初始化一些简单的表单的输入验证。后期还会继续完善内容。 1&#xff1a;首先还是准备好login页面的组件代码内容。 <script …

企业风险管理的智能监控:Kompas.ai如何预警潜在风险

在企业运营中&#xff0c;风险管理是确保业务连续性和稳健发展的关键环节。随着技术的进步&#xff0c;智能风险管理成为预防损失和识别潜在风险的重要手段。Kompas.ai&#xff0c;作为一款先进的智能监控工具&#xff0c;正通过数据分析和模式识别技术&#xff0c;帮助企业实现…

《华为数据之道》读书笔记六---面向自助消费的数据服务建设

七、从结果管理到过程管理&#xff0c; 从能“看”到能“管” 1、数据赋能业务运营 数字化运营旨在利用数字化技术获取、管理和分析数据&#xff0c;从而为企业的战略决策与业务运营提供可量化的、科学的支撑。 数字化运营归根结底是运营&#xff0c;旨在推动运营效率与能力的…

基站光伏直流叠光能效管理方案

安科瑞 华楠 基站现状和趋势 5G基站是专门提供5G网络服务的公用移动通信基站。5G基站主要用于提供5G空口协议功能&#xff0c;支持与用户设备、核心网之间的通信。按照逻辑功能划分&#xff0c;5G基站可分为5G基带单元与5G射频单元&#xff0c;二者之间可通过CPRI或eCPRI接口…

AFSim仿真系统-架构概览

引言 本文档从最终用户的角度描述了AFSIM架构&#xff0c;旨在帮助最终用户深入了解AFSIM的操作概念。 核心架构 AFSIM基于面向对象的C架构&#xff0c;提供了一种可扩展和模块化的架构&#xff0c;使得许多附加功能能够轻松集成。AFSIM允许新的组件模型&#xff08;如传感器、…

vue3.0学习笔记(二)——生命周期与响应式数据(ref,reactive,toRef,toRefs函数)

1. 组合API-setup函数 使用细节&#xff1a; setup 是一个新的组件选项&#xff0c;作为组件中使用组合API的起点。从组件生命周期来看&#xff0c;它的执行在组件实例创建之前vue2.x的beforeCreate执行。这就意味着在setup函数中 this 还不是组件实例&#xff0c;this 此时是…

SpringBoot 实现图形验证码

一、最终结果展示 二、前端代码 2.1 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title>验证码</title><style>#inputCaptcha {height: 30px;vertical-align: middle;}#verifica…

交易积累-BR

BR指标&#xff0c;全称是“买卖意愿比率”&#xff08;Bull/Bear Ratio&#xff09;&#xff0c;是一个衡量市场买卖力量对比的技术分析工具。BR指标是由中国的技术分析师发展起来的&#xff0c;它通过比较股票或市场在一定时间内的上涨能量与下跌能量来评估市场情绪和潜在的趋…

自动驾驶(八十八)---------通讯之SOMEIP

1. 什么是SOME/IP 服务导向架构&#xff08;SOA&#xff0c;Service-Oriented Architecture&#xff09;是一种设计软件系统的方法&#xff0c;强调通过可重用的服务来实现系统的松散耦合。每个服务是独立的功能单元&#xff0c;可以被不同的应用程序使用。这些服务通过标准化的…

【教程】Node.js+Apache 部署网页全过程(非常详细!)

文章目录 背景0. 前置假设1. 更新系统和安装必要软件2. 打包并上传项目到服务器2.1 识别需要上传的文件2.2 文件归档和压缩2.3 压缩文件上传到服务器2.4 解压文件 3. 配置Node.js应用3.1 启动 PM23.2 确认 PM2 进程 4. 配置Apache反向代理5. 启用必要的Apache模块6. 检查 Apach…

linux如何卸载python3.5

卸载&#xff1a; 1、卸载python3.5 sudo apt-get remove python3.5 2、卸载python3.5及其依赖 sudo apt-get remove --auto-remove python3.5 3、清除python3.5 sudo apt-get purge python3.5 或者 sudo apt-get purge --auto-remove python3.5

AI发展下的伦理挑战:构建未来科技的道德框架

一、引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;我们正处在一个前所未有的科技变革时代。AI不仅在医疗、教育、金融、交通等领域展现出巨大的应用潜力&#xff0c;也在日常生活中扮演着越来越重要的角色。然而&#xff0c;这一技术的迅猛进步也带来…

【OpenCV C++20 学习笔记】操作图片

操作图片 概述图片的导入和保存对导入的图片的操作获取像素值Point类型和图片像素 内存管理和引用计数一些简便操作图片可视化更精确的类型转换 概述 在本专栏的第一篇文章中就介绍了一个用OpenCV处理图片的实例&#xff08;《图片处理基础》&#xff09;&#xff0c;这篇文章…

【Linux】信号3——信号的处理

1.信号的处理 我们都说信号被收到了&#xff0c;可能不会里面处理 信号是什么时候被处理的呢&#xff1f; 前提是我们得知道自己收到了信号&#xff0c;进程就得在合适的时候去查自己的pending表和block表&#xff0c;这些属于内核数据结构&#xff0c;进程一定要处于内核态&a…

学习测试12-车(略)

系统讲解&#xff0c;可以在懂车帝网站去了解汽车结构

DMv8共享存储集群部署

DMv8共享存储集群部署 环境说明 操作系统&#xff1a;centos7.6 服务器&#xff1a;2台虚拟机 达梦数据库版本&#xff1a;达梦V8 安装前准备工作 参考达梦官方文档&#xff1a;https://eco.dameng.com/document/dm/zh-cn/ops/DSC-installation-cluster.html#%E4%B8%80%E3…

如何为 DigitalOcean 上的托管数据库收集可观测指标

DigitalOcean 在 2024 年 5 月开始支持在托管数据库&#xff08;PostgreSQL、MySQL、Redis和Kafka&#xff09;中收集可观测指标。我们将在本偏内容中&#xff0c;告诉大家如何使用部署在 DigitalOcean App Platform 上的网络应用程序&#xff0c;为 DigitalOcean 上的 Postgre…

数据恢复教程:如何从硬盘、SD存储卡、数码相机中恢复误删除数据。

您正在摆弄 Android 设备。突然&#xff0c;您意外删除了一张或多张图片。不用担心&#xff0c;您总能找到一款价格实惠的数据恢复应用。这款先进的软件可帮助 Android 用户从硬盘、安全数字 (SD) 或存储卡以及数码相机中恢复已删除的数据。 Android 上数据被删除的主要原因 在…

厚积薄发,详解 IoTeX 2.0 如何推动 DePIN 赛道迈向新台阶

背 景 DePIN 是加密货币行业的一个新兴垂直领域&#xff0c;也是本轮牛市最重要的叙事之一。DePIN 通常通过发行和分配代币来激励参与者&#xff0c;用户可以通过提供资源、维护网络、参与治理等方式获得代币奖励并产生直接的经济收益&#xff0c;从而重新洗牌财富分配方…