Android轻量级RTSP服务使用场景分析和设计探讨

技术背景

好多开发者,对我们Android平台轻量级RTSP服务模块有些陌生,不知道这个模块具体适用于怎样的场景,有什么优缺点,实际上,我们的Android平台轻量级RTSP服务模块更适用于内网环境下、对并发要求不高的场景,实现低成本、低延迟的音视频实时传输。本文就上述问题,做个技术探讨,先说适用场景:

1. 内网环境

  • 无纸化/电子教室:在这些环境中,需要实现音视频的低延迟传输,而轻量级RTSP服务能够避免单独部署RTSP或RTMP服务器,简化部署流程,同时满足对并发要求不高的场景。
  • 车载自组网:在多辆车组成的网络中,轻量级RTSP服务可以确保车辆间实时视频传输,帮助驾驶员了解前方路况等信息。
  • 视频监控记录仪:把Android终端做成类似于网络摄像头的执法记录类设备,更便携。
  • 智能安全帽:用于内网自组网环境智能安全帽,实时巡检时可录像可快照,指挥中心还可实时查看现场情况。

2. 本地音视频数据传输

  • 摄像头和麦克风数据:将本地的摄像头和麦克风采集的音视频数据编码后,通过轻量级RTSP服务汇聚并对外提供可供拉流的RTSP URL,实现音视频数据的实时传输。
  • 屏幕共享:除了摄像头和麦克风,轻量级RTSP服务还支持屏幕共享功能,可以将设备屏幕内容编码后通过RTSP服务进行传输。

3. 低成本解决方案

  • 避免额外服务器部署:轻量级RTSP服务直接在Android设备上实现,无需额外部署RTSP或RTMP服务器,降低了成本。
  • 简化产品设计:对于需要实时音视频传输但不想增加产品复杂度的开发者来说,轻量级RTSP服务是一个理想的选择。

4. 特定功能支持

  • 支持多种音视频格式:轻量级RTSP服务通常支持H.264/H.265视频编码和AAC音频编码,满足不同的音视频传输需求。
  • 鉴权、单播和组播模式:支持RTSP鉴权功能,保障传输安全;同时支持单播和组播模式,满足不同的传输需求。
  • 多服务并发:考虑到单个服务承载能力,支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

Android轻量级RTSP服务优缺点探讨

优点

  1. 低成本与简化部署
    • 轻量级RTSP服务直接在Android设备上实现,无需额外部署RTSP或RTMP服务器,从而降低了硬件和运营成本。
    • 简化了产品设计和部署流程,使得开发者能够更快速地集成实时音视频传输功能。
  2. 高效与低延迟
    • RTSP协议本身对实时性有较好的支持,能够提供低延迟的音视频传输服务。
    • 轻量级RTSP服务通过优化传输机制和减少中间环节,进一步提高了传输效率。
  3. 灵活性与可扩展性
    • 支持多种音视频编码格式,如H.264/H.265视频编码和AAC音频编码,满足不同场景下的传输需求。
    • 支持RTSP鉴权功能,保障传输安全;同时支持单播和组播模式,满足不同的传输需求。
    • 考虑到单个服务承载能力,支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数,便于管理和扩展。
  4. 内网环境友好
    • 特别适用于内网环境下的音视频传输,如企业内网、校园网络等。
    • 在这些环境中,轻量级RTSP服务能够避免网络延迟和带宽限制等问题,提供稳定的音视频传输服务。
  5. 易于集成与调试
    • 提供了丰富的接口和文档支持,便于开发者进行集成和调试工作。
    • 开发者可以根据实际需求进行定制开发,实现更加个性化的功能。

缺点

  1. 并发能力有限
    1. 轻量级RTSP服务通常适用于对并发要求不高的场景。在高并发场景下,可能需要考虑增加服务器数量或使用更高级的流媒体服务器解决方案。
  2. 功能相对单一
    • 相比于专业的流媒体服务器解决方案,轻量级RTSP服务的功能可能相对单一。如果需要实现更复杂的音视频处理功能(如转码、录制等),可能需要结合其他工具或服务来实现。

如何实现Android轻量级RTSP服务

在Android平台上实现轻量级RTSP服务,主要涉及到视频和音频的采集、编码、封装成RTSP流,并通过网络进行传输。由于Android原生API并不直接支持RTSP服务器的功能,因此通常需要使用第三方库或自行实现RTSP服务器的逻辑。以下是一个基本的实现步骤和思路:

1. 自行开发

如果你对RTSP协议有较深的理解,并希望实现自定义的RTSP服务器,你可以考虑使用Java或C++(通过JNI)来编写RTSP服务器的核心逻辑。这通常涉及到解析RTSP请求、管理会话、控制音视频流等。

2. 音视频采集与编码

在Android上,你可以使用MediaCodec API来进行音视频数据的编码。MediaCodec是Android提供的一个强大的API,支持多种音视频编码格式,如H.264、AAC等。

  • 视频采集:可以使用Camera2 API(Android 5.0及以上)或Camera API(较旧的Android版本)来捕获视频帧。
  • 音频采集:可以使用AudioRecord API来捕获音频数据。

3. 封装成RTSP流

将编码后的音视频数据封装成RTSP流需要遵循RTSP协议。这通常涉及到将音视频数据封装成RTP(Real-time Transport Protocol)包,并通过RTSP协议控制这些包的传输。

  • RTP封装:RTP是用于在互联网上传输音频和视频数据的协议。你需要将编码后的音视频数据按照RTP包的格式进行封装。
  • RTSP控制:RTSP协议用于控制音频/视频的播放停止等。你需要实现RTSP服务器以处理这些控制请求。

4. 网络传输

使用Socket编程在Android上进行网络传输。你可以使用TCP或UDP协议来传输RTP包。RTSP控制信令通常使用TCP,而RTP数据包则常使用UDP。

5. 集成与测试

将上述所有组件集成到你的Android应用中,并进行充分的测试以确保RTSP服务的稳定性和性能。测试应包括不同的网络环境、设备性能以及并发请求等场景。

6. 注意事项

  • 性能优化:音视频处理和网络传输都是资源密集型的任务,因此需要注意性能优化,如合理使用线程、避免内存泄漏等。
  • 兼容性:由于Android设备的多样性和不同版本的API差异,你的RTSP服务需要尽可能兼容更多的设备和Android版本。
  • 权限:确保你的应用已正确声明了所有必要的权限,以便进行音视频采集和网络通信。

轻量级RTSP服务设计示例

文本以大牛直播SDK的Android平台轻量级RTSP服务模块为例,介绍下我们的开发思路和功能设计。

数据接入类型

轻量级RTSP服务数据源,支持编码前、编码后数据对接:

  • 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型);
  • 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据);
  • 拉取RTSP或RTMP流并注入轻量级RTSP服务模块,组合形成内置RTSP网关模块。

接口设计

我们的接口,RTSP服务和流发布分离,并添加了获取RTSP Session链接数接口,便于轻量级RTSP服务模块统计实时并发连接数。

Android内置轻量级RTSP服务SDK接口详解

调用描述

接口

接口描述

SmartRTSPServerSDK

初始化RTSP Server

InitRtspServer

Init rtsp server(和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用)

创建一个rtsp server

OpenRtspServer

创建一个rtsp server,返回rtsp server句柄

设置端口

SetRtspServerPort

设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口

设置鉴权用户名、密码

SetRtspServerUserNamePassword

设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置

获取rtsp server当前会话数

GetRtspServerClientSessionNumbers

获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用

启动rtsp server

StartRtspServer

启动rtsp server

停止rtsp server

StopRtspServer

停止rtsp server

关闭rtsp server

CloseRtspServer

关闭rtsp server

UnInit rtsp server

UnInitRtspServer

UnInit rtsp server(和InitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次UnInitRtspServer)

SmartRTSPServerSDK供Publisher调用的接口

设置rtsp的流名称

SetRtspStreamName

设置rtsp的流名称

给要发布的rtsp流设置rtsp server

AddRtspStreamServer

给要发布的rtsp流设置rtsp server, 一个流可以发布到多个rtsp server上,rtsp server的创建启动请参考OpenRtspServer和StartRtspServer接口

清除设置的rtsp server

ClearRtspStreamServer

清除设置的rtsp server

启动rtsp流

StartRtspStream

启动rtsp流

停止rtsp流

StopRtspStream

停止rtsp流

功能支持

  •  ​[视频格式]H.264/H.265(Android H.265硬编码);
  •  [音频格式]G.711 A律、AAC;
  • 协议:RTSP;
  •  [音量调节]Android平台采集端支持实时音量调节;
  •  [H.264硬编码]支持H.264特定机型硬编码;
  •  [H.265硬编码]支持H.265特定机型硬编码;
  • [音视频]支持纯音频/纯视频/音视频;
  • [摄像头]支持采集过程中,前后摄像头实时切换;
  • 支持帧率、关键帧间隔(GOP)、码率(bit-rate)设置;
  • [实时水印]支持动态文字水印、png水印;
  • [实时快照]支持实时快照;
  • [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  • [外部编码前视频数据对接]支持YUV数据对接;
  • [外部编码前音频数据对接]支持PCM对接;
  • [外部编码后视频数据对接]支持外部H.264、H.265数据对接;
  • [外部编码后音频数据对接]外部AAC数据对接;
  • [扩展录像功能]支持和录像SDK组合使用,录像相关功能。​
  • 支持RTSP端口设置;
  • 支持RTSP鉴权用户名、密码设置;
  • 支持获取当前RTSP服务会话连接数;
  • 支持Android 5.1及以上版本。

接口调用示例

本文以大牛直播SDK Android平台Camera2Demo为例,启动RTSP服务、发布RTSP流之前,可以先选择视频分辨率、软编还是硬编码,音频是PCMA还是AAC编码等基础设置,其他参数的设置,可以参考下面InitAndSetConfig()。

以Android平台Camera2对接为例,先初始化RTSP Server:

/*
 * MainActivity.java
 * Author: daniusdk.com
 * WeChat: xinsheng120
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	...

	context_ = this.getApplicationContext();
	
	libPublisher = new SmartPublisherJniV2();

	libPublisher.InitRtspServer(context_);      //和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用
}

启动、停止RTSP服务:

//启动/停止RTSP服务
class ButtonRtspServiceListener implements View.OnClickListener {
	public void onClick(View v) {
		if (isRTSPServiceRunning) {
			stopRtspService();

			btnRtspService.setText("启动RTSP服务");
			btnRtspPublisher.setEnabled(false);

			isRTSPServiceRunning = false;
			return;
		}

		Log.i(TAG, "onClick start rtsp service..");

		rtsp_handle_ = libPublisher.OpenRtspServer(0);

		if (rtsp_handle_ == 0) {
			Log.e(TAG, "创建rtsp server实例失败! 请检查SDK有效性");
		} else {
			int port = 8554;
			if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) {
				libPublisher.CloseRtspServer(rtsp_handle_);
				rtsp_handle_ = 0;
				Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
			}

			if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) {
				Log.i(TAG, "启动rtsp server 成功!");
			} else {
				libPublisher.CloseRtspServer(rtsp_handle_);
				rtsp_handle_ = 0;
				Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
			}

			btnRtspService.setText("停止RTSP服务");
			btnRtspPublisher.setEnabled(true);

			isRTSPServiceRunning = true;
		}
	}
}

stopRtspService()实现如下:

//停止RTSP服务
private void stopRtspService() {
	if(!isRTSPServiceRunning)
	{
		return;
	}
	if (libPublisher != null && rtsp_handle_ != 0) {
		libPublisher.StopRtspServer(rtsp_handle_);
		libPublisher.CloseRtspServer(rtsp_handle_);
		rtsp_handle_ = 0;
	}
}

发布、停止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_handle_);

		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:CBR
			lib_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 4
			lib_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:CBR
			lib_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 encoder
	if (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 {
	@Override
	public 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 (libPublisher != null && rtsp_handle_ != 0) {
			int session_numbers = libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);

			Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);

			PopRtspSessionNumberDialog(session_numbers);
		}
	}
}

//当前RTSP会话数弹出框
private void PopRtspSessionNumberDialog(int session_numbers) {
	final EditText inputUrlTxt = new EditText(this);
	inputUrlTxt.setFocusable(true);
	inputUrlTxt.setEnabled(false);

	String session_numbers_tag = "RTSP服务当前客户会话数: " + session_numbers;
	inputUrlTxt.setText(session_numbers_tag);

	AlertDialog.Builder builderUrl = new AlertDialog.Builder(this);
	builderUrl
			.setTitle("内置RTSP服务")
			.setView(inputUrlTxt).setNegativeButton("确定", null);
	builderUrl.show();
}

总结

Android平台轻量级RTSP服务模块,除了可以对接编码前音视频数据外,还还需要支持对接编码后音视频数据,并实现本地录像、快照等。实现一个完整的轻量级RTSP服务是一个相对复杂的任务,需要对音视频处理、网络编程和RTSP协议有深入的理解。如果你没有这些经验,使用现成的第三方库可能是一个更好的选择。感兴趣的开发者,可以单独跟我们探讨。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Activiti7《第九式:破气式》——流畅驱动工作流进程。面试题大全

冲冲冲!开干 这篇文章将分为九个篇章,带你逐步掌握工作流的核心知识。“破气式”,代表着工作流中的 无形之力,它是贯穿整个流程的 关键驱动 不知不觉已经到了独孤九剑最后一式了,我相信到这里之后各位都已经出神入化…

windows桌面管理软件推荐:一键整理桌面!美化电脑桌面小助手!

windows桌面管理软件推荐来咯!在繁忙的工作和生活中,一个整洁、有序的电脑桌面不仅能提升工作效率,还能带来愉悦的视觉体验。然而,随着文件的增多,桌面往往变得杂乱无章。幸运的是,市面上有许多优秀的Windo…

构建高可用和高防御力的云服务架构第五部分:PolarDB(5/5)

引言 云计算与数据库服务 云计算作为一种革命性的技术,已经深刻改变了信息技术行业的面貌。它通过提供按需分配的计算资源,使得数据存储、处理和分析变得更加灵活和高效。在云计算的众多服务中,数据库服务扮演着核心角色。数据库服务不仅负…

​地质图制图规范大全资料分享

我们在《2024年最新测绘地理信息规范在线查看下载》一文整理460个测绘地理信息相关规范的在线查看链接。 现在我们又整理了地质图制图规范大全分享给大家,你可以在文末查看该文档的领取方法。 地质图制图规范大全 这些地质图制图规范来自地质科学数据出版系统&am…

Rustrover2024.2 正式发布:个人非商用免费,泰裤辣

如果这个世界本身 已经足够荒唐 那究竟什么才能算是疯狂 爱情就是这样 一旦错过了 就会有另一个人代替 我们知道 jetbrains 在今年的早些时候正式为 rust 语言发布了专用的 IDE ,也就是 rustrover。如今 rustrover 也正式跻身为 jetbrains IDE 系列的一员猛将。…

network request to https://registry.npmjs.org/xxx failed, reason: connect ETIM

目录: 1、问题描述2、解决方案3、npm镜像仓库替换 1、问题描述 npm install 时,报错:npm ERR! network request to https://registry.npmjs.org/postcss-pxtorem failed, reason: connect ETIMEDOU npm ERR! code ETIMEDOUT npm ERR! errno…

COMTRADE 录波文件 | 可视化工具 | 电能质量查看软件

COMTRADE 录波文件 | 可视化工具 | 电能质量查看软件 主要功能介绍 支持 IEEE Std C37.111-1991/1999/2013 规范。读取 ASCII 或二进制 COMTRADE 文件。查看来自 COMTRADE 配置文件的模拟和数字通道列表。将图表导出为 SVG、BMP、JPEG 和 PNG 图形格式。将显示的观察结果以 C…

携手长江存储,构建高性能分布式存储

近年来,《金融科技(FinTech)发展规划(2022-2025 年)》《关于银行业保险业数字化转型的指导意见》《金融标准化“十四五”发展规划》等金融监管政策陆续出台,金融机构对于数据基础设施的升级部署&#xff0c…

工业建模设计软件概览与SOLIDWORKS深度解析

在当今快速发展的工业领域,高效的建模设计软件是工程师和设计师不可或缺的工具。这些软件不仅提高了设计的精确度,还大幅缩短了产品从概念到市场的周期。本文将为您介绍当前市场上主流的工业建模设计软件,并重点介绍SOLIDWORKS的优势和应用。…

10.软件工程知识详解上

软件工程概述 软件开发生命周期 软件定义时期:包括可行性研究和详细需求分析过程,任务是确定软件开发工程必须完成的总目标,具体可分成问题定义、可行性研究、需求分析等。软件开发时期:就是软件的设计与实现,可分成…

汽车总线之----FlexRay总线

Introduction 随着汽车智能化发展,车辆开发的ECU数量不断增加,人们对汽车系统的各个性能方面提出了更高的需求,比如更多的数据交互,更高的传输带宽等。现如今人们广泛接受电子功能来提高驾驶安全性,像ABS防抱死系统&a…

git push出错Push cannot contain secrets

报错原因: 因为你的代码里面包含了github token明文信息,github担心你的token会泄漏,所以就不允许你推送这些内容。 解决办法: 需要先把代码里面的github token信息删除掉,并且删掉之前的历史提交,只要包…

关于ShuffleNetV1中的channel shuffle操作【代码分析】

1. 官方给出的代码 旷视科技在自己的开源GitHub上给出的channel shuffle相关代码如下图所示: 分析上图中的代码,旷视科技将channel shuffle这个操作视为一个函数,函数传入的参数是输入张量x,x的shape为(batchsize, num_ch…

Ceph 基本架构(一)

Ceph架构图 Ceph整体组成 Ceph 是一个开源的分布式存储系统,设计用于提供优秀的性能、可靠性和可扩展性。Ceph 的架构主要由几个核心组件构成,每个组件都有特定的功能,共同协作以实现高可用性和数据的一致性。 以下是 Ceph 的整体架构及其…

大数据处理从零开始————3.Hadoop伪分布式和分布式搭建

1.伪分布式搭建(不会用,了解就好不需要搭建) 这里接上一节。 1.1 伪分布式集群概述 伪分布式集群就是只有⼀个服务器节点的分布式集群。在这种模式中,我们也是只需要⼀台机器。 但与本地模式不同,伪分布式采⽤了分布式…

新手操作指引:快速上手腾讯混元大模型

引言 腾讯混元大模型是一款功能强大的AI工具,适用于文本生成、图像创作和视频生成等多种应用场景。对于新手用户,快速上手并充分利用这一工具可能会有些挑战。本文将提供详细的新手操作指引,帮助您轻松开始使用腾讯混元大模型。 步骤一&…

计算机毕业设计 基于Python内蒙古旅游景点数据分析系统 Django+Vue 前后端分离 附源码 讲解 文档

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

006——队列

目录 队列: 单端队列: 存储结构: 顺序队列 思路1:r指针指向尾元素的下一个位置 思路2:r指针指向真正的尾元素 如何解决假溢出的问题? 链式队列 双端队列 存储方式: 顺式存储 代码案例…

Redis基础数据结构之 Sorted Set 有序集合 源码解读

目录标题 Sorted Set 是什么?Sorted Set 数据结构跳表(skiplist)跳表节点的结构定义跳表的定义跳表节点查询层数设置 Sorted Set 基本操作 Sorted Set 是什么? 有序集合(Sorted Set)是 Redis 中一种重要的数据类型,…

国央企如何完善黑名单排查体系?

国央企完善黑名单排查体系的关键在于建立健全的供应商管理机制、风险评估体系和信息共享平台。以下是一些具体措施: 1.建立黑名单库:国央企可以依据外部黑名单数据(如政府监管部门、行业协会、第三方征信机构公布的黑名单)和内部…