局域網TCP通過組播放地址rtp推流和拉流實現實時喊話

應用場景,安卓端局域網不用ip通過組播放地址實現實時對講功能

發送端: ffmpeg -f alsa -i hw:1 -acodec aac -ab 64k -ac 2 -ar 16000 -frtp -sdp file stream.sdp rtp://224.0.0.1:14556

接收端: ffmpeg -protocol whitelist file,udp,rtp -i stream.sdp -acodec pcm s16le -ar 16000 -ac 2 -f alsa default

在windows上測試通過后然后在安卓中實現

# 查詢本地可用麥克風設備
ffmpeg -list_devices true -f dshow -i dummy

麥克風 (Realtek(R) Audio)這是我電腦的
# windows? 執行RTM推音頻流
ffmpeg -f dshow -i audio="麥克風 (Realtek(R) Audio)" -acodec aac -ab 64k -ac 2 -ar 16000 -f rtp -sdp_file stream.sdp rtp://239.0.0.1:15556

上面windows上調通后接下來在安卓上實現

implementation("com.arthenica:mobile-ffmpeg-full:4.4.LTS")主要用到這個庫
package com.xhx.megaphone.tcpimport android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import com.arthenica.mobileffmpeg.Config
import com.arthenica.mobileffmpeg.FFmpeg
import com.blankj.utilcode.util.LogUtils
import com.xhx.megaphone.App
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.concurrent.atomic.AtomicBoolean/*** 實時推流助手 - 最優化版本* * 功能:* 1. 錄音buffer直接實時寫入臨時文件* 2. FFmpeg同時讀取文件進行推流* 3. 最小化延遲的實時推流*/
object LiveStreamingHelper {private const val TAG = "LiveStreamingHelper"// 組播配置private const val MULTICAST_ADDRESS = "239.0.0.1"private const val MULTICAST_PORT = 15556// 音頻參數private const val SAMPLE_RATE = 16000private const val CHANNELS = 1private const val BIT_RATE = 64000private const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT// 緩沖區大小 - 使用較小的緩沖區減少延遲private val BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE,AudioFormat.CHANNEL_IN_MONO,AUDIO_FORMAT).let { minSize ->// 使用最小緩沖區的2倍,減少延遲minSize * 2}// 推流狀態private val isStreaming = AtomicBoolean(false)private var audioRecord: AudioRecord? = nullprivate var recordingThread: Thread? = nullprivate var ffmpegExecutionId: Long = 0// 文件路徑private val cacheDir = File(App.ctx.cacheDir, "live_streaming")private val sdpFile = File(cacheDir, "stream.sdp")private val liveAudioFile = File(cacheDir, "live_audio.pcm")/*** 開始實時錄音推流*/fun startStreaming(): Boolean {if (isStreaming.get()) {LogUtils.w(TAG, "推流已在進行中")return false}return try {initializeFiles()createSdpFile()startAudioRecording()startLiveStreaming()isStreaming.set(true)LogUtils.i(TAG, "? 實時推流啟動成功")true} catch (e: Exception) {LogUtils.e(TAG, "? 實時推流啟動失敗", e)stopStreaming()false}}/*** 停止推流*/fun stopStreaming() {if (!isStreaming.get()) {return}isStreaming.set(false)// 停止錄音audioRecord?.stop()audioRecord?.release()audioRecord = null// 停止錄音線程recordingThread?.interrupt()recordingThread = null// 停止FFmpegif (ffmpegExecutionId != 0L) {FFmpeg.cancel(ffmpegExecutionId)ffmpegExecutionId = 0}LogUtils.i(TAG, "🛑 實時推流已停止")}/*** 獲取推流狀態*/fun isStreaming(): Boolean = isStreaming.get()/*** 獲取SDP文件路徑*/fun getSdpFilePath(): String = sdpFile.absolutePath/*** 獲取組播地址信息*/fun getMulticastInfo(): String {val fileSize = if (liveAudioFile.exists()) {"${liveAudioFile.length() / 1024}KB"} else {"0KB"}return "組播地址: $MULTICAST_ADDRESS:$MULTICAST_PORT\n" +"SDP文件: ${sdpFile.absolutePath}\n" +"傳輸方式: 實時文件流\n" +"緩沖區大小: ${BUFFER_SIZE}字節\n" +"當前文件大小: $fileSize\n" +"推流狀態: ${if (isStreaming.get()) "進行中" else "已停止"}"}/*** 初始化文件和目錄*/private fun initializeFiles() {if (!cacheDir.exists()) {cacheDir.mkdirs()}// 清理舊文件if (liveAudioFile.exists()) {liveAudioFile.delete()}// 創建新的音頻文件liveAudioFile.createNewFile()}/*** 創建SDP文件*/private fun createSdpFile() {val sdpContent = """v=0o=- 0 0 IN IP4 127.0.0.1s=No Namec=IN IP4 $MULTICAST_ADDRESSt=0 0a=tool:libavformat 58.45.100m=audio $MULTICAST_PORT RTP/AVP 97b=AS:64a=rtpmap:97 MPEG4-GENERIC/$SAMPLE_RATE/$CHANNELSa=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=141056E500""".trimIndent()sdpFile.writeText(sdpContent)LogUtils.i(TAG, "SDP文件創建成功: ${sdpFile.absolutePath}")}/*** 開始音頻錄音 - 直接實時寫入文件*/private fun startAudioRecording() {audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,SAMPLE_RATE,AudioFormat.CHANNEL_IN_MONO,AUDIO_FORMAT,BUFFER_SIZE)if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) {throw IOException("AudioRecord初始化失敗")}audioRecord?.startRecording()// 啟動錄音線程,實時寫入文件recordingThread = Thread {val buffer = ByteArray(BUFFER_SIZE)var fileOutputStream: FileOutputStream? = nullvar totalBytes = 0var lastLogTime = System.currentTimeMillis()try {fileOutputStream = FileOutputStream(liveAudioFile, false) // 不追加,覆蓋寫入LogUtils.i(TAG, "錄音線程啟動,實時寫入: ${liveAudioFile.absolutePath}")LogUtils.i(TAG, "緩沖區大小: $BUFFER_SIZE 字節")while (isStreaming.get() && !Thread.currentThread().isInterrupted) {val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0if (bytesRead > 0) {// 立即寫入文件并刷新fileOutputStream.write(buffer, 0, bytesRead)fileOutputStream.flush()totalBytes += bytesRead// 每3秒打印一次狀態(更頻繁的狀態更新)val currentTime = System.currentTimeMillis()if (currentTime - lastLogTime > 3000) {LogUtils.d(TAG, "🎙? 實時錄音中: ${totalBytes / 1024}KB, 速率: ${(totalBytes / ((currentTime - (lastLogTime - 3000)) / 1000.0) / 1024).toInt()}KB/s")lastLogTime = currentTime}} else if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) {LogUtils.e(TAG, "AudioRecord讀取錯誤: ERROR_INVALID_OPERATION")break} else if (bytesRead < 0) {LogUtils.w(TAG, "AudioRecord讀取返回負值: $bytesRead")}}LogUtils.i(TAG, "錄音線程結束,總計: ${totalBytes / 1024}KB")} catch (e: Exception) {LogUtils.e(TAG, "錄音數據寫入異常", e)} finally {fileOutputStream?.close()}}recordingThread?.start()LogUtils.i(TAG, "音頻錄音已啟動")}/*** 啟動實時推流*/private fun startLiveStreaming() {GlobalScope.launch(Dispatchers.IO) {// 等待一些音頻數據寫入Thread.sleep(300)// 構建FFmpeg命令 - 使用較小的緩沖區和實時參數val command = "-re -f s16le -ar $SAMPLE_RATE -ac $CHANNELS " +"-thread_queue_size 512 " +  // 增加線程隊列大小"-i ${liveAudioFile.absolutePath} " +"-acodec aac -ab ${BIT_RATE/1000}k -ac $CHANNELS -ar $SAMPLE_RATE " +"-f rtp -sdp_file ${sdpFile.absolutePath} " +"rtp://$MULTICAST_ADDRESS:$MULTICAST_PORT"LogUtils.i(TAG, "FFmpeg實時推流命令: $command")ffmpegExecutionId = FFmpeg.executeAsync(command) { executionId, returnCode ->LogUtils.i(TAG, "FFmpeg推流結束: executionId=$executionId, returnCode=$returnCode")when (returnCode) {Config.RETURN_CODE_SUCCESS -> {LogUtils.i(TAG, "? 推流正常結束")}Config.RETURN_CODE_CANCEL -> {LogUtils.i(TAG, "🛑 推流被用戶取消")}else -> {LogUtils.w(TAG, "?? 推流異常結束,返回碼: $returnCode")}}}LogUtils.i(TAG, "FFmpeg執行ID: $ffmpegExecutionId")}}
}

拉流

package com.xhx.megaphone.tcpimport android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import com.arthenica.mobileffmpeg.Config
import com.arthenica.mobileffmpeg.FFmpeg
import com.blankj.utilcode.util.LogUtils
import com.xhx.megaphone.App
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.io.RandomAccessFile
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong/*** 低延遲拉流播放助手* * 優化策略:* 1. 最小化FFmpeg緩沖* 2. 減少AudioTrack緩沖區* 3. 更頻繁的數據讀取* 4. 優化文件IO*/
object LowLatencyPullHelper {private const val TAG = "LowLatencyPullHelper"// 音頻參數private const val SAMPLE_RATE = 16000private const val CHANNELS = 1private const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT// 低延遲參數private const val SMALL_BUFFER_SIZE = 1024  // 使用更小的緩沖區private const val READ_INTERVAL_MS = 20     // 更頻繁的讀取間隔// 拉流狀態private val isPulling = AtomicBoolean(false)private var ffmpegExecutionId: Long = 0private var audioTrack: AudioTrack? = nullprivate var playbackThread: Thread? = null// 文件讀取位置private val fileReadPosition = AtomicLong(0)// 文件路徑private val cacheDir = File(App.ctx.cacheDir, "low_latency_pull")private val outputPcmFile = File(cacheDir, "realtime_audio.pcm")/*** 開始低延遲拉流播放*/fun startPulling(sdpFilePath: String): Boolean {if (isPulling.get()) {LogUtils.w(TAG, "拉流已在進行中")return false}val sdpFile = File(sdpFilePath)if (!sdpFile.exists()) {LogUtils.e(TAG, "SDP文件不存在: $sdpFilePath")return false}return try {initializeFiles()startLowLatencyDecoding(sdpFilePath)startLowLatencyPlayback()isPulling.set(true)fileReadPosition.set(0)LogUtils.i(TAG, "? 低延遲拉流播放啟動成功")true} catch (e: Exception) {LogUtils.e(TAG, "? 低延遲拉流播放啟動失敗", e)stopPulling()false}}/*** 停止拉流*/fun stopPulling() {if (!isPulling.get()) {return}isPulling.set(false)// 停止FFmpegif (ffmpegExecutionId != 0L) {FFmpeg.cancel(ffmpegExecutionId)ffmpegExecutionId = 0}// 停止音頻播放audioTrack?.stop()audioTrack?.release()audioTrack = null// 停止播放線程playbackThread?.interrupt()playbackThread = nullLogUtils.i(TAG, "🛑 低延遲拉流已停止")}/*** 獲取拉流狀態*/fun isPulling(): Boolean = isPulling.get()/*** 獲取拉流信息*/fun getPullInfo(): String {val fileSize = if (outputPcmFile.exists()) {"${outputPcmFile.length() / 1024}KB"} else {"0KB"}return "拉流狀態: ${if (isPulling.get()) "進行中" else "已停止"}\n" +"解碼文件: ${outputPcmFile.absolutePath}\n" +"文件大小: $fileSize\n" +"讀取位置: ${fileReadPosition.get() / 1024}KB\n" +"優化模式: 低延遲"}/*** 初始化文件和目錄*/private fun initializeFiles() {if (!cacheDir.exists()) {cacheDir.mkdirs()}// 清理舊文件if (outputPcmFile.exists()) {outputPcmFile.delete()}}/*** 啟動低延遲FFmpeg解碼*/private fun startLowLatencyDecoding(sdpFilePath: String) {GlobalScope.launch(Dispatchers.IO) {// 減少等待時間Thread.sleep(500)// 構建超低延遲FFmpeg解碼命令val command = "-protocol_whitelist file,udp,rtp " +"-fflags +nobuffer+flush_packets " +      // 禁用緩沖并立即刷新"-flags low_delay " +                     // 低延遲模式"-probesize 32 " +                        // 最小探測大小"-analyzeduration 0 " +                   // 不分析流"-max_delay 0 " +                         // 最大延遲為0"-reorder_queue_size 0 " +                // 禁用重排序隊列"-rw_timeout 3000000 " +                  // 3秒超時"-i $sdpFilePath " +"-acodec pcm_s16le " +"-ar $SAMPLE_RATE " +"-ac $CHANNELS " +"-f s16le " +"-flush_packets 1 " +                     // 立即刷新數據包"${outputPcmFile.absolutePath}"LogUtils.i(TAG, "低延遲FFmpeg解碼命令: $command")ffmpegExecutionId = FFmpeg.executeAsync(command) { executionId, returnCode ->LogUtils.i(TAG, "FFmpeg解碼結束: executionId=$executionId, returnCode=$returnCode")when (returnCode) {Config.RETURN_CODE_SUCCESS -> {LogUtils.i(TAG, "? 解碼正常結束")}Config.RETURN_CODE_CANCEL -> {LogUtils.i(TAG, "🛑 解碼被用戶取消")}else -> {LogUtils.w(TAG, "?? 解碼異常結束,返回碼: $returnCode")}}}}}/*** 啟動低延遲音頻播放*/private fun startLowLatencyPlayback() {// 使用最小緩沖區val minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE,AudioFormat.CHANNEL_OUT_MONO,AUDIO_FORMAT)// 使用稍大于最小緩沖區的大小,但不要太大val bufferSize = minBufferSize * 2audioTrack = AudioTrack(AudioManager.STREAM_MUSIC,SAMPLE_RATE,AudioFormat.CHANNEL_OUT_MONO,AUDIO_FORMAT,bufferSize,AudioTrack.MODE_STREAM)// 設置低延遲模式(API 26+)try {if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {val audioAttributes = android.media.AudioAttributes.Builder().setUsage(android.media.AudioAttributes.USAGE_MEDIA).setContentType(android.media.AudioAttributes.CONTENT_TYPE_MUSIC).setFlags(android.media.AudioAttributes.FLAG_LOW_LATENCY).build()audioTrack = AudioTrack.Builder().setAudioAttributes(audioAttributes).setAudioFormat(AudioFormat.Builder().setEncoding(AUDIO_FORMAT).setSampleRate(SAMPLE_RATE).setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build()).setBufferSizeInBytes(bufferSize).setTransferMode(AudioTrack.MODE_STREAM).build()}} catch (e: Exception) {LogUtils.w(TAG, "無法設置低延遲AudioTrack,使用默認配置", e)}audioTrack?.play()LogUtils.i(TAG, "AudioTrack初始化完成,緩沖區大小: $bufferSize")// 啟動高頻率播放線程playbackThread = Thread {val buffer = ByteArray(SMALL_BUFFER_SIZE)var totalPlayed = 0var lastLogTime = System.currentTimeMillis()LogUtils.i(TAG, "低延遲音頻播放線程啟動")while (isPulling.get() && !Thread.currentThread().isInterrupted) {try {if (outputPcmFile.exists()) {val currentFileSize = outputPcmFile.length()val currentReadPos = fileReadPosition.get()// 如果有新數據可讀if (currentFileSize > currentReadPos) {RandomAccessFile(outputPcmFile, "r").use { randomAccessFile ->randomAccessFile.seek(currentReadPos)val bytesRead = randomAccessFile.read(buffer)if (bytesRead > 0) {audioTrack?.write(buffer, 0, bytesRead)totalPlayed += bytesReadfileReadPosition.addAndGet(bytesRead.toLong())// 每2秒打印一次狀態val currentTime = System.currentTimeMillis()if (currentTime - lastLogTime > 2000) {LogUtils.d(TAG, "🔊 低延遲播放: ${totalPlayed / 1024}KB, 延遲: ${(currentFileSize - currentReadPos) / 32}ms")lastLogTime = currentTime}}}}}// 高頻率檢查,減少延遲Thread.sleep(READ_INTERVAL_MS.toLong())} catch (e: Exception) {LogUtils.e(TAG, "低延遲播放異常", e)Thread.sleep(100)}}LogUtils.i(TAG, "低延遲播放線程結束,總計播放: ${totalPlayed / 1024}KB")}playbackThread?.start()}
}

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

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

相關文章

基于深度學習的醫學圖像分析:使用YOLOv5實現細胞檢測

最近研學過程中發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊鏈接跳轉到網站人工智能及編程語言學習教程。讀者們可以通過里面的文章詳細了解一下人工智能及其編程等教程和學習方法。下面開始對正文內容的…

32.768KHZ 3215晶振CM315D與NX3215SA應用全場景

在現代電子設備中&#xff0c;一粒米大小的晶振&#xff0c;卻是掌控時間精度的“心臟”。CITIZEN的CM315D系列與NDK的NX3215SA系列晶振便是其中的佼佼者&#xff0c;它們以 3.2 1.5 mm 的小尺寸”(厚度不足1mm)&#xff0c;成為智能設備中隱形的節奏大師。精準計時的奧秘這兩…

嵌軟面試——ARM Cortex-M寄存器組

Cortex-M內存架構包含16個通用寄存器&#xff0c;其中R0-R12是13個32位的通用寄存器&#xff0c;另外三個寄存器是特殊用途&#xff0c;分別是R13&#xff08;棧指針&#xff09;,R14&#xff08;鏈接寄存器&#xff09;,R15&#xff08;程序計數器&#xff09;。對于處理器來說…

7.DRF 過濾、排序、分頁

過濾Filtering 對于列表數據可能需要根據字段進行過濾&#xff0c;我們可以通過添加django-fitlter擴展來增強支持。 pip install django-filter在配置文件中增加過濾器類的全局設置&#xff1a; """drf配置信息必須全部寫在REST_FRAMEWORK配置項中""…

二、CUDA、Pytorch與依賴的工具包

CUDA Compute Unified Device Architecture&#xff08;統一計算架構&#xff09;。專門用于 GPU 通用計算 的平臺 編程接口。CUDA可以使你的程序&#xff08;比如矩陣、神經網絡&#xff09;由 GPU 執行&#xff0c;這比CPU能快幾十甚至上百倍。 PyTorch 是一個深度學習框架…

SpringCloude快速入門

近期簡單了解一下SpringCloude微服務,本文主要就是我學習中所記錄的筆記,然后具體原理可能等以后再來深究,本文可能有些地方用詞不專業還望包容一下,感興趣可以參考官方文檔來深入學習一下微服務,然后我的下一步學習就是docker和linux了。 nacos: Nacos 快速開始 | Nacos 官網…

GPT Agent與Comet AI Aent瀏覽器對比橫評

1. 架構設計差異GPT Agent的雙瀏覽器架構&#xff1a;文本瀏覽器&#xff1a;專門用于高效處理大量文本內容&#xff0c;適合深度信息檢索和文獻追蹤&#xff0c;相當于Deep Research的延續可視化瀏覽器&#xff1a;具備界面識別與交互能力&#xff0c;可以點擊網頁按鈕、識別圖…

應用信息更新至1.18.0

增加DO權限 增加權限管理&#xff08;需DO支持&#xff09; 增加應用凍結隱藏&#xff08;需DO支持&#xff09; 增加權限委托&#xff08;需DO支持&#xff09; 增加特殊組件 ...

常用git命令集錦

git init 初始化 將當前目錄初始化為 git 本地倉庫&#xff0c;此時會在本地創建一個 .git 的文件夾 git init -q 靜默執行&#xff0c;就是在后臺執行 git init --bare –bare 參數,一般用來初始化一個空的目錄&#xff0c;作為遠程存儲倉庫 git init --template dir –templa…

skywalking安裝

一、簡介 SkyWalking是一款用于分布式系統跟蹤和性能監控的開源工具。它可以幫助開發人員了解分布式系統中不同組件之間的調用關系和性能指標&#xff0c;從而進行故障排查和性能優化。 它支持多種語言和框架&#xff0c;包括Java、.NET、Node.js等。它通過在應用程序中插入代…

利用DataStream和TrafficPeak實現大數據可觀察性

可觀察性工作流對于深入了解應用程序的健康狀況、客戶流量和整體性能至關重要。然而&#xff0c;要實現真正的可觀察性還面臨一些挑戰&#xff0c;包括海量的流量數據、數據保留、實施時間以及各項成本等。TrafficPeak是一款為Akamai云平臺打造&#xff0c;簡單易用、可快速部署…

jQuery 最新語法大全詳解(2025版)

引言 jQuery 作為輕量級 JavaScript 庫&#xff0c;核心價值在于 簡化 DOM 操作、跨瀏覽器兼容性和高效開發。盡管現代框架崛起&#xff0c;jQuery 仍在遺留系統維護、快速原型開發中廣泛應用。本文涵蓋 jQuery 3.6 核心語法&#xff0c;重點解析高效用法與最佳實踐。 一、jQu…

Android 15 修改截圖默認音量大小

概述 在 Android 15 中,截圖音效的默認音量可能過大,影響用戶體驗。本文將介紹如何通過修改系統源碼來調整截圖音效的默認音量大小。 修改位置 需要修改的文件路徑: frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt…

Python爬蟲實戰:快速采集教育政策數據(附官網工具庫API)

解鎖教育政策研究的數據金礦&#xff0c;用技術提升學術效率 在教育政策研究領域&#xff0c;獲取最新、最全面的政策文本是學術工作的基礎。傳統手動收集方式效率低下且容易遺漏關鍵政策&#xff0c;而Python爬蟲技術為教育研究者提供了高效的數據采集解決方案。本文將系統介…

驗證回文串-leetcode

如果在將所有大寫字符轉換為小寫字符、并移除所有非字母數字字符之后&#xff0c;短語正著讀和反著讀都一樣。則可以認為該短語是一個 回文串 。 字母和數字都屬于字母數字字符。 給你一個字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b;否則&#xf…

嵌入式學習日志(十)

10 學習指針1 指針核心定義與本質1.1 指針與指針變量1、指針即地址&#xff0c;指針變量是存放地址的變量&#xff0c;其大小與操作系統位數相關&#xff1a;64 位系統中占 8 字節&#xff0c;32 位系統中占 4 字節。2、指針的核心功能是通過地址間接訪問目標變量&#xff0…

Anaconda創建環境報錯:CondaHTTPEFTOT: HTTP 403 FORBIDDEN for url

一、快速解決方案這類報錯的原因通常是由于 conda 無法訪問鏡像源或權限被服務器拒絕&#xff0c;以下是常見原因和對應的解決方案&#xff1a;檢查鏡像源拼寫是否正確conda config --show channels清華源鏡像示例如果不正確&#xff0c;先清除舊配置del %USERPROFILE%\.condar…

亞馬遜地址關聯暴雷:新算法下的賬號安全保衛戰

2025年Q3&#xff0c;上千個店鋪因共享稅代地址、海外倉信息重疊等問題被批量凍結&#xff0c;為行業敲響了“精細化合規”的警鐘。事件復盤&#xff1a;地址成為關聯風控的“致命開關”稅代機構違規引發“多米諾效應”事件的導火索指向稅代機構“saqibil”&#xff0c;其為降低…

在本地環境中運行 ‘dom-distiller‘ GitHub 庫的完整指南

在本地環境中運行 ‘dom-distiller’ GitHub 庫的完整指南 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家&#xff0c;覺得好請收藏。點擊跳轉到網站。 1. 項目概述 ‘dom-distiller’ 是一個用于將網頁…

11. isaacsim4.2教程-Transform 樹與Odometry

1. 前言學習目標在本示例中&#xff0c;你將學習如何&#xff1a;使用 TF 發布器將相機作為 TF 樹的一部分發布在 TF 上發布機械臂&#xff0f;可動結構&#xff08;articulation&#xff09;的樹狀結構發布里程計&#xff08;Odometry&#xff09;消息開始之前前置條件已完成 …