Linux平台x86_64|aarch64架构如何实现轻量级RTSP服务

news2024/11/25 23:18:25

技术背景

我们在做Linux平台x86_64架构或aarch64架构的推送模块的时候,有公司提出这样的技术需求,希望在Linux平台,实现轻量级RTSP服务,实现对摄像头或屏幕对外RTSP拉流,同步到大屏上去。

技术实现

废话不多说,直接上代码,先调用start_rtsp_server()指定端口号,启动RTSP服务。

	LogInit();

	NT_SmartPublisherSDKAPI push_api;
	if (!PushSDKInit(push_api))
	{
		XDestroyWindow(display, sub_wid);
		XDestroyWindow(display, main_wid);
		XCloseDisplay(display);

		return 0;
	}

	// auto rtsp_server_handle = start_rtsp_server(&push_api, 8554, "test", "12345");
	auto rtsp_server_handle = start_rtsp_server(&push_api, 8554, "", "");
	if (nullptr == rtsp_server_handle) {
		fprintf(stderr, "start_rtsp_server failed.\n");

		XDestroyWindow(display, sub_wid);
		XDestroyWindow(display, main_wid);
		XCloseDisplay(display);

		push_api.UnInit();

		return 0;
	}

	auto push_handle = open_config_instance(&push_api, 20);
	if (nullptr == push_handle) {
		fprintf(stderr, "open_config_instance failed.\n");

		XDestroyWindow(display, sub_wid);
		XDestroyWindow(display, main_wid);
		XCloseDisplay(display);

		stop_rtsp_server(&push_api, rtsp_server_handle);

		push_api.UnInit();

		return 0;
	}

PushSDKInit()实现如下:

	/*
     * publisherdemo.cpp
     * Author: daniusdk.com
     */
    bool PushSDKInit(NT_SmartPublisherSDKAPI& push_api)
	{
		memset(&push_api, 0, sizeof(push_api));
		NT_GetSmartPublisherSDKAPI(&push_api);

		auto ret = push_api.Init(0, nullptr);
		if (NT_ERC_OK != ret)
		{
			fprintf(stderr, "push_api.Init failed!\n");
			return false;
		}
		else
		{
			fprintf(stdout, "push_api.Init ok!\n");
		}

		return true;
	}

启动RTSP服务对应的代码如下:

	NT_HANDLE start_rtsp_server(NT_SmartPublisherSDKAPI* push_api, int port, std::string user_name, std::string password) {

		NT_HANDLE rtsp_server_handle = nullptr;
		if (NT_ERC_OK != push_api->OpenRtspServer(&rtsp_server_handle, 0)) {
			fprintf(stderr, "OpenRtspServer failed\n");
			return nullptr;
		}

		if (nullptr == rtsp_server_handle) {
			fprintf(stderr, "rtsp_server_handle is null\n");
			return nullptr;
		}

		if (NT_ERC_OK != push_api->SetRtspServerPort(rtsp_server_handle, port)) {
			push_api->CloseRtspServer(rtsp_server_handle);
			return nullptr;
		}

		if (!user_name.empty() && !password.empty())
			push_api->SetRtspServerUserNamePassword(rtsp_server_handle, user_name.c_str(), password.c_str());

		if (NT_ERC_OK == push_api->StartRtspServer(rtsp_server_handle, 0))
			return rtsp_server_handle;

		fprintf(stderr, "StartRtspServer failed\n");
		push_api->CloseRtspServer(rtsp_server_handle);
		
		return nullptr;
	}

open_config_instance()实现如下,可以获取摄像头或屏幕数据,并做基础的编码等参数配置:

	NT_HANDLE open_config_instance(NT_SmartPublisherSDKAPI* push_api, int dst_fps) {
		NT_INT32 pulse_device_number = 0;
		if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(2, &pulse_device_number))
		{
			fprintf(stdout, "[daniusdk.com]Pulse device num:%d\n", pulse_device_number);
			char device_name[512];

			for (auto i = 0; i < pulse_device_number; ++i)
			{
				if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(2, i, device_name, 512))
				{
					fprintf(stdout, "[daniusdk.com]index:%d name:%s\n", i, device_name);
				}
			}
		}

		NT_INT32 alsa_device_number = 0;
		if (pulse_device_number < 1)
		{
			if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(1, &alsa_device_number))
			{
				fprintf(stdout, "Alsa device num:%d\n", alsa_device_number);
				char device_name[512];
				for (auto i = 0; i < alsa_device_number; ++i)
				{
					if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(1, i, device_name, 512))
					{
						fprintf(stdout, "[daniusdk.com]index:%d name:%s\n", i, device_name);
					}
				}
			}
		}

		NT_INT32 capture_speaker_flag = 0;
		if (NT_ERC_OK == push_api->IsCanCaptureSpeaker(2, &capture_speaker_flag))
		{
			if (capture_speaker_flag)
				fprintf(stdout, "[daniusdk.com]Support speaker capture\n");
			else
				fprintf(stdout, "[daniusdk.com]UnSupport speaker capture\n");
		}

		NT_INT32 is_support_window_capture = 0;
		if (NT_ERC_OK == push_api->IsCaptureXWindowSupported(NULL, &is_support_window_capture))
		{
			if (is_support_window_capture)
				fprintf(stdout, "[daniusdk.com]Support window capture\n");
			else
				fprintf(stdout, "[daniusdk.com]UnSupport window capture\n");
		}

		if (is_support_window_capture)
		{
			NT_INT32 win_count = 0;
			if (NT_ERC_OK == push_api->UpdateCaptureXWindowList(NULL, &win_count) && win_count > 0)
			{

				fprintf(stdout, "X Capture Winows list++\n");

				for (auto i = 0; i < win_count; ++i)
				{
					NT_UINT64 wid;
					char title[512];

					if (NT_ERC_OK == push_api->GetCaptureXWindowInfo(i, &wid, title, sizeof(title) / sizeof(char)))
					{
						x_win_list.push_back(wid);
						fprintf(stdout, "wid:%llu, title:%s\n", wid, title);
					}
				}

				fprintf(stdout, "[daniusdk.com]X Capture Winows list--\n");
			}
		}

		std::vector<CameraInfo> cameras;
		GetCameraInfo(push_api, cameras);

		if (!cameras.empty())
		{
			fprintf(stdout, "cameras count:%d\n", (int)cameras.size());

			for (const auto& c : cameras)
			{
				fprintf(stdout, "camera name:%s, id:%s, cap_num:%d\n", c.name_.c_str(), c.id_.c_str(), (int)c.capabilities_.size());

				for (const auto& i : c.capabilities_)
				{
					fprintf(stdout, "[daniusdk.com]cap w:%d, h:%d, fps:%d\n", i.width_, i.height_, i.max_frame_rate_);
				}
			}
		}

		NT_UINT32 auido_option = NT_PB_E_AUDIO_OPTION_NO_AUDIO;

		if (pulse_device_number > 0 || alsa_device_number > 0)
		{
			auido_option = NT_PB_E_AUDIO_OPTION_CAPTURE_MIC;
		}
		else if (capture_speaker_flag)
		{
			auido_option = NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER;
		}

		//auido_option = NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER;

		NT_UINT32 video_option = NT_PB_E_VIDEO_OPTION_SCREEN;

		if (!cameras.empty())
		{
			video_option = NT_PB_E_VIDEO_OPTION_CAMERA;
		}
		else if (is_support_window_capture)
		{
			video_option = NT_PB_E_VIDEO_OPTION_WINDOW;
		}

		// video_option = NT_PB_E_VIDEO_OPTION_LAYER;

		//video_option = NT_PB_E_VIDEO_OPTION_NO_VIDEO;

		NT_HANDLE push_handle = nullptr;

		//if (NT_ERC_OK != push_api->Open(&push_handle, NT_PB_E_VIDEO_OPTION_LAYER, NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER, 0, NULL))
		if (NT_ERC_OK != push_api->Open(&push_handle, video_option, auido_option, 0, NULL))
		{
			return nullptr;
		}

		push_api->SetEventCallBack(push_handle, nullptr, OnSDKEventHandle);

		//push_api->SetXDisplayName(push_handle, ":0");
		//push_api->SetXDisplayName(push_handle, NULL);

		// 视频层配置方式
		if (NT_PB_E_VIDEO_OPTION_LAYER == video_option)
		{
			std::vector<std::shared_ptr<nt_pb_sdk::layer_conf_wrapper_base> > layer_confs;

			auto index = 0;

			 第0层填充RGBA矩形, 目的是保证帧率, 颜色就填充全黑
			auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);

			rgba_layer_c0->conf_.red_ = 200;
			rgba_layer_c0->conf_.green_ = 200;
			rgba_layer_c0->conf_.blue_ = 200;
			rgba_layer_c0->conf_.alpha_ = 255;

			layer_confs.push_back(rgba_layer_c0);

			// 第一层为桌面层
			//auto screen_layer_c1 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);

			//screen_layer_c1->conf_.scale_filter_mode_ = 3;

			//layer_confs.push_back(screen_layer_c1);

			 第一层为窗口
			if (!x_win_list.empty())
			{
				auto window_layer_c1 = std::make_shared<nt_pb_sdk::WindowLayerConfigWrapper>(index++, true, 0, 0, 640, 360);

				window_layer_c1->conf_.xwindow_ = x_win_list.back();

				layer_confs.push_back(window_layer_c1);
			}

			 摄像头层
			if (!cameras.empty())
			{
				auto camera_layer_c1 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapper>(index++, true,
					640, 0, 640, 360);

				strcpy(camera_layer_c1->conf_.device_unique_id_, cameras.front().id_.c_str());

				camera_layer_c1->conf_.is_flip_horizontal_ = 0;
				camera_layer_c1->conf_.is_flip_vertical_ = 0;
				camera_layer_c1->conf_.rotate_degress_ = 0;

				layer_confs.push_back(camera_layer_c1);

				if (cameras.size() > 1)
				{
					auto camera_layer_c2 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapper>(index++, true,
						640, 0, 320, 240);

					strcpy(camera_layer_c2->conf_.device_unique_id_, cameras.back().id_.c_str());

					camera_layer_c2->conf_.is_flip_horizontal_ = 0;
					camera_layer_c2->conf_.is_flip_vertical_ = 0;
					camera_layer_c2->conf_.rotate_degress_ = 0;

					layer_confs.push_back(camera_layer_c2);
				}
			}

			auto image_layer1 = std::make_shared<nt_pb_sdk::ImageLayerConfigWrapper>(index++, true, 650, 120, 324, 300);

			strcpy(image_layer1->conf_.file_name_utf8_, "./testpng/tca.png");

			layer_confs.push_back(image_layer1);

			auto image_layer2 = std::make_shared<nt_pb_sdk::ImageLayerConfigWrapper>(index++, true, 120, 380, 182, 138);

			strcpy(image_layer2->conf_.file_name_utf8_, "./testpng/t4.png");

			layer_confs.push_back(image_layer2);

			std::vector<const NT_PB_LayerBaseConfig* > layer_base_confs;

			for (const auto& i : layer_confs)
			{
				layer_base_confs.push_back(i->getBase());
			}

			if (NT_ERC_OK != push_api->SetLayersConfig(push_handle, 0, layer_base_confs.data(),
				layer_base_confs.size(), 0, nullptr))
			{
				push_api->Close(push_handle);
				push_handle = nullptr;
				return nullptr;
			}
		}

		// push_api->SetScreenClip(push_handle, 0, 0, 1280, 720);

		if (video_option == NT_PB_E_VIDEO_OPTION_CAMERA)
		{
			if (!cameras.empty())
			{
				push_api->SetVideoCaptureDeviceBaseParameter(push_handle, cameras.front().id_.c_str(),
					640, 480);

				//push_api->FlipVerticalCamera(push_handle, 1);
				//push_api->FlipHorizontalCamera(push_handle, 1);
				//push_api->RotateCamera(push_handle, 0);
			}
		}

		if (video_option == NT_PB_E_VIDEO_OPTION_WINDOW)
		{
			if (!x_win_list.empty())
			{
				//push_api->SetCaptureXWindow(push_handle, x_win_list[0]);
				push_api->SetCaptureXWindow(push_handle, x_win_list.back());
			}
		}

		push_api->SetFrameRate(push_handle, dst_fps); // 帧率设置

		push_api->SetVideoEncoder(push_handle, 0, 1, NT_MEDIA_CODEC_ID_H264, 0);

		push_api->SetVideoBitRate(push_handle, 2000);  // 平均码率2000kbps
		push_api->SetVideoQuality(push_handle, 26);
		push_api->SetVideoMaxBitRate(push_handle, 4000); // 最大码率4000kbps

		// openh264 配置特定参数
		push_api->SetVideoEncoderSpecialInt32Option(push_handle, "usage_type", 0); //0是摄像头编码, 1是屏幕编码
		push_api->SetVideoEncoderSpecialInt32Option(push_handle, "rc_mode", 1); // 0是质量模式, 1是码率模式
		push_api->SetVideoEncoderSpecialInt32Option(push_handle, "enable_frame_skip", 0); // 0是关闭跳帧, 1是打开跳帧

		push_api->SetVideoKeyFrameInterval(push_handle, dst_fps * 2); // 关键帧间隔
		push_api->SetVideoEncoderProfile(push_handle, 3); // H264 high
		push_api->SetVideoEncoderSpeed(push_handle, 3); // 编码速度设置到3

		if (pulse_device_number > 0)
		{
			push_api->SetAudioInputLayer(push_handle, 2);
			push_api->SetAuidoInputDeviceId(push_handle, 0);
		}
		else if (alsa_device_number > 0)
		{
			push_api->SetAudioInputLayer(push_handle, 1);
			push_api->SetAuidoInputDeviceId(push_handle, 0);
		}

		push_api->SetEchoCancellation(push_handle, 1, 0);
		push_api->SetNoiseSuppression(push_handle, 1);
		push_api->SetAGC(push_handle, 1);
		push_api->SetVAD(push_handle, 1);

		push_api->SetInputAudioVolume(push_handle, 0, 1.0);
		push_api->SetInputAudioVolume(push_handle, 1, 0.2);

		// 音频配置
		push_api->SetPublisherAudioCodecType(push_handle, 1);
		//push_api->SetMute(push_handle, 1);

		return push_handle;
	}

发布RTSP流实现如下:

	bool start_rtsp_stream(NT_SmartPublisherSDKAPI* push_api, NT_HANDLE rtsp_server_handle, NT_HANDLE handle, const std::string stream_name) {

		push_api->SetRtspStreamName(handle, stream_name.c_str());

		push_api->ClearRtspStreamServer(handle);

		push_api->AddRtspStreamServer(handle, rtsp_server_handle, 0);
		
		if (NT_ERC_OK != push_api->StartRtspStream(handle, 0))
			return false;

		return true;
	}

如果需要本地摄像头或者屏幕预览数据,调研预览接口即可:

	// 开启预览,也可以不开启, 根据需求来
	push_api.SetPreviewXWindow(push_handle, "", sub_wid);
	push_api.StartPreview(push_handle, 0, nullptr);

如需停止:

	fprintf(stdout, "StopRtspStream++\n");

	push_api.StopRtspStream(push_handle);
	
	fprintf(stdout, "StopRtspStream--\n");

	fprintf(stdout, "stop_rtsp_server++\n");

	stop_rtsp_server(&push_api, rtsp_server_handle);

	fprintf(stdout, "stop_rtsp_server--\n");

	push_api.StopPreview(push_handle);

	// push_api.StopPublisher(push_handle);

	push_api.Close(push_handle);

	push_handle = nullptr;

	XDestroyWindow(display, sub_wid);
	XDestroyWindow(display, main_wid);
	XCloseDisplay(display);

	push_api.UnInit();

总结

Linux平台arm64实现轻量级RTSP服务,目前实现的功能如下:

  • 音频编码:AAC;
  • 视频编码:H.264;
  • 协议:RTSP;
  • [音视频]支持纯音频/纯视频/音视频推送;
  • 支持X11屏幕采集;
  • 支持部分V4L2摄像头设备采集;
  • [屏幕/V4L2摄像头]支持帧率、关键帧间隔(GOP)、码率(bit-rate)设置;
  • [V4L2摄像头]支持V4L2摄像头设备选择(设备文件名范围:[/dev/video0, /dev/video63])、分辨率设置、帧率设置;
  • [V4L2摄像头]支持水平反转、垂直反转、0° 90° 180° 270°旋转;
  • [音频]支持基于alsa-lib接口的音频采集;
  • [音频]支持基于libpulse接口采集本机PulseAudio服务音频;
  • [预览]支持实时预览; 支持RTSP端口设置;
  • 支持RTSP鉴权用户名、密码设置;
  • 支持获取当前RTSP服务会话连接数;
  • 支持x64_64架构、aarch64架构(需要glibc-2.21及以上版本的Linux系统, 需要libX11.so.6, 需要GLib–2.0, 需安装 libstdc++.so.6.0.21、GLIBCXX_3.4.21、 CXXABI_1.3.9)。

配合我们的RTSP播放器,可轻松实现150-400ms低延迟体验,感兴趣的开发者,可以单独跟我沟通。

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

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

相关文章

鸿蒙 HarmonyOS Next 路由 不废话 全干货

一、页面的创建 &#xff08;1&#xff09;直接通过创建一个新的Page的方式创建 &#xff08;2&#xff09;先创建一个 ArkTs File文件&#xff0c;然后在resources/base/profile/main_pages.json中加上页面对应的src路径&#xff0c;下面的Index_3.ets文件是通过创建ArkTs Fi…

创建本地仓库

一、新建挂载目录 二、将挂载本地镜像挂载到目录 三、配置yum仓库 一、新建挂载目录 mkdir /BenDiCangKu 二、将挂载本地镜像挂载到目录 1、先连接本地光盘 2、挂载光盘 mount /dev/sr0 /BenDiCangKu 3、查看挂载 由此可见挂载成功 三、配置yum仓库 1、新建yum仓库文件…

LeetCode 196, 73, 105

目录 196. 删除重复的电子邮箱题目链接表要求知识点思路代码 73. 矩阵置零题目链接标签简单版思路代码 优化版思路代码 105. 从前序与中序遍历序列构造二叉树题目链接标签思路代码 196. 删除重复的电子邮箱 题目链接 196. 删除重复的电子邮箱 表 表Person的字段为id和email…

图像处理中的二维傅里叶变换

图像处理中的二维傅里叶变换 问题来源是对彩色图像进行压缩时得出的傅里叶系数的图像如何解释&#xff0c;导入图片&#xff0c;转化为灰度图片&#xff1a; #彩色图片一般是由RGB组成&#xff0c;其实就是3个二维数组叠加而成&#xff0c;当RGB时&#xff0c;彩色图片就会变成…

基于springboot的工作绩效管理系统的设计与实现+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

1117 数字之王

solution 判断现有数字是否全为个位数 全为个位数&#xff0c;找出出现次数最多的数字&#xff0c;并首行输出最多出现次数&#xff0c;第二行输出所有出现该次数的数值不全为个位数 若当前位数值为0&#xff0c;无需处理若当前位数值非0&#xff0c;则每位立方相乘&#xff0…

VBA语言専攻T3学员领取资料通知0706

T3学员领取资料通知0706 各位学员∶本周MF系列VBA技术资料增加641-645讲&#xff0c;T3学员看到通知后请免费领取,领取时间7月5日晚上19:00-7月6日晚上19:00。本次增加内容&#xff1a; MF641:前个窗体组合框选项联动下个组合框 MF642:工作表中数据选项联动下个数据验证 MF…

数据库-MySQL 实战项目——书店图书进销存管理系统数据库设计与实现(附源码)

一、前言 该项目非常适合MySQL入门学习的小伙伴&#xff0c;博主提供了源码、数据和一些查询语句&#xff0c;供大家学习和参考&#xff0c;代码和表设计有什么不恰当还请各位大佬多多指点。 所需环境 MySQL可视化工具&#xff1a;navicat&#xff1b; 数据库&#xff1a;MySq…

FlowUs设计师展示自己作品集的优质平台

作为一位资深设计师&#xff0c;我认为在多个渠道展示我们的作品是至关重要的。这不仅仅是为了展示我们的设计能力&#xff0c;更是为了建立我们作为创意专业人士的声誉和品牌。以下是一些我们应当考虑的理由&#xff1a; 专业展示&#xff1a;在多个平台上展示作品集&#xff…

一文带你了解“商贸物流大脑”

商贸物流大脑源于实体物流&#xff0c;物理世界的实体物流是构建商贸物流大脑的基础。商贸物流大脑应该是物理世界的实际物流系统和相应的虚拟物流系统两个层面血肉相连、相互作用、有机统一的整体。商贸物流的研究内容包括数字产业化和物流数字化两大部分。信息产业化是进入物…

OpenCV 灰度直方图及熵的计算

目录 一、概述 1.1灰度直方图 1.1.1灰度直方图的原理 1.1.2灰度直方图的应用 1.1.3直方图的评判标准 1.2熵 二、代码实现 三、实现效果 3.1直方图显示 3.2 熵的计算 一、概述 OpenCV中的灰度直方图是一个关键的工具&#xff0c;用于分析和理解图像的灰度分布情况。直…

electron教程(二)控制应用程序的事件生命周期

1.will-finish-launching 当应用程序完成基础的启动的时候被触发&#xff0c;在 Windows 和 Linux 中, will-finish-launching 事件与 ready 事件是相同的; 在 macOS 中&#xff0c;这个事件相当于 NSApplication 中的 applicationWillFinishLaunching 提示。 app.on(will-fi…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【生成密钥(C/C++)】

生成密钥(C/C) 以生成ECC密钥为例&#xff0c;生成随机密钥。具体的场景介绍及支持的算法规格。 注意&#xff1a; 密钥别名中禁止包含个人数据等敏感信息。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复…

教师商调函流程详解

作为一名教师&#xff0c;您是否曾面临过工作调动的困惑&#xff1f;当您决定迈向新的教育环境&#xff0c;是否清楚整个商调函流程的每一个细节&#xff1f;今天&#xff0c;就让我们一起来探讨这一过程&#xff0c;确保您能够顺利地完成工作调动。 首先需要确定新调入的学校已…

如何摆脱反爬虫机制?

在网站设计时&#xff0c;为了保证服务器的稳定运行&#xff0c;防止非法数据访问&#xff0c;通常会引入反爬虫机制。一般来说&#xff0c;网站的反爬虫机制包括以下几种&#xff1a; 1. CAPTCHA&#xff1a;网站可能会向用户显示CAPTCHA&#xff0c;要求他们在访问网站或执行…

在Ubuntu上配置PPPoE服务:从安装到自动化启动的全指南

在Ubuntu上配置PPPoE服务&#xff1a;从安装到自动化启动的全指南 PPPoE&#xff08;点对点协议以太网&#xff09;是一种广泛用于DSL和光纤宽带连接的协议。在本篇技术博客中&#xff0c;我们将详细介绍如何在Ubuntu系统上配置PPPoE服务&#xff0c;包括安装、配置、启动以及…

《简历宝典》02 - 如果你是HR,你会优先打开哪份简历?

现在的求职环境不必多说&#xff0c;其实我们大家都还是很清楚的。所以&#xff0c;在这个环境下&#xff0c;写一份优秀的简历&#xff0c;目的与作用也不必多说。那么&#xff0c;这一小节呢&#xff0c;我们先从简历这份文档的文档名开始说起。 目录 1 你觉得HR们刷简历的时…

沙龙回顾|MongoDB如何充当企业开发加速器?

数据不仅是企业发展转型的驱动力&#xff0c;也是开发者最棘手的问题。前日&#xff0c;MongoDB携手阿里云、NineData在杭州成功举办了“数据驱动&#xff0c;敏捷前行——MongoDB企业开发加速器”技术沙龙。此次活动吸引了来自各行各业的专业人员&#xff0c;共同探讨MongoDB的…

娱乐圈惊爆已婚男星刘端端深夜幽会

【娱乐圈惊爆&#xff01;已婚男星刘端端深夜幽会&#xff0c;竟是《庆余年》二皇子“戏外风云”】在这个信息爆炸的时代&#xff0c;娱乐圈的每一次风吹草动都能瞬间点燃公众的热情。今日&#xff0c;知名娱乐博主刘大锤的一则预告如同投入湖中的巨石&#xff0c;激起了层层涟…

香橙派AIpro测评:yolo8+usb鱼眼摄像头的Camera图像获取及识别

一、前言 近期收到了一块受到业界人士关注的开发板"香橙派AIpro",因为这块板子具有极高的性价比&#xff0c;同时还可以兼容ubuntu、安卓等多种操作系统&#xff0c;今天博主便要在一块832g的香橙派AI香橙派AIpro进行YoloV8s算法的部署并使用一个外接的鱼眼USB摄像头…