一套rk3588 rtsp服務器推流的 github 方案及記錄 -01

我不生產代碼,我只是代碼的搬運工,相信我,看完這個文章你的圖片一定能變成流媒體推出去。
訴求:使用opencv拉流,轉成bgr數據,需要把處理后的數據(BGR)編碼成264,然后推流推出去,相當于直播(實時編碼)

播放器

超低延遲的RTSP播放器
https://github.com/tsingsee/EasyPlayer-RTSP-Win

青犀的一個播放器,直接下他的EasyPlayer-RTSP-Win用來測試就行。劃重點,超低延時,我整體方案的延時大概是600-700ms,使用海康相機,rtsp拉流,做了yolo處理,再推出去,有編碼,有解碼,vlc的延時設置低了就回卡幀,Gop已經改成5了還是卡幀,沒有測試Gop改成1的情況,但是vlc的延時和流暢,整體看是不太兼容的。ffmpeg使用nobuffer也會卡幀。直觀感受卡的就是Gop的P幀。

服務器

live555 方案

如果你不著急的話。。。 可以試試這個方案,這方面的參考文獻給列下面了,因為確實正經研究了幾天還看了不少代碼,認真想了應該怎么處理,但是確實不太想寫,而且對我的需求來講live555冗余了很多功能,再加上網上確實沒有寫好的,我又很著急要結果,確定方案能用,所以也沒有用這個方案。

官方demo

live555自己的測試文件是有推流demo的,主要是根據實時需求推264文件,以及無腦做一個推264文件的服務器,當時看代碼的時候一頭霧水加上著急,也沒太認真看,主要在live555\testProgs下面,testOnDemandRTSPServer,testH264VideoStreamer ,第二個是無腦推,第一個是你來一個請求,我從頭開始給你播放一次視頻文件。
這個東西的底層是向一個fTo指針里面拷貝264碼流。
如果你Cpp、coding能力強的話,應該是能看懂直接改的,也就不用往后看了。

參考demo 零聲 usb相機推流

網上基本上和我需求最接近的live555方案下的代碼是國內的做音視頻開發教學的一個零聲出的視頻還有他們傳的這個代碼。

主要功能是 v4l2相機讀取mjpeg,然后ffmpeg的avcodec相關庫編碼,然后送live555,然后推實時流,像是改的testOnDemandRTSPServer,結構很清晰,除了不能用我也找不到原因外都挺好的。另外他的課是5K的,有點貴。

視頻的話去B站帶關鍵詞,基本都能搜到,這個代碼我加了那個聯系QQ要到的。但是在我本地沒有推成功,我也不確定是哪里的問題,編譯過了,放在這里
https://gitee.com/qingfuliao/v4l2_ipc_live555?_from=gitee_search

在我這是下面這個demo 實現了一個相似的功能,編譯實測是可以讀取usb相機然后推流成功的。但是代碼結構沒有上面那個清晰。

參考demo

https://github.com/mpromonet/v4l2rtspserver

這個功能是基于linux的v4l2,使用264的方式讀取相機視頻流(如果你的usb相機不支持264輸出,會驅動失敗),然后直接拆幀把流發出去
需要自己下一個libv4l2cpp的代碼放進來就能編譯了

這個現在看稍微改一改就能用了,不過當時對整體沒有概念,改了一陣子不知道怎么下手把我自己的碼流變成demo的輸入,碼流送進去了但是沒有推成功,定位了一會兒很難定位問題,也就擱置了。有興趣的可以基于這個改一改。

https://blog.csdn.net/qq_43418269/article/details/122488866

這個方案我是成功了的,不過延時不太能滿足我的需求,這個復現很快。他是用一個管道文件做的,我把編碼之后264文件直接寫到live555的testOnDemandRTSPServer.cpp這邊的讀取文件里面,然后邏輯是live555這邊接收到請求,創建管道,相機程序初始化后阻塞住,管道被創建后往管道里寫,然后另一邊就開始播。。

這個就相當于是運行在一個demo里的兩個程序。流倒是推出來了,只不過,我這樣實現,延時很大,他說的百毫秒量級我做不到,我是2s左右。感覺也可能是我操作不當。

https://blog.csdn.net/lifexx/article/details/52823777

live555讀文件改為內存讀取實現,確實C++不太行,這個文章對我理解Live555,還有改成內存中的數據方向給了很大啟發,但是沒有按照他的做,而且他的參考代碼無法運行。對我理解另一個推相機的demo有幫助

其他開源服務器框架

這個也是一個很容易就編譯成功的服務器,可以使用這個做服務器,然后調用ffmpeg推流,在RK3588上也推成功了,基本沒改make相關的配置,需要按照他給的快速開始流程使用git下附加庫,功能很強大,但是對我的需求來講,這個功能我進行二次開發比較慢。不排除我太菜。
https://github.com/ZLMediaKit/ZLMediaKit/tree/master

這里寫這個的主要原因是他的一些文章對我的啟發和快速上手有很大的參考意義,比如下面這個。

https://github.com/ZLMediaKit/ZLMediaKit/wiki/%E6%80%8E%E4%B9%88%E6%B5%8B%E8%AF%95ZLMediaKit%E7%9A%84%E5%BB%B6%E6%97%B6%EF%BC%9F

RK3399 參考

網上有瑞芯微其他方案的rtsp推流,我只能幫忙排除錯誤選項
https://t.rock-chips.com/forum.php?mod=viewthread&tid=749&extra=page%3D1
如果你是在看這個帖子,可以不用看了,這個貓頭的代碼雖然推出去了,但是他的Rtsp是調庫,這個庫是閉源的,3588沒有,這個網站注冊要兩三天才能通過,不必等這個,用不了。

rtsp推流

在github上搜索rtsp,排名最高的那個結果,就是那個小烏龜,

https://github.com/PHZ76/RtspServer
這個功能比較單一,但是足夠滿足我的需求了,他還使用他自己的庫做了一個windows下的應用,windows上編譯成功了,但是不太好用,不過對我理解他的Demo運行有一定幫助。因為用的是一套庫。另外他主頁還有一個rtmp ,我沒進去看也沒試能不能用。

https://github.com/PHZ76/DesktopSharing

在這里插入圖片描述

下載安裝

下載下來直接在3588上面make就可以,編譯的是RtspServer-master/example里面的main 文件,這里的rtsp_h264_file.cpp是可以直接推運行推264文件的,一般不需要修改就能直接用,如果不能用,有可能是554端口被占用,改一個大點的就好了

    std::string suffix = "live";std::string ip = "127.0.0.1";std::string port = "5543";// 改這里 不要改那個"0.0.0.0" 那個是對的不用改std::string rtsp_url = "rtsp://" + ip + ":" + port + "/" + suffix;

然后rtsp_pusher.cpp還有rtsp_server.cpp都把發送文件的部分注釋掉了,需要結合h264那個文件來對比把264碼流寫進去。

運行測試

編譯出來之后運行 ./rtsp_h264_file ./test.264 就能推出來了
工程已經被我魔改過了,重新生成一個sample 來演示下結果
這是RK3588 服務器
在這里插入圖片描述

這是VLC的界面IP
在這里插入圖片描述

下面的是顯示的結果
在這里插入圖片描述

改cmake 支持opencv 、rknn,mpp

因為我使用的RKNN之間是在Qt里面編譯的,工程使用的都是Cmake的cmakelists,他的makefile也不難改,主要的問題是我自己寫的解碼器,在使用makefile指定mpp庫之后編譯出來的mpp庫運行不正常,具體報錯找不到了,然后我qt上用是沒問題的,定位到是makefile沒寫好,改成cmakelist就可以正常編譯了
-g是為了支持gdb調試,配合我的vscode 調試配置文件,可以單步調試,全程在板上編譯,沒配置交叉編譯環境

cmake_minimum_required(VERSION 3.5)
project(rtspserver)
set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSOCKLEN_T=socklen_t -g ")find_package(OpenCV 4.5.5 REQUIRED)
find_package(OpenSSL REQUIRED)# MPP
set(MPP_PATH /home/orangepi/code/mpp-develop/inc)
set(MPP_LIBS /home/orangepi/code/mpp-develop/mpp/librockchip_mpp.so)
include_directories(${MPP_PATH})# OSAL
set(OSAL_PATH /home/orangepi/code/mpp-develop/osal/inc/ /home/orangepi/code/mpp-develop/utils)
set(OSAL_LIBS /home/orangepi/code/mpp-develop/osal/libosal.a /home/orangepi/code/mpp-develop/utils/libutils.a)
include_directories(${OSAL_PATH})# RKNN lib
set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/lib)
set(RKNN_RT_LIB ${RKNN_API_PATH}/aarch64/librknnrt.so)
include_directories(${RKNN_API_PATH}/include)
include_directories(${CMAKE_SOURCE_DIR}/3rdparty)# RGA
set(RGA_PATH ${CMAKE_SOURCE_DIR}/3rdparty/rga/RK3588)
set(RGA_LIB ${RGA_PATH}/lib/Linux/aarch64/librga.so)
include_directories(${RGA_PATH}/include)aux_source_directory(src/xop SOURCE1)
aux_source_directory(src/net SOURCE2)include_directories(    src    src/xopsrc/netsrc/3rdpart)# add_executable(rtsp
# example/rtsp_server.cpp
# ${SOURCE1}
# ${SOURCE2}
# src/3rdpart/md5/md5.hpp
# )
# target_link_libraries(rtsp ${MPP_LIBS} ${OSAL_LIBS}  ${OpenCV_LIBS} ${RKNN_RT_LIB} ${RGA_LIB} OpenSSL::SSL OpenSSL::Crypto )
# add_executable(rh264
# example/rtsp_h264_file.cpp
# ${SOURCE1}
# ${SOURCE2}
# src/3rdpart/md5/md5.hpp
# )
# target_link_libraries(rh264 ${MPP_LIBS} ${OSAL_LIBS}  ${OpenCV_LIBS} OpenSSL::SSL OpenSSL::Crypto )
add_executable(sample
example/sample.cpp
${SOURCE1}
${SOURCE2}
src/3rdpart/md5/md5.hpp
)
target_link_libraries(sample ${MPP_LIBS} ${OSAL_LIBS}  ${OpenCV_LIBS}  ${RKNN_RT_LIB} ${RGA_LIB} OpenSSL::SSL OpenSSL::Crypto )
代碼修改

代碼是基于他的rtsp_server來修改的,主要修改的內容是sendFrameThread,大概思路是這樣的,還差一個問題,怎么把你的原始mat圖像轉成264碼流

bool IsKeyFrame(const char* data, uint32_t size)
{if (size > 4) {//0x67:sps ,0x65:IDR, 0x6: SEIif (data[4] == 0x67 || data[4] == 0x65 || data[4] == 0x6 || data[4] == 0x27) {return true;}}return false;
}void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, int& clients)
{       encoder e;//  encoder相關 內存拷貝int size = 0;char* buffer ;// 編碼標志位int i = 0;// 生成圖像int width = 1920;int height = 1080;cv::Mat colorBar= cv::Mat::zeros(height, width, CV_8UC3);// 設置彩條的寬度int barWidth = width / 8; // 8個彩條,你可以根據需要調整// 生成彩條for (int i = 0; i < 8; ++i) {// 計算彩條的起始和結束位置int startX = i * barWidth;int endX = (i + 1) * barWidth;// 設置彩條顏色(BGR格式)cv::Vec3b color;if (i % 2 == 0) {color = cv::Vec3b(255, 0, 155); // 藍色} else {color = cv::Vec3b(0, 255, 0); // 綠色}// 在colorBar上畫出彩條colorBar(cv::Rect(startX, 0, barWidth, height)) = color;}while(1){if(clients > 0) /* 會話有客戶端在線, 發送音視頻數據 */{{     xop::AVFrame videoFrame = {0};// printf("width is %d, height is %d",colorBar.rows,colorBar.cols);// 編碼 發包if(0==i){// 第一幀有sps信息 給他兩幀拼一起char *buffer1;int size1;e.init(buffer1,size1);videoFrame.size = size1;e.postAframe(colorBar,buffer,size);videoFrame.size += size;					videoFrame.buffer.reset(new uint8_t[videoFrame.size]);memcpy(videoFrame.buffer.get(), buffer1, size1);memcpy(videoFrame.buffer.get()+size1, buffer, size);i++;}		else{e.postAframe(colorBar,buffer,size);videoFrame.size = size;  // 視頻幀大小 videoFrame.buffer.reset(new uint8_t[videoFrame.size]);memcpy(videoFrame.buffer.get(), buffer, videoFrame.size);}videoFrame.type = IsKeyFrame(buffer, size) ? xop::VIDEO_FRAME_I : xop::VIDEO_FRAME_P;// videoFrame.type = 0; // 建議確定幀類型。I幀(xop::VIDEO_FRAME_I) P幀(xop::VIDEO_FRAME_P)videoFrame.timestamp = xop::H264Source::GetTimestamp(); // 時間戳, 建議使用編碼器提供的時間戳// writeCharPointerToFile((char *)videoFrame.buffer.get(), videoFrame.size,  "filename.txt");		rtsp_server->PushFrame(session_id, xop::channel_0, videoFrame); //送到服務器進行轉發, 接口線程安全/*//獲取一幀 H264, 打包xop::AVFrame videoFrame = {0};videoFrame.type = 0; // 建議確定幀類型。I幀(xop::VIDEO_FRAME_I) P幀(xop::VIDEO_FRAME_P)videoFrame.size = video frame size;  // 視頻幀大小 videoFrame.timestamp = xop::H264Source::GetTimestamp(); // 時間戳, 建議使用編碼器提供的時間戳videoFrame.buffer.reset(new uint8_t[videoFrame.size]);                    memcpy(videoFrame.buffer.get(), video frame data, videoFrame.size);					rtsp_server->PushFrame(session_id, xop::channel_0, videoFrame); //送到服務器進行轉發, 接口線程安全*/}{				/*//獲取一幀 AAC, 打包xop::AVFrame audioFrame = {0};audioFrame.type = xop::AUDIO_FRAME;audioFrame.size = audio frame size;  /* 音頻幀大小 audioFrame.timestamp = xop::AACSource::GetTimestamp(44100); // 時間戳audioFrame.buffer.reset(new uint8_t[audioFrame.size]);                    memcpy(audioFrame.buffer.get(), audio frame data, audioFrame.size);rtsp_server->PushFrame(session_id, xop::channel_1, audioFrame); // 送到服務器進行轉發, 接口線程安全*/}		}// xop::Timer::Sleep(20);  /* 實際使用需要根據幀率計算延時!  我這里處理延時很大,就不人工延遲了*/}videocapture->release();e.deinit((MPP_RET)0);yolo5.deinit();}

rk3588編碼

這方面網上的文章不太多,但是官方給了demo,都是中文,認真看看,功能都是能用的。
主要參考他的mpp-develop/test/mpi_enc_test 以及Rk3588-linux-v002\linux\docs\Linux\Multimedia\Rockchip_Developer_Guide_MPP_CN.pdf進行配置和使用。這相關的東西我之后再另開一個文章單獨說,總之參考這部分可以做一個BGR888轉成264存儲的demo。
這里重點說一個概念,I、P、B幀
這個東西是264流編碼的一個概念,正常你每一幀的圖像都很大,比如一個1920*1080,每個像素點存一個BGR888的話,就是1920*1080*3 Byte =6220800 Byte ≈ 6M ,然后一秒30幀的話,一秒就要傳180M,局域網或許勉強可以,但是對帶寬壓力也很大。所以這里就涉及到了壓縮,264、265就是壓縮標準,壓縮中需要做兩種壓縮 幀內壓縮幀間壓縮

  • 幀內壓縮:使用一定方法使用盡量小的空間存一幀數據。

  • 幀間壓縮:利用幀和前后幀的關聯來進一步的壓縮視頻。

這里我們重點關注幀間壓縮。給一個參考文獻
https://zhuanlan.zhihu.com/p/409527359
說的挺透徹的,這里我粗略說一下,看下面這個圖
在這里插入圖片描述

I幀 ,是幀間壓縮里面的一幀完整圖像,P幀是前向預測幀,B幀是雙向預測幀
IDR幀是特殊的I幀,在編解碼時候P、B幀可以參考I幀前面的幀進行復原,但是不能參考IDR幀前面的幀進行復原

而一個上面這樣的循環,被編碼成一組,我們指定了h264的gop大小,就確定了多少幀中有一個I幀
而我們在做直播,就導致為了低延時,不要B幀,gop 也要盡量的不要太大,如果要解碼P幀,前面就一定要緩存I幀,

監測工具

wireshark, 這個感覺還是蠻必要的,至少你能看見客戶端和服務器之間說沒說話。如果你有耐心開RTSP 或者網絡協議握手說明的話,你甚至能看到他們之間的握手流程。

雜記

中間遇到了一個問題,因為我這個地方沒有公網,所以只能自己給開一個熱點給電腦,PC和RK3588之間通過路由器連接,然后使用前面的某個demo的時候遇到的,主機向服務器(RK3588)發出視頻請求時,服務器并沒有直接給客戶端發rtcp包,而是給一個200多的地址發包,而這個包在客戶端收不到,但是他能收到rtp的協議包,所以vlc這邊也不提示打不開,就是沒有圖像顯示。
后來使用的方法是,和板子通過路由器網線連接,然后電腦PC wifi 連接路由器,然后路由器沒有公網,再使用一個手機usb給電腦共享網絡,讓我的調試環境穩定可以接受到3588的流,和路由器網線連接的時候接受不到包,感覺是因為路由器沒有網,使用網線連接時候被屏蔽了服務器功能,所以交給路由器的包轉發請求沒有被PC識別到,但是wifi連接的話,就算他沒有網,也是不能忽略的。 目前是這樣理解的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/209714.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/209714.shtml
英文地址,請注明出處:http://en.pswp.cn/news/209714.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

字符串函數strtok

1.調用格式&#xff1a; 2.調用形式&#xff1a;char*strtok(char*p1,const char*p2),其中第二個是由分隔符組成的字符串&#xff0c;第一個為需要分隔的字符串 3.調用目的&#xff1a;將分隔符之間的字符串取出 4.調用時一般將源字符串拷貝后調用&#xff0c;因為此函數會將…

基于Unity3D 低多邊形地形模型紋理貼圖

在線工具推薦&#xff1a; 3D數字孿生場景編輯器 - GLTF/GLB材質紋理編輯器 - 3D模型在線轉換 - Three.js AI自動紋理開發包 - YOLO 虛幻合成數據生成器 - 三維模型預覽圖生成器 - 3D模型語義搜索引擎 當談到游戲角色的3D模型風格時&#xff0c;有幾種不同的風格&#xf…

【工程實踐】使用modelscope下載大模型文件

前言 Modelscope&#xff08;魔搭社區&#xff09;是阿里達摩院的一款開源模型平臺&#xff0c;里面提供了很多的熱門模型供使用體驗&#xff0c;其中的模型文件可以通過git clone 快速下載。并且為模型提供了Notebook的快速開發體驗&#xff0c;使用阿里云服務&#xff0c;不需…

【優選算法系列】【專題二滑動窗口】第三節.904. 水果成籃和438. 找到字符串中所有字母異位詞

文章目錄 前言一、水果成籃 1.1 題目描述 1.2 題目解析 1.2.1 算法原理 1.2.2 代碼編寫 1.2.3 題目總結二、找到字符串中所有字母異位詞 2.1 題目描述 2.2 題目解析 2.2.1 算法原理 2.2.2 代碼編寫 …

SAP UI5 walkthrough step9 Component Configuration

在之前的章節中&#xff0c;我們已經介紹完了MVC的架構和實現&#xff0c;現在我們來講一下&#xff0c;SAPUI5的結構 這一步&#xff0c;我們將所有的UI資產從index.html里面獨立封裝在一個組件里面 這樣組件就變得獨立&#xff0c;可復用了。這樣&#xff0c;無所什么時候我…

隊列的實現

學習就像一段長跑&#xff0c;比的不是誰跑得快&#xff0c;而是誰更能堅持&#xff01;&#xff01; 1 隊列的概念及結構 隊列&#xff1a;只允許在一端進行插入數據操作&#xff0c;在另一端進行刪除數據操作的特殊線性表&#xff0c;隊列具有先進先出 FIFO(First In First O…

外網訪問內網服務器使用教程

如何在任何地方都能訪問自己家里的筆記本上的應用&#xff1f;如何讓局域網的服務器可以被任何地方訪問到&#xff1f;有很多類似的需求&#xff0c;我們可以統一用一個解決方案&#xff1a;內網穿透。內網穿透的工具及方式有很多&#xff0c;如Ngrok、Ssh、autossh、Natapp、F…

linux具體命令(一)

1. cd CD命令是Linux和類Unix操作系統中非常常用的一個命令&#xff0c;它的全稱是“change directory”&#xff0c;用于改變當前的工作目錄。用戶可以通過這個命令進入到不同的目錄中&#xff0c;進行文件操作或是執行其他任務。 以下是CD命令的一些基本用法&#xff1a; 進…

特殊進程之守護進程

文章目錄 1、守護進程的概念2、如何查看守護進程3、編寫守護進程的步驟3.1 創建子進程&#xff0c;父進程退出3.2 在子進程中創建新會話3.3 改變當前工作目錄3.4 重設文件權限掩碼3.5 關閉不需要的文件描述符3.6 某些特殊的守護進程打開/dev/null 4、守護進程代碼示例 1、守護進…

[UNILM]論文實現:Unified Language Model Pre-training for Natural Language.........

文章目錄 一、完整代碼二、論文解讀2.1 介紹2.2 架構2.3 輸入端2.4 結果 三、過程實現四、整體總結 論文&#xff1a;Unified Language Model Pre-training for Natural Language Understanding and Generation 作者&#xff1a;Li Dong, Nan Yang, Wenhui Wang, Furu Wei, Xia…

js new 原理

mdn new new 調用函數時&#xff0c;該函數將被用作構造函數 類只能用 new 運算符實例化 不使用 new 調用一個類將拋出 TypeError。 過程 new Foo(…) 執行時&#xff1a; 創建一個空的簡單 JavaScript 對象。 為方便起見&#xff0c;我們稱之為 newInstance。 如果構造函數…

華為OD機試真題-執行任務賺積分-2023年OD統一考試(C卷)

題目描述: 現有N個任務需要處理,同一時間只能處理一個任務,處理每個任務所需要的時間固定為1。 每個任務都有最晚處理時間限制和積分值,在最晚處理時間點之前處理完成任務才可獲得對應的積分獎勵。 可用于處理任務的時間有限,請問在有限的時間內,可獲得的最多積分。 輸入…

《LeetCode力扣練習》代碼隨想錄——字符串(替換數字---Java)

《LeetCode力扣練習》代碼隨想錄——字符串&#xff08;替換數字—Java&#xff09; 刷題思路來源于 代碼隨想錄 54. 替換數字 受制于語言限制&#xff0c;很普通的解法 import java.util.Scanner; class Main {public static void main(String[] args) {Scanner innew Scanner…

MyBatis--07--啟動過程分析、SqlSession安全問題、攔截器

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 談談MyBatis的啟動過程具體的操作過程如下&#xff1a;實現測試類,并測試SqlSessionFactorySqlSession SqlSession有數據安全問題?在MyBatis中&#xff0c;SqlSess…

vuex如何存儲數據、獲取數據、以及數據的持久化

前提必須已經在vue中安裝了vuex插件不然無法使用&#xff0c;不知道怎么創建vue和安裝vuex的可以看這個視頻&#xff0c;node.js版本最好16以上不然可能會安裝失敗&#xff1a;30分鐘學會Vue之VueRouter&Vuex 趁著暑假掌握一門技能 大學生前端實習畢業設計必備技能_嗶哩嗶哩…

好代碼資源網整站打包代碼(包含了最新數據),集成了深度二開的ripro主題,非常適合做資源網站創業用

好代碼資源網是基于wordpress開發的一個資源分享類網站&#xff0c;在開發者圈子里還算小有名氣&#xff0c;這里分享嬰整站打包代碼&#xff08;包含了最新數據&#xff09;。網站本身集成了深度二開的ripro主題&#xff0c;非常適合做資源網站創業用。 資源下載類網站目前還…

Button背景顏色改不了,一直是默認的紫色

使用android.widget.Button <android.widget.Buttonandroid:layout_width"wrap_content"android:layout_height"wrap_content"android:onClick"doClick"android:text"這是一個按鈕"android:textColor"color/black"androi…

kubesphere安裝后啟用DevOps

官方文檔&#xff1a;KubeSphere DevOps 系統 1、集群管理---定制資源定義 進入目錄&#xff1a;集群管理---定制資源定義搜索&#xff1a;clusterconfiguration 點擊 ks-installer 右側的 &#xff0c;選擇編輯 YAML 在該 YAML 文件中&#xff0c;搜索 devops&#xff0c;…

力扣98. 驗證二叉搜索樹

深度優先遍歷 思路&#xff1a; 根據二叉搜索樹特性&#xff0c;通過中序遍歷得到有序序列&#xff0c;驗證序列是否有序來判斷&#xff1b;中序遍歷使用棧通過深度優先遍歷&#xff1b; /*** Definition for a binary tree node.* struct TreeNode {* int val;* Tre…

No CUDA GPUs are available

文章目錄 前言嘗試方法一、嘗試方法一二、嘗試方法二 總結 前言 之前用服務器跑的時候&#xff0c;發現是可以跑的。但當有其他人一同使用的時候&#xff0c;就會拋出&#xff1a;No CUDA GPUs are available&#xff0c;這個時候我嘗試了以下兩種方式解決&#xff0c;后面終于…