Android平台如何实现多路低延迟RTSP|RTMP播放?

news2024/11/22 10:55:43

技术背景

实际上,我们在2015年做Android平台RTSP、RTMP播放模块的时候,第一版就支持了多实例播放,因为SDK设计比较灵活,做个简单的player实例封装即可实现多实例播放(Android Unity的就有多路demo),所以官方一直没有正式demo,本次也是有个开发者提到,希望测试下我们多路播放的效果,自己又不想做封装,索性给做个版本。

技术实现

废话不多说,先上图:

我们针对的功能展示,主要是播放和录像这块,先说播放:

/*
 * SmartPlayer.java
 * Author: https://daniusdk.com
 * Created by DaniuLive on 2015/09/26.
 */
class ButtonPlayback1Listener implements View.OnClickListener {
	public void onClick(View v) {
		if (stream_player_1_.is_playing()) {
			Log.i(TAG, "Stop player1..");

			boolean iRet = stream_player_1_.StopPlayer();

			if (!iRet) {
				Log.e(TAG, "Call StopPlayer failed..");
				return;
			}

			stream_player_1_.try_release();
			btn_playback1.setText("开始播放1");
			SetViewVisibility(surface_view_1_);
		} else {
			Log.i(TAG, "Start playback stream1++");

			int play_buffer = 0;
			int is_using_tcp = 0;
			if(!stream_player_1_.OpenPlayerHandle(playback_url_1_, play_buffer, is_using_tcp))
				return;

			stream_player_1_.SetView(surface_view_1_);

			boolean is_mute = false;
			boolean iPlaybackRet = stream_player_1_.StartPlayer(isHardwareDecoder, is_enable_hardware_render_mode, is_mute);
			if (!iPlaybackRet) {
				Log.e(TAG, "Call StartPlayer failed..");
				return;
			}

			btn_playback1.setText("停止播放1");
		}
	}
}

对应的OpenPlayerHandle()实现如下:

/*
 * LibPlayerWrapper.java.java
 * Author: https://daniusdk.com
 */
public boolean OpenPlayerHandle(String playback_url, int play_buffer, int is_using_tcp) {

	if (check_native_handle())
		return true;

	if(!isValidRtspOrRtmpUrl(playback_url))
		return false;

	long handle = lib_player_.SmartPlayerOpen(application_context());
	if (0==handle) {
		Log.e(TAG, "sdk open failed!");
		return false;
	}

	lib_player_.SetSmartPlayerEventCallbackV2(handle, new EventHandleV2());

	lib_player_.SmartPlayerSetBuffer(handle, play_buffer);

	// set report download speed(默认2秒一次回调 用户可自行调整report间隔)
	lib_player_.SmartPlayerSetReportDownloadSpeed(handle, 1, 4);

	boolean isFastStartup = true;
	lib_player_.SmartPlayerSetFastStartup(handle, isFastStartup ? 1 : 0);

	//设置RTSP超时时间
	int rtsp_timeout = 10;
	lib_player_.SmartPlayerSetRTSPTimeout(handle, rtsp_timeout);

	//设置RTSP TCP/UDP模式自动切换
	int is_auto_switch_tcp_udp = 1;
	lib_player_.SmartPlayerSetRTSPAutoSwitchTcpUdp(handle, is_auto_switch_tcp_udp);

	lib_player_.SmartPlayerSaveImageFlag(handle, 1);

	// It only used when playback RTSP stream..
	lib_player_.SmartPlayerSetRTSPTcpMode(handle, is_using_tcp);

	lib_player_.DisableEnhancedRTMP(handle, 0);

	lib_player_.SmartPlayerSetUrl(handle, playback_url);

	set(handle);

	return true;
}

对应的开始播放、停止播放设计:

/*
 * LibPlayerWrapper.java
 * Author: https://daniusdk.com
 */
public boolean StartPlayer(boolean is_hardware_decoder, boolean is_enable_hardware_render_mode, boolean is_mute) {
	if (is_playing()) {
		Log.e(TAG, "already playing, native_handle:" + get());
		return false;
	}

	SetPlayerParam(is_hardware_decoder, is_enable_hardware_render_mode, is_mute);

	int ret = lib_player_.SmartPlayerStartPlay(get());
	if (ret != OK) {
		Log.e(TAG, "call StartPlay failed, native_handle:" + get() + ", ret:" + ret);
		return false;
	}

	write_lock_.lock();
	try {
		this.is_playing_ = true;
	} finally {
		write_lock_.unlock();
	}

	Log.i(TAG, "call StartPlayer OK, native_handle:" + get());
	return true;
}

public boolean StopPlayer() {
	if (!check_native_handle())
		return false;

	if (!is_playing()) {
		Log.w(TAG, "it's not playing, native_handle:" + get());
		return false;
	}

	boolean is_need_call = false;
	write_lock_.lock();
	try {
		if (this.is_playing_) {
			this.is_playing_ = false;
			is_need_call = true;
		}
	} finally {
		write_lock_.unlock();
	}

	if (is_need_call)
		lib_player_.SmartPlayerStopPlay(get());

	return true;
}

录像设计:

/*
 * SmartPlayer.java
 * Author: https://daniusdk.com
 */
class ButtonRecorder1Listener implements View.OnClickListener {
	public void onClick(View v) {
		if (stream_player_1_.is_recording()) {
			Log.i(TAG, "Stop recorder1..");

			boolean iRet = stream_player_1_.StopRecorder();

			if (!iRet) {
				Log.e(TAG, "Call StopRecorder failed..");
				return;
			}

			stream_player_1_.try_release();
			btn_recorder1.setText("开始录像1");
		} else {
			Log.i(TAG, "Start recorder stream1++");

			int play_buffer = 0;
			int is_using_tcp = 0;

			if(!stream_player_1_.OpenPlayerHandle(playback_url_1_, play_buffer, is_using_tcp))
				return;

			stream_player_1_.ConfigRecorderParam(recDir, 400, 1, 1, 1);

			boolean iRecRet = stream_player_1_.StartRecorder();
			if (!iRecRet) {
				Log.e(TAG, "Call StartRecorder failed..");
				return;
			}

			btn_recorder1.setText("停止录像1");
		}
	}
}

录像参数配置选项:

/*
 * LibPlayerWrapper.java
 * Author: https://daniusdk.com
 */
public boolean ConfigRecorderParam(String rec_dir, int file_max_size, int is_transcode_aac,
								   int is_record_video, int is_record_audio) {

	if(!check_native_handle())
		return false;

	if (null == rec_dir || rec_dir.isEmpty())
		return false;

	int ret = lib_player_.SmartPlayerCreateFileDirectory(rec_dir);
	if (ret != 0) {
		Log.e(TAG, "Create record dir failed, path:" + rec_dir);
		return false;
	}

	if (lib_player_.SmartPlayerSetRecorderDirectory(get(), rec_dir) != 0) {
		Log.e(TAG, "Set record dir failed , path:" + rec_dir);
		return false;
	}

	if (lib_player_.SmartPlayerSetRecorderFileMaxSize(get(),file_max_size) != 0) {
		Log.e(TAG, "SmartPlayerSetRecorderFileMaxSize failed.");
		return false;
	}

	lib_player_.SmartPlayerSetRecorderAudioTranscodeAAC(get(), is_transcode_aac);

	// 更细粒度控制录像的, 一般情况无需调用
	lib_player_.SmartPlayerSetRecorderVideo(get(), is_record_video);
	lib_player_.SmartPlayerSetRecorderAudio(get(), is_record_audio);
	return true;
}

开始录像、结束录像:

/*
 * LibPlayerWrapper.java
 * Author: https://daniusdk.com
 */
public boolean StartRecorder() {

	if (is_recording()) {
		Log.e(TAG, "already recording, native_handle:" + get());
		return false;
	}

	int ret = lib_player_.SmartPlayerStartRecorder(get());
	if (ret != OK) {
		Log.e(TAG, "call SmartPlayerStartRecorder failed, native_handle:" + get() + ", ret:" + ret);
		return false;
	}

	write_lock_.lock();
	try {
		this.is_recording_ = true;
	} finally {
		write_lock_.unlock();
	}

	Log.i(TAG, "call SmartPlayerStartRecorder OK, native_handle:" + get());
	return true;
}

public boolean StopRecorder() {
	if (!check_native_handle())
		return false;

	if (!is_recording()) {
		Log.w(TAG, "it's not recording, native_handle:" + get());
		return false;
	}

	boolean is_need_call = false;
	write_lock_.lock();
	try {
		if (this.is_recording_) {
			this.is_recording_ = false;
			is_need_call = true;
		}
	} finally {
		write_lock_.unlock();
	}

	if (is_need_call)
		lib_player_.SmartPlayerStopRecorder(get());

	return true;
}

总结

说了这么多,以RTSP播放为例,大概说下实现的功能:

  • [支持播放协议]高稳定、超低延迟、业内首屈一指的RTSP直播播放器SDK;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持H.265、H.264,此外,还支持RTSP MJPEG播放;
  •  [音频格式]支持AAC/PCMA/PCMU;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Windows/Android/iOS支持特定机型H.264硬解;
  •  [H.265硬解]Windows/Android/iOS支持特定机型H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [RTSP模式设置]支持RTSP TCP/UDP模式设置;
  •  [RTSP TCP/UDP自动切换]支持RTSP TCP、UDP模式自动切换;
  •  [RTSP超时设置]支持RTSP超时时间设置,单位:秒;
  •  [RTSP 401认证处理]支持上报RTSP 401事件,如URL携带鉴权信息,会自动处理;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时音量调节]支持播放过程中实时调节音量;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [只播关键帧]Windows平台支持实时设置是否只播放关键帧;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [等比例缩放]支持图像等比例缩放绘制(Android设置surface模式硬解模式不支持);
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]完美支持和录像SDK组合使用。

上面只是简单的播放、录像的演示,除此之外,大牛直播SDK的RTSP、RTMP播放器海康实现播放缓冲设置、软硬解码设置、实时快照、实时音量调节、实时解码后数据回调等。毫秒级延迟,完全满足对延迟、稳定性要求苛刻的场景下。感兴趣的开发者,可以单独和我沟通。

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

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

相关文章

arsetryhtehrwgefwadasdadasd

48b91400000080f7ffff48b8bd427ae5d594bfd6488b0948f7e148b8cdcccccccccccccc48c1ea1748f7e24c8bea49c1ed02 直接在windbg中把执行内存修改为上面这一串字节序列,运行完成后r13中将包含当前时间戳,可使用如下代码转换成人类可阅读时间格式 /*代码BEGIN*…

Codesys 编程实现随机数字+仿照rand()原理+代码下载

目录 一、C语言中rand()随机数的演示及问题 二、同样的原理,在Codesys中实现随机数 三、codesys在线仿真验证功能 四、代码下载 一、C语言中rand()随机数的演示及问题 (1)只用rand(…

WebGIS如何加载微件

本篇文章以加载切换底图微件做示范 首先,添加require "esri/widgets/ScaleBar",//比例尺"esri/widgets/Legend",//图例"esri/widgets/basemapGallery" 然后添加加载切换底图的组件代码 const basemapGallery new BasemapGallery(…

探索CSS clip-path: polygon():塑造元素的无限可能

在CSS的世界里,clip-path 属性赋予了开发者前所未有的能力,让他们能够以非传统的方式裁剪页面元素,创造出独特的视觉效果。其中,polygon() 函数尤其强大,它允许你使用多边形来定义裁剪区域的形状,从而实现各…

为什么idea总是提示将内部类设置为static

在写一些内部类的时候,Idea总是提示要设置为static,你知道为什么吗 在Java中,内部类可以被声明为static,这种内部类称为静态内部类(Static Nested Class)。静态内部类和非静态内部类有显著的区别&#xf…

分层Agent

分层Teams 分层Agent创建tool研究团队工具文档编写团队工具 通用能力定义Agent团队研究团队文档编写团队 添加图层 分层Agent 在前面的示例(Agent管理)中,我们引入了单个管理节点的概念,用于在不同工作节点之间路由工作。 但是&a…

气膜馆的保温措施—轻空间

气膜馆是一种新型的建筑形式,广泛应用于体育场馆、仓储、展览等多个领域。其主要特点是通过气体压力支撑膜结构,实现大跨度无柱空间。为了保证气膜馆在不同气候条件下的使用舒适性和能源效率,保温措施至关重要。以下是气膜馆常见的保温措施及…

【大分享04】OFD版式赋能政务服务电子文件归档和电子档案管理

关注我们 - 数字罗塞塔计划 - 本篇是参加由电子文件管理推进联盟联合数字罗塞塔计划发起的“大分享”活动投稿文章,来自北京数科网维技术有限责任公司,作者:张严。 PART1 政务服务电子文件归档和电子档案管理背景 政务服务是政务服务机构…

充电学习—3、Uevent机制和其在android层的实现

sysfs 是 Linux userspace 和 kernel 进行交互的一个媒介。通过 sysfs,userspace 可以主动去读写 kernel 的一些数据,同样的, kernel 也可以主动将一些“变化”告知给 userspace。也就是说,通过sysfs,userspace 和 ker…

C++中list容器常用接口

list的基本定义: 在C中&#xff0c;list被定义为一个双向链表容器。它是标准模板库&#xff08;STL&#xff09;中的一部分&#xff0c;位于<list>头文件中。 list是一个通用模板类&#xff0c;可以存储任何类型的数据&#xff0c;因此它是一个模板类。它实现了双向链表数…

【MATLAB】- 随笔 :如何检测一个字符串数组中是否包含自己想要的序列

1. 问题重述 比如我现在有一个 strArray [“a”, “1”, “2”, “b”]; 我想确定里面是否包含[“1”, “2”]; &#xff0c;由于MATLAB基础库中没有现成的函数可以直接检查连续子数组或连续多个元素的序列&#xff0c;下面给出自定义函数来实现这一功能。 2. 自定义函数 2…

vivado在implementation时出现错误[Place 30-494] The design is empty的一个可能原因和解决方法

在查询类似帖子时我发现这一问题是由于在设计实现时vivado认为没有输出端口所以报错。 于是在.v文件中我添加了一个随意的端口&#xff0c;并且在.xdc文件中为它分配了管脚 这样做的确可以让设计实现的过程顺利进行&#xff0c;但是会发现在summary中&#xff0c;设计实现的…

哈尔滨等保测评驱动下的智慧城市建设思考

面对滚滚而来的大数据时代&#xff0c;信息安全等级保护测评&#xff08;简称等保测评&#xff09;对城市发展的推动作用不容忽视。作为黑龙江省的省会&#xff0c;哈尔滨在智慧城市建设上的积极探索和实践&#xff0c;必须以完善的等保测评体系为前提&#xff0c;确保信息的安…

C++性能分析工具gperftools安装教程与使用案例分析

目录 1 什么是gperftools&#xff1f;2 gperftools安装流程3 gperftools使用案例 1 什么是gperftools&#xff1f; gperftools是Google Performance Tools的简称&#xff0c;是由谷歌开发的一套性能分析工具。它主要用于帮助开发人员进行性能分析和优化&#xff0c;使他们能够…

05眼动识别软件详情2波形优化

对应视频链接点击直达 01项目点击下载&#xff0c;可直接运行&#xff08;含数据库&#xff09; 05眼动识别软件详情2 对应视频链接点击直达期望的数据展示数据波形对比如何实现几种常用滤波介绍维纳滤波巴特沃斯滤波器中值滤波排序滤波 推荐 结语其他以下是废话 原始数据的波…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] K小姐和A先生的聚餐计划(200分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

[自动驾驶 SoC]-4 特斯拉FSD

FSD, 参考资料来源FSD Chip - Tesla - WikiChip 另外可参考笔者之前分享文章&#xff1a;[自动驾驶技术]-6 Tesla自动驾驶方案之硬件&#xff08;AI Day 2021&#xff09;&#xff0c;​​​​​​​[自动驾驶技术]-8 Tesla自动驾驶方案之硬件&#xff08;AI Day 2022&#xf…

短信群发平台众多,如何挑选一家靠谱的合作伙伴?

随着通信技术的不断发展和智能手机的普及&#xff0c;短信群发平台在企业营销、客户通知、活动推广等领域的应用越来越广泛。然而&#xff0c;市场上的短信群发平台琳琅满目&#xff0c;如何从中挑选出一家靠谱、高效、安全的合作伙伴&#xff0c;成为了许多企业和个人关注的焦…

Matlab r2023a v23.2.0 解锁版安装步骤 (工程计算商业数学软件)

前言 Matlab&#xff08;矩阵实验室&#xff09;是全球领先的数学计算软件开发商美国 MathWorks 公司研发的一款面向科学与工程计算的高级语言的商业数学软件&#xff0c;集算法开发、数据分析、可视化和数值计算于一体的编程环境&#xff0c;其核心是仿真交互式矩阵计算&…

管理员如何踢掉登录用户?

这是 Spring Security 学习小组有小伙伴提的一个问题&#xff1a; 感觉这个问题还有点意思&#xff0c;拿出来和各位小伙伴一起分享下。 一 问题分析 首先大家注意限制条件&#xff1a;常规 Session 方案。 如果不是这几个字&#xff0c;这个问题根本就不是问题&#xff0c;…