对话音视频牛哥:开发RTSP|RTMP直播播放器难不难?难在哪?

我关注的播放器指标

好多开发者跟我交流音视频相关技术的时候,经常问我的问题是,多久可以开发个商业级别的RTMP或RTSP播放器?你们是怎样做到毫秒级延迟的?为什么一个播放器,会被你们做到那么复杂?带着这些疑问,结合Windows平台RTMP、RTSP播放模块,探讨下我的一点心得,不当之处权当抛砖引玉:

1. 低延迟:大多数RTMP或RTSP的播放都面向直播场景,如果延迟过大,严重影响体验,所以,低延迟是衡量一个好的直播播放器非常重要的指标,好多人对RTMP直播播放的印象,还停留在3-5秒的延迟,实际我们从2015年开发的RTMP播放来看,延迟也就几百毫秒,在一些强交互的场景,设置低延迟模式,甚至可以在200ms左右,RTSP延迟也在毫秒级,一些需要控制的场景,比如智能机器人、无人机等,实际使用下来,都可用满足场景诉求;

2. 音视频同步处理:大多播放器为了追求低延迟,甚至不做音视频同步,拿到audio video直接播放,导致音视频不同步,还有就是时间戳乱跳等各种问题,所以,一个好的直播播放器,需要有时间戳同步和异常时间戳矫正机制,当然,如果是超低延迟模式下,可以0 buffer,不做音视频同步:

3. 支持多实例:多实例RTMP、RTSP播放,是衡量一个直播播放器重要的指标,比如4-8-16路高分辨率播放;

4. 支持buffer time设置:在一些有网络抖动的场景,播放器需要支持buffer time设置;

5. 实时音量调节:比如,多窗口播放RTMP或RTSP流,如果每个audio都播放出来,体验非常不好,所以实时静音功能非常必要;

6. 视频view旋转:部分硬件设备,由于安装限制,导致图像倒置或旋转,所以一个好的RTMP、RTSP播放器应该支持如视频view实时旋转(0° 90° 180° 270°)、水平反转、垂直反转;

7. 支持解码后audio/video数据输出:好多开发者,希望能在播放的同时,获取到YUV或RGB数据,进行人脸匹配等算法分析,他们一个非常重要的诉求,就是高效率的获取到YUV或RGB数据;

8. 实时快照:感兴趣或重要的场景画面,实时截取下来非常必要;

9. 网络抖动处理(如断网重连):稳定的网络处理机制、支持如断网重连等,对直播播放器来说,非常重要;

10. 长期运行稳定性:7*24小时使用场景非常普遍,长时间运行稳定性的重要性不言而喻;

11. 实时下载速度反馈:提供音视频流实时下载回调,可设置回调时间间隔,确保实时下载速度反馈,以此来监听网络状态;

12. 异常状态处理、Event状态回调:如播放的过程中断网,我们提供的播放器可实时回调相关状态,确保上层模块感知处理,开源播放器对此支持不好;

13. 设置视频填充模式(等比例显示):好多情况下,有些场景需要全view铺满播放,有些为了防止视频拉伸,可以设置成等比例缩放显示;

14. D3D检测:一般来说市面上的大多Windows都支持D3D,有些小众化的,只支持GDI模式绘制,所以为了更好的兼容性,这个接口非常必要;

15. 特定机型硬解码:特定机型硬解码,也主要是用于多路播放场景下,通过硬解码,实现更低的CPU占用目的;

16. 只播放关键帧:特别是大屏多实例场景播放的时候,尽管我们已经CPU占用非常低了,如果只是查看大概的监控情景,实现更多路的播放,只播放关键帧是个非常好的功能点,如果需要原始帧播放,可以实时调节即可;

17. TCP-UDP设置:考虑到部分服务器或硬件设备或网络环境对TCP、UDP某一个支持的比较好,我们加了设置接口;

18. TCP-UDP自动切换:这个是更细力度的接口,比如默认设置了TCP模式,TCP模式下收不到数据,超时后,自动切换到UDP模式尝试,一般开源播放器不具备此功能;

19. RTSP超时时间设定:比如10-12秒收不到数据,自动重连,一般开源播放器支持不好。

技术实现

本文以大牛直播SDK的Windows平台C++的demo为例,探讨下RTMP、RTSP播放、录像、实时音量调节、快照等接口设计和处理:

模块初始化:

	GetSmartPlayerSDKAPI(&player_api_);if ( NT_ERC_OK != player_api_.Init(0, NULL) ){return FALSE;}is_support_h264_hardware_decoder_ = NT_ERC_OK == player_api_.IsSupportH264HardwareDecoder();is_support_h265_hardware_decoder_ = NT_ERC_OK == player_api_.IsSupportH265HardwareDecoder();if ( NT_ERC_OK != player_api_.Open(&player_handle_, NULL, 0, NULL) ){return FALSE;}player_api_.SetEventCallBack(player_handle_, GetSafeHwnd(), &NT_SP_SDKEventHandle);

其他参数初始化:

bool CSmartPlayerDlg::InitCommonSDKParam()
{ASSERT(!is_playing_);ASSERT(!is_recording_);if ( NULL == player_handle_ )return false;CString wbuffer_str;edit_buffer.GetWindowTextW(wbuffer_str);std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;auto buffer_str = conv.to_bytes(wbuffer_str);player_api_.SetBuffer(player_handle_, atoi(buffer_str.c_str()));// 设置rtsp 超时时间player_api_.SetRtspTimeout(player_handle_, rtsp_conf_info_.timeout_);// 设置rtsp tcp模式,rtmp不使用, 可以不设置player_api_.SetRTSPTcpMode(player_handle_, rtsp_conf_info_.is_tcp_ ? 1 : 0);player_api_.SetRtspAutoSwitchTcpUdp(player_handle_, rtsp_conf_info_.is_tcp_udp_auto_switch_ ? 1 : 0);if ( btn_check_fast_startup_.GetCheck() == BST_CHECKED ){player_api_.SetFastStartup(player_handle_, 1);}else{player_api_.SetFastStartup(player_handle_, 0);}player_api_.SetReportDownloadSpeed(player_handle_, 1, 1);if (NT_ERC_OK != player_api_.SetURL(player_handle_, GetURL().c_str())){return false;}connection_status_	= 0;buffer_status_		= 0;buffer_percent_		= 0;download_speed_		= -1;return true;
}

播放控制:

void CSmartPlayerDlg::OnBnClickedButtonPlay()
{if ( player_handle_ == NULL )return;CString btn_play_str;btn_play_.GetWindowTextW(btn_play_str);if ( btn_play_str == _T("播放") ){if ( !is_recording_ ){if ( !InitCommonSDKParam() ){AfxMessageBox(_T("设置参数错误!"));return;}}player_api_.SetVideoSizeCallBack(player_handle_, GetSafeHwnd(), SP_SDKVideoSizeHandle);bool is_support_d3d_render = false;NT_INT32 in_support_d3d_render = 0;if ( NT_ERC_OK == player_api_.IsSupportD3DRender(player_handle_,wrapper_render_wnd_.RenderWnd(), &in_support_d3d_render)){if ( 1 == in_support_d3d_render ){is_support_d3d_render = true;}}if ( is_support_d3d_render ){is_gdi_render_ = false;// 支持d3d绘制的话,就用D3D绘制player_api_.SetRenderWindow(player_handle_, wrapper_render_wnd_.RenderWnd());player_api_.SetRenderScaleMode(player_handle_, btn_check_render_scale_mode_.GetCheck() == BST_CHECKED ? 1 : 0);}else{is_gdi_render_ = true;// 不支持D3D就让播放器吐出数据来,用GDI绘制wrapper_render_wnd_.SetRenderScaleMode(btn_check_render_scale_mode_.GetCheck() == BST_CHECKED ? 1 : 0);player_api_.SetVideoFrameCallBack(player_handle_, NT_SP_E_VIDEO_FRAME_FORMAT_RGB32,GetSafeHwnd(), SM_SDKVideoFrameHandle);}if ( BST_CHECKED == btn_check_hardware_decoder_.GetCheck() ){player_api_.SetH264HardwareDecoder(player_handle_, is_support_h264_hardware_decoder_?1:0, 0);player_api_.SetH265HardwareDecoder(player_handle_, is_support_h265_hardware_decoder_?1:0, 0);}else{player_api_.SetH264HardwareDecoder(player_handle_, 0, 0);player_api_.SetH265HardwareDecoder(player_handle_, 0, 0);}player_api_.SetOnlyDecodeVideoKeyFrame(player_handle_, BST_CHECKED == btn_check_only_decode_video_key_frame_.GetCheck() ? 1 : 0);player_api_.SetLowLatencyMode(player_handle_, BST_CHECKED == btn_check_low_latency_.GetCheck() ? 1 : 0);player_api_.SetFlipVertical(player_handle_, BST_CHECKED == btn_check_flip_vertical_.GetCheck() ? 1 :0 );player_api_.SetFlipHorizontal(player_handle_, BST_CHECKED == btn_check_flip_horizontal_.GetCheck() ? 1 : 0);player_api_.SetRotation(player_handle_, rotate_degrees_);player_api_.SetAudioVolume(player_handle_, slider_audio_volume_.GetPos());player_api_.SetUserDataCallBack(player_handle_, GetSafeHwnd(), NT_SP_SDKUserDataHandle);if (NT_ERC_OK != player_api_.StartPlay(player_handle_)){AfxMessageBox(_T("播放器失败!"));return;}btn_play_.SetWindowTextW(_T("停止"));is_playing_ = true;}else{StopPlayback();}
}

实时快照:

void CSmartPlayerDlg::OnBnClickedButtonCaptureImage()
{if ( capture_image_path_.empty() ){AfxMessageBox(_T("请先设置保存截图文件的目录! 点击截图左边的按钮设置!"));return;}if ( player_handle_ == NULL ){return;}if ( !is_playing_ ){return;}std::wostringstream ss;ss << capture_image_path_;if ( capture_image_path_.back() != L'\\' ){ss << L"\\";}SYSTEMTIME sysTime;::GetLocalTime(&sysTime);ss << L"SmartPlayer-"<< std::setfill(L'0') << std::setw(4) << sysTime.wYear<< std::setfill(L'0') << std::setw(2) << sysTime.wMonth<< std::setfill(L'0') << std::setw(2) << sysTime.wDay<< L"-"<< std::setfill(L'0') << std::setw(2) << sysTime.wHour<< std::setfill(L'0') << std::setw(2) << sysTime.wMinute<< std::setfill(L'0') << std::setw(2) << sysTime.wSecond;ss << L"-" << std::setfill(L'0') << std::setw(3) << sysTime.wMilliseconds<< L".png";std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;auto val_str = conv.to_bytes(ss.str());auto ret = player_api_.CaptureImage(player_handle_, val_str.c_str(), NULL, &SM_SDKCaptureImageHandle);if (NT_ERC_OK == ret){// 发送截图请求成功}else if (NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS == ret){// 通知用户延时OutputDebugStringA("Too many capture image requests!!!\r\n");}else{// 其他失败}
}

只解关键帧:

void CSmartPlayerDlg::OnBnClickedCheckOnlyDecodeVideoKeyFrame()
{if (player_handle_ != NULL){player_api_.SetOnlyDecodeVideoKeyFrame(player_handle_, BST_CHECKED == btn_check_only_decode_video_key_frame_.GetCheck() ? 1 : 0);}
}

设置等比例显示或全铺满模式:

void CSmartPlayerDlg::OnBnClickedCheckRenderScaleMode()
{if (player_handle_ != NULL){if (!is_gdi_render_){player_api_.SetRenderScaleMode(player_handle_, BST_CHECKED == btn_check_render_scale_mode_.GetCheck() ? 1 : 0);}else{wrapper_render_wnd_.SetRenderScaleMode(btn_check_render_scale_mode_.GetCheck() == BST_CHECKED ? 1 : 0);}}
}

视频view水平反转、垂直反转、旋转:

void CSmartPlayerDlg::OnBnClickedCheckFlipHorizontal()
{if (player_handle_ != NULL){player_api_.SetFlipHorizontal(player_handle_, BST_CHECKED == btn_check_flip_horizontal_.GetCheck() ? 1 : 0);}
}void CSmartPlayerDlg::OnBnClickedCheckFlipVertical()
{if (player_handle_ != NULL){player_api_.SetFlipVertical(player_handle_, BST_CHECKED == btn_check_flip_vertical_.GetCheck() ? 1 : 0);}
}void CSmartPlayerDlg::OnBnClickedButtonRotation()
{rotate_degrees_ += 90;rotate_degrees_ = rotate_degrees_ % 360;if (0 == rotate_degrees_){btn_rotation_.SetWindowText(_T("旋转90度"));}else if (90 == rotate_degrees_){btn_rotation_.SetWindowText(_T("旋转180度"));}else if (180 == rotate_degrees_){btn_rotation_.SetWindowText(_T("旋转270度"));}else if (270 == rotate_degrees_){btn_rotation_.SetWindowText(_T("不旋转"));}if ( player_handle_ != NULL ){player_api_.SetRotation(player_handle_, rotate_degrees_);}
}

实时录像:

void CSmartPlayerDlg::OnBnClickedButtonRecord()
{if ( player_handle_ == NULL )return;CString btn_record_str;btn_record_.GetWindowTextW(btn_record_str);if ( btn_record_str == _T("录像") ){if ( !rec_conf_info_.is_record_video_ && !rec_conf_info_.is_record_audio_ ){AfxMessageBox(_T("音频录制选项和视频录制选项至少需要选择一个!"));return;}if ( !is_playing_ ){if ( !InitCommonSDKParam() ){AfxMessageBox(_T("设置参数错误!"));return;}}player_api_.SetRecorderVideo(player_handle_, rec_conf_info_.is_record_video_ ? 1 : 0);player_api_.SetRecorderAudio(player_handle_, rec_conf_info_.is_record_audio_ ? 1 : 0);auto ret = player_api_.SetRecorderDirectory(player_handle_, rec_conf_info_.dir_.c_str());if ( NT_ERC_OK != ret ){AfxMessageBox(_T("设置录像目录失败,请确保目录存在且是英文目录"));return;}player_api_.SetRecorderFileMaxSize(player_handle_, rec_conf_info_.file_max_size_);NT_SP_RecorderFileNameRuler rec_name_ruler = { 0 };rec_name_ruler.type_ = 0;rec_name_ruler.file_name_prefix_ = rec_conf_info_.file_name_prefix_.c_str();rec_name_ruler.append_date_		 = rec_conf_info_.is_append_date_ ? 1 : 0;rec_name_ruler.append_time_		 = rec_conf_info_.is_append_time_ ? 1 : 0;player_api_.SetRecorderFileNameRuler(player_handle_, &rec_name_ruler);player_api_.SetRecorderCallBack(player_handle_, GetSafeHwnd(), &SP_SDKRecorderHandle);player_api_.SetRecorderAudioTranscodeAAC(player_handle_, rec_conf_info_.is_audio_transcode_aac_ ? 1 : 0);if ( NT_ERC_OK != player_api_.StartRecorder(player_handle_) ){AfxMessageBox(_T("录像失败!"));return;}btn_record_.SetWindowTextW(_T("停止录像"));is_recording_ = true;}else{StopRecorder();}
}

Event回调:

LRESULT CSmartPlayerDlg::OnSDKEvent(WPARAM wParam, LPARAM lParam)
{if (!is_playing_ && !is_recording_){return S_OK;}NT_UINT32 event_id = (NT_UINT32)(wParam);if ( NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS == event_id ){StopPlayback();return S_OK;}else if ( NT_SP_E_EVENT_ID_RECORDER_REACH_EOS == event_id ){StopRecorder();return S_OK;}else if ( NT_SP_E_EVENT_ID_RTSP_STATUS_CODE == event_id ){int status_code = (int)lParam;if ( 401 == status_code ){HandleVerification();}return S_OK;}else if (NT_SP_E_EVENT_ID_NEED_KEY == event_id){HandleKeyEvent(false);return S_OK;}else if (NT_SP_E_EVENT_ID_KEY_ERROR == event_id){HandleKeyEvent(true);return S_OK;}else if ( NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS == event_id ){if (player_handle_ != NULL){player_api_.StopPullStream(player_handle_);}return S_OK;}else if ( NT_SP_E_EVENT_ID_DURATION == event_id ){NT_INT64 duration = (NT_INT64)(lParam);edit_duration_.SetWindowTextW(GetHMSMsFormatStr(duration, false, false).c_str());return S_OK;}if ( NT_SP_E_EVENT_ID_CONNECTING == event_id|| NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id|| NT_SP_E_EVENT_ID_CONNECTED == event_id|| NT_SP_E_EVENT_ID_DISCONNECTED == event_id|| NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == event_id){if ( NT_SP_E_EVENT_ID_CONNECTING == event_id ){OutputDebugStringA("connection status: connecting\r\n");}else if ( NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id ){OutputDebugStringA("connection status: connection failed\r\n");}else if ( NT_SP_E_EVENT_ID_CONNECTED == event_id ){OutputDebugStringA("connection status: connected\r\n");}else if (NT_SP_E_EVENT_ID_DISCONNECTED == event_id){OutputDebugStringA("connection status: disconnected\r\n");}else if (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == event_id){OutputDebugStringA("connection status: no mediadata received\r\n");}connection_status_ = event_id;}if ( NT_SP_E_EVENT_ID_START_BUFFERING == event_id|| NT_SP_E_EVENT_ID_BUFFERING == event_id|| NT_SP_E_EVENT_ID_STOP_BUFFERING == event_id ){buffer_status_ = event_id;if ( NT_SP_E_EVENT_ID_BUFFERING == event_id ){buffer_percent_ = (NT_INT32)lParam;std::wostringstream ss;ss << L"buffering:" << buffer_percent_ << "%";OutputDebugStringW(ss.str().c_str());OutputDebugStringW(L"\r\n");}}if ( NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id ){download_speed_ = (NT_INT32)lParam;}CString show_str = base_title_;if ( connection_status_ != 0 ){show_str += _T("--链接状态: ");if ( NT_SP_E_EVENT_ID_CONNECTING == connection_status_ ){show_str += _T("链接中");}else if ( NT_SP_E_EVENT_ID_CONNECTION_FAILED == connection_status_ ){show_str += _T("链接失败");}else if ( NT_SP_E_EVENT_ID_CONNECTED == connection_status_ ){show_str += _T("链接成功");}else if ( NT_SP_E_EVENT_ID_DISCONNECTED == connection_status_ ){show_str += _T("链接断开");}else if (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == connection_status_){show_str += _T("收不到数据");}}if (download_speed_ != -1){std::wostringstream ss;ss << L"--下载速度:" << (download_speed_ * 8 / 1000) << "kbps"<< L"(" << (download_speed_ / 1024) << "KB/s)";show_str += ss.str().c_str();}if ( buffer_status_ != 0 ){show_str += _T("--缓冲状态: ");if ( NT_SP_E_EVENT_ID_START_BUFFERING == buffer_status_ ){show_str += _T("开始缓冲");}else if (NT_SP_E_EVENT_ID_BUFFERING == buffer_status_){std::wostringstream ss;ss << L"缓冲中" << buffer_percent_ << "%";show_str += ss.str().c_str();}else if (NT_SP_E_EVENT_ID_STOP_BUFFERING == buffer_status_){show_str += _T("结束缓冲");}}SetWindowText(show_str);return S_OK;
}

总结

我们常说,做RTMP或RTSP播放器容易,但是做个高稳定、低延迟、功能强大的直播播放器,特别是细节处理,还是有一定的难度,开源播放器在点播播放问题不大,直播场景下,欠缺还很多,做播放器容易,做好播放器,难。上述只是一点经验分享,感兴趣的开发者,可以参考。

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

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

相关文章

使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器

使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器 在本文中&#xff0c;我们将创建一个实时网页编辑器。这是一个 Web 应用程序&#xff0c;允许我们在网页上编写 HTML、CSS 和 JavaScript 代码并实时查看结果。这是学习 Web 开发和测试代码片段的绝佳工具。我们将使用ifram…

nodejs+vue古诗词在线测试管理系统

一开始&#xff0c;本文就对系统内谈到的基本知识&#xff0c;从整体上进行了描述&#xff0c;并在此基础上进行了系统分析。为了能够使本系统较好、较为完善的被设计实现出来&#xff0c;就必须先进行分析调查。基于之前相关的基础&#xff0c;在功能上&#xff0c;对新系统进…

通过请求头传数据向后端发请求

axios &#xff08;get post请求、头部参数添加&#xff09;傻瓜式入门axios_axiospost请求参数_web_blog的博客-CSDN博客

【Ubuntu】简洁高效企业级日志平台后起之秀Graylog

简介 Graylog 是一个用于集中式日志管理的开源平台。在现代数据驱动的环境中&#xff0c;我们需要处理来自各种设备、应用程序和操作系统的大量数据。Graylog提供了一种方法来聚合、组织和理解所有这些数据。它的核心功能包括流式标记、实时搜索、仪表板可视化、告警触发、内容…

【图论】最短路的传送问题

一.分层图问题&#xff08;单源传送&#xff09; &#xff08;1&#xff09;题目 P4568 [JLOI2011] 飞行路线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) &#xff08;2&#xff09;思路 可知背景就是求最短路问题&#xff0c;但难点是可以使一条路距离缩短至0&#xf…

抓包工具Fiddler下载与安装

一、Fiddler介绍 1.Fiddler简介 Fiddler 是一款免费、灵活、操作简单、功能强大的 HTTP 代理工具&#xff0c;是目前最常用的 HTTP 抓包工具之一。可以抓取所有的 HTTP/HTTPS 包、过滤会话、分析请求详细内容、伪造客户端请求、篡改服务器响应、重定向、网络限速、断点调试等…

【IMX6ULL驱动开发学习】06.DHT11温湿度传感器驱动程序编写与测试

一、DHT11简介 DHT11是一款可测量温度和湿度的传感器。比如市面上一些空气加湿器&#xff0c;会测量空气中湿度&#xff0c;再根据测量结果决定是否继续加湿。 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器&#xff0c;具有超小体积、极低功耗的特点…

JQuery快速入门教程

1、JQuery快速入门 1.1、JQuery介绍 jQuery 是一个 JavaScript 库。所谓的库&#xff0c;就是一个 JS 文件&#xff0c;里面封装了很多预定义的函数&#xff0c;比如获取元素&#xff0c;执行隐藏、移动等&#xff0c;目的就 是在使用时直接调用&#xff0c;不需要再重复定义…

【Unity开发必备】100多个 Unity 学习网址 资源 收藏整理大全【持续更新】

Unity 相关网站整理大全 众所周知&#xff0c;工欲善其事必先利其器&#xff0c;有一个好的工具可以让我们事半功倍&#xff0c;有一个好用的网站更是如此&#xff01; 但是好用的网站真的太多了&#xff0c;收藏夹都满满的(但是几乎没打开用过&#x1f601;)。 所以本文是对…

开源ChatGPT系统源码 采用NUXT3+Laravel9后端开发 前后端分离版本

开源ChatGPT系统源码 采用NUXT3Laravel9后端开发 前后端分离版本 ChatGPT是一种基于AI的聊天机器人技术&#xff0c;它可以帮助用户与聊天机器人进行自然语言交流&#xff0c;以解决用户的问题或满足用户的需求。ChatGPT的核心技术是使用自然语言处理&#xff08;NLP&#xff…

Linux/Ubuntu 的日常升级和安全更新,如何操作?

我安装的是Ubuntu 20.04.6 LTS的Windows上Linux子系统版本&#xff0c;启动完成后显示&#xff1a; Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.90.4-microsoft-standard-WSL2 x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.c…

P6685 可持久化动态仙人掌的直径问题

思路1&#xff1a;二分快速幂 #include<bits/stdc.h> using namespace std; #define int long long int n,m; bool check(int a,int b){int ans1;while(b){if(a>n)return false;if(b&1)ans*a;if(ans>n)return false;aa*a;b>>1;}return ans<n; } voi…

AI重新定义音视频生产力“新范式”

// 编者按&#xff1a;AIGC无疑是当下的热门话题和场景。面对AI带来的技术变革和算力挑战&#xff0c;该如何应对&#xff1f;LiveVideoStackCon 2023上海站邀请到了网心科技副总裁武磊为我们分享网心在面对AI应用场景和业务需求下的实践经验。 文/武磊 编辑/LiveVideoStack …

元宇宙电商—NFG系统:区块链技术助力商品确权。

在国内&#xff0c;以“数字藏品”之名崛起以来&#xff0c;其与NFT的对比就从未停歇。从上链模式到数据主权&#xff0c;从炒作需求到实际应用&#xff0c;从售卖形式到价值属性&#xff0c;在各种抽丝剥茧般的比较中&#xff0c;围绕两者孰优孰劣的讨论不绝于耳。 NFT的每一…

从NLP到聊天机器人

一、说明 今天&#xff0c;当打电话给银行或其他公司时&#xff0c;听到电话另一端的机器人向你打招呼是很常见的&#xff1a;“你好&#xff0c;我是你的数字助理。请问你的问题。是的&#xff0c;机器人现在不仅可以说人类语言&#xff0c;还可以用人类语言与用户互动。这是由…

Shell编程之正则表达式

文本处理器&#xff1a;三剑客&#xff1a;grep查找sed awk shell正则表达式由一类特殊字符以及文本字符所编写的一种模式&#xff0c;处理文本当中的内容&#xff0c;其中的一些字符不表示字符的字面含义表示一种控制或者通配的功能 通配符&#xff1a;匹配文件名和目录名&a…

List和ObservableCollection和ListBinding在MVVM模式下的对比

List和ObservableCollection和ListBinding在MVVM模式下的对比 List 当对List进行增删操作后&#xff0c;并不会对View进行通知。 //Employee public class Employee : INotifyPropertyChanged {public event PropertyChangedEventHandler? PropertyChanged;public string N…

VSCode无法从Extensions下载工具时,把工具下载到本地并添加到VSCode编辑器

从VSCode 的 Extensions 下载 下载报错&#xff1a;Error while installing ...... extension. Please check the log for more details. 由于内网限制&#xff08;或者其他网络限制&#xff09;无法正常下载扩展工具到VSCode编辑器&#xff0c;可以把工具下载到本地再添加到V…

Python的六种参数?

很多人说&#xff0c;Python的参数类型有四种、五种&#xff0c;我个人认为归纳起来是六种参数&#xff0c;分别为&#xff1a;位置参数&#xff08;Positional Arguments&#xff09;、默认参数&#xff08;Default Arguments&#xff09;、关键字参数&#xff08;Keyword Arg…

Autosar存储入门系列02_NVM之CRC校验及显隐式同步机制

本文框架 0.前言1. NVM中CRC校验2. NVM的显隐式同步机制2.1 隐式同步2.2 显式同步 0.前言 本系列是Autosar存储入门系列&#xff0c;希望能从学习者的角度把存储相关的知识点梳理一遍&#xff0c;这个过程中如果大家觉得有讲得不对或者不够清晰的地方&#xff0c;还请一定指出…