webrtc之高通濾波——HighPassFilter源碼及原理分析

文章目錄

  • 前言
  • 一、導讀
  • 二、高通濾波過程
    • 1.HighPassFilter的創建
      • 1)HighPassFilter的作用
      • 2)開啟條件
      • 3)開啟配置
    • 2.高通濾波整體過程
      • 1)觸發時機
      • 2)濾波器創建
      • 3)高通濾波過程
  • 三、算法實現
    • 1.原理
      • 1)濾波器種類
      • 2)給定系數創建濾波器
      • 3)給定零極點創建濾波器
        • 零點位置
        • 極點位置
    • 2.matlab分析
    • 3.濾波過程
      • 1)串聯二級IIR濾波器
      • 2)濾波計算
  • 總結


前言

webrtc中的高通濾波器可以大幅抑制 100 Hz 以下的頻率成分(如 50 Hz 電源噪聲、低頻雜音等),其核心使用一個二階IIR高通濾波器過濾低頻分量。

本篇文章中,將先對于HighPassFilter的創建和使用配置以及整體的使用流程做一個梳理,然后再深入介紹算法實現,并且會使用matlab畫出對應的通帶、阻帶、以及過渡帶方便直觀理解。

|版本聲明:山河君,未經博主允許,禁止轉載


一、導讀

在 WebRTC 的語音處理鏈路中,高通濾波器(High Pass Filter,HPF)幾乎是必備的一環。它的作用很直接:削弱 100Hz 以下的低頻成分,比如電源嗡聲、桌面震動、風扇噪聲等,從而保證后續 AGC、NS、AEC 模塊處理的信號更干凈。

  • 觸發方式
    可以手動配置開啟,也可能因為啟用 AEC 等模塊而被自動啟用。
  • 算法原理
    HPF 基于二階 IIR 濾波器實現,每個通道/子帶獨立濾波。核心公式:
    y[n]=b0x[n]+b1x[n?1]+b2x[n?2]?a1y[n?1]?a2y[n?2]y[n]=b_0x[n]+b_1x[n-1]+b_2x[n-2]-a_1y[n-1]-a_2y[n-2]y[n]=b0?x[n]+b1?x[n?1]+b2?x[n?2]?a1?y[n?1]?a2?y[n?2]
  • 濾波器系數
    根據采樣率(16k/32k/48k)選擇不同的 Butterworth 濾波器參數,保證截止頻率在 ~100Hz。
  • 實現特點
    • 使用 CascadedBiQuadFilter 串聯二階濾波器
    • 支持全頻帶或子帶處理
    • 性能開銷低,適合實時語音處理

二、高通濾波過程

1.HighPassFilter的創建

1)HighPassFilter的作用

HighPassFilter是用于去除音頻輸入中的低頻噪聲(如風聲、風扇、桌面震動等)。例如:在會議中,麥克風經常會錄到桌子震動的‘嗡嗡聲’,WebRTC 的高通濾波器就是專門干掉這種低頻噪聲的。

這個配置常用于語音增強處理鏈(如 AGC、NS、AEC)之前。

2)開啟條件

在 WebRTC 中,主動使用配置項 config.high_pass_filter.enabled = true 可以開啟高通濾波器(High Pass Filter,HPF)。但如果不主動配置,同樣有可能會默認打開高通濾波,如下代碼:

void AudioProcessingImpl::InitializeHighPassFilter(bool forced_reset) {bool high_pass_filter_needed_by_aec =config_.echo_canceller.enabled &&config_.echo_canceller.enforce_high_pass_filtering &&!config_.echo_canceller.mobile_mode;if (submodule_states_.HighPassFilteringRequired() ||high_pass_filter_needed_by_aec) {bool use_full_band = config_.high_pass_filter.apply_in_full_band &&!constants_.enforce_split_band_hpf;int rate = use_full_band ? proc_fullband_sample_rate_hz(): proc_split_sample_rate_hz();size_t num_channels =use_full_band ? num_output_channels() : num_proc_channels();if (!submodules_.high_pass_filter ||rate != submodules_.high_pass_filter->sample_rate_hz() ||forced_reset ||num_channels != submodules_.high_pass_filter->num_channels()) {submodules_.high_pass_filter.reset(new HighPassFilter(rate, num_channels));}} else {submodules_.high_pass_filter.reset();}
}

例如在開啟AEC時,會默認打開高通濾波,這只會影響高通濾波在3A處理時的順序。

3)開啟配置

HighPassFilter創建時受到采樣率和通道數的影響,具體代碼如下:

HighPassFilter::HighPassFilter(int sample_rate_hz, size_t num_channels): sample_rate_hz_(sample_rate_hz) {filters_.resize(num_channels);const auto& coefficients = ChooseCoefficients(sample_rate_hz_);for (size_t k = 0; k < filters_.size(); ++k) {filters_[k].reset(new CascadedBiQuadFilter(coefficients, kNumberOfHighPassBiQuads));}
}
  • ChooseCoefficients(sample_rate_hz_):根據采樣率確定濾波器系數
  • new CascadedBiQuadFilter:根據通道數確定創建濾波器個數,同時濾波器獲得系數

在本文中,將以采樣率為16k,單通道為例進行介紹

2.高通濾波整體過程

1)觸發時機

上文中已經說過,在不同條件下創建的HighPassFilter將會在不同時機進行高通濾波,以在處理近端采集信號做增益之前觸發高通濾波舉例:

if (submodules_.high_pass_filter &&(!config_.high_pass_filter.apply_in_full_band ||constants_.enforce_split_band_hpf)) {submodules_.high_pass_filter->Process(capture_buffer,/*use_split_band_data=*/true);}

其中滿足以下兩個條件時會進行高通濾波器

  • submodules_.high_pass_filter 不為空(即高通濾波器模塊已經初始化)
  • 滿足以下任意一個:
    • !config_.high_pass_filter.apply_in_full_band :表示不在 full-band 上應用 HPF
    • constants_.enforce_split_band_hpf 為真:強制在 split-band 上應用 HPF。

對于條件二,是判斷是否進行了頻帶分割對于全頻帶或者各個子頻道分別做高通濾波,而默認是對于各個子帶做高通濾波的

2)濾波器創建

HPF濾波器真正創建構造函數有兩種方式:

CascadedBiQuadFilter::CascadedBiQuadFilter(const CascadedBiQuadFilter::BiQuadCoefficients& coefficients,size_t num_biquads): biquads_(num_biquads, BiQuad(coefficients)) {}CascadedBiQuadFilter::CascadedBiQuadFilter(const std::vector<CascadedBiQuadFilter::BiQuadParam>& biquad_params) {for (const auto& param : biquad_params) {biquads_.push_back(BiQuad(param));}
}

前者是根據給定濾波器系數創建,后者是根據給定零極點計算出濾波器系數,具體的參數下文中會詳細介紹。

3)高通濾波過程

在webrtc之子帶分割下——SplittingFilter源碼分析文章中,對于頻帶分割規則有過詳細介紹:

  • 對于32k采樣率應該劃分為兩個子帶
  • 對于48k采樣率應該分為三個自帶
  • 對于16k采樣率不劃分子帶

而在使用HPF過程中,會默認對于每個通道的每個子帶進行高通濾波,如以下代碼

void HighPassFilter::Process(AudioBuffer* audio, bool use_split_band_data) {RTC_DCHECK(audio);RTC_DCHECK_EQ(filters_.size(), audio->num_channels());if (use_split_band_data) {for (size_t k = 0; k < audio->num_channels(); ++k) {rtc::ArrayView<float> channel_data = rtc::ArrayView<float>(audio->split_bands(k)[0], audio->num_frames_per_band());filters_[k]->Process(channel_data);}} else {for (size_t k = 0; k < audio->num_channels(); ++k) {rtc::ArrayView<float> channel_data =rtc::ArrayView<float>(&audio->channels()[k][0], audio->num_frames());filters_[k]->Process(channel_data);}}
}

而對于16k,單通道的音頻信號,只需要進行一次濾波即可,具體的算法進行下文會詳細介紹。

三、算法實現

1.原理

1)濾波器種類

CascadedBiQuadFilter是一種二階的IIR濾波器,并且會根據創建時的參數來指定是否進行串聯,如下文參數中:

  • 根據給定濾波器系數創建:num_biquads參數指定串聯個數
  • 根據給定零極點計算創建:std::vector<CascadedBiQuadFilter::BiQuadParam>個數決定串聯個數

在本文中,創建時傳入的是給定的濾波器系數,并且指定濾波器個數為:

constexpr size_t kNumberOfHighPassBiQuads = 1;

根據二階IIR濾波器差分方程,見文章語音信號處理六——遞歸/非遞歸離散時間系統與差分方程:
y[n]=b0x[n]+b1x[n?1]+b2x[n?2]?a1y[n?1]?a2y[n?2]y[n]=b_0x[n]+b_1x[n-1]+b_2x[n-2]-a_1y[n-1]-a_2y[n-2]y[n]=b0?x[n]+b1?x[n?1]+b2?x[n?2]?a1?y[n?1]?a2?y[n?2]
而結構體BiQuadCoefficients 保存濾波器系數

struct BiQuadCoefficients {float b[3];float a[2];};

2)給定系數創建濾波器

根據采樣率確定:

const CascadedBiQuadFilter::BiQuadCoefficients& ChooseCoefficients(int sample_rate_hz) {switch (sample_rate_hz) {case 16000:return kHighPassFilterCoefficients16kHz;case 32000:return kHighPassFilterCoefficients32kHz;case 48000:return kHighPassFilterCoefficients48kHz;default:RTC_NOTREACHED();}RTC_NOTREACHED();return kHighPassFilterCoefficients16kHz;
}

其中根據奎納斯采樣定理:

  • 16k采樣率過濾保留:100Hz~8kHz頻率分量
  • 32k采樣率過濾保留:100Hz~16kHz頻率分量
  • 48k采樣率過濾保留:100Hz~24kHz頻率分量

濾波器系數如下:

// [B,A] = butter(2,100/8000,'high')
constexpr CascadedBiQuadFilter::BiQuadCoefficientskHighPassFilterCoefficients16kHz = {{0.97261f, -1.94523f, 0.97261f},{-1.94448f, 0.94598f}};// [B,A] = butter(2,100/16000,'high')
constexpr CascadedBiQuadFilter::BiQuadCoefficientskHighPassFilterCoefficients32kHz = {{0.98621f, -1.97242f, 0.98621f},{-1.97223f, 0.97261f}};// [B,A] = butter(2,100/24000,'high')
constexpr CascadedBiQuadFilter::BiQuadCoefficientskHighPassFilterCoefficients48kHz = {{0.99079f, -1.98157f, 0.99079f},{-1.98149f, 0.98166f}};

3)給定零極點創建濾波器

值得注意的是,創建高通濾波器時不會使用到這種方式。

根據二階IIR濾波器傳遞函數,見文章語音信號處理十三——Z變換二(有理z變換、穩定性與反變換):
H(z)=b0+b1z?1+b2z?21+a0z?1+a1z?2H(z)=\frac{b_0+b_1z^{-1}+b_2z^{-2}}{1+a_0z^{-1}+a_1z^{-2}}H(z)=1+a0?z?1+a1?z?2b0?+b1?z?1+b2?z?2?
而對于代碼中


CascadedBiQuadFilter::BiQuad::BiQuad(const CascadedBiQuadFilter::BiQuadParam& param): x(), y() {float z_r = std::real(param.zero);float z_i = std::imag(param.zero);float p_r = std::real(param.pole);float p_i = std::imag(param.pole);float gain = param.gain;if (param.mirror_zero_along_i_axis) {// Assuming zeroes at z_r and -z_r.RTC_DCHECK(z_i == 0.f);coefficients.b[0] = gain * 1.f;coefficients.b[1] = 0.f;coefficients.b[2] = gain * -(z_r * z_r);} else {// Assuming zeros at (z_r + z_i*i) and (z_r - z_i*i).coefficients.b[0] = gain * 1.f;coefficients.b[1] = gain * -2.f * z_r;coefficients.b[2] = gain * (z_r * z_r + z_i * z_i);}// Assuming poles at (p_r + p_i*i) and (p_r - p_i*i).coefficients.a[0] = -2.f * p_r;coefficients.a[1] = p_r * p_r + p_i * p_i;
}

其中:

  • z_r ,z_i:是零點位置
  • p_r ,p_i :是極點位置
  • gain ,p_i :是極點位置
  • mirror_zero_along_i_axis:是否沿虛軸鏡像零點(實數對稱)
零點位置
  • 零點沿虛軸鏡像(zzz?z-z?z),其傳遞函數分子為:
    (1?zrz?1)(1+zrz?1)=1?zr2z?2(1-z_rz^{-1})(1+z_rz^{-1})=1-z_r^2z^{-2}(1?zr?z?1)(1+zr?z?1)=1?zr2?z?2
    那么對應系數為:
b[0] = gain * 1.f;
b[1] = 0.f;
b[2] = gain * -(z_r * z_r);
  • 常規復共軛零點(z=zr±ziiz=z_r\pm z_i iz=zr?±zi?i),其傳遞函數分母為:
    (1?(zr+jzi)z?1)(1+(zr?jzi)z?1)=1?2zr2z?1+(zr2+zi2)z?1(1-(z_r+jz_i)z^{-1})(1+(z_r-jz_i)z^{-1})=1-2z_r^2z^{-1}+(z_r^2+z_i^2)z^{-1}(1?(zr?+jzi?)z?1)(1+(zr??jzi?)z?1)=1?2zr2?z?1+(zr2?+zi2?)z?1
    那么對應的系數為
else {coefficients.b[0] = gain * 1.f;coefficients.b[1] = gain * -2.f * z_r;coefficients.b[2] = gain * (z_r * z_r + z_i * z_i);
}
極點位置

極點部分(pr±pi?i)(p_r \pm p_i*i)(pr?±pi??i)為復共軛,直接用共軛復數對展開,其傳遞函數分母為:
1?2prz?1+(pr2+pi2)z?21-2p_rz^{-1}+(p_r^2+p_i^2)z^{-2}1?2pr?z?1+(pr2?+pi2?)z?2

  coefficients.a[0] = -2.f * p_r;coefficients.a[1] = p_r * p_r + p_i * p_i;

2.matlab分析

對于給定濾波器系數,使用matlab畫出幅頻響應如下圖:
在這里插入圖片描述
對應代碼如下

% 二階濾波器系數
b = [0.97261, -1.94523, 0.97261];     % 分子系數(zeros)
a = [1.0, -1.94448, 0.94598];         % 分母系數(poles)% 使用 freqz 繪制頻率響應(默認 512 點)
fs = 16000; % 采樣率
[H, f] = freqz(b, a, 1024, fs);% 繪制幅頻響應
figure;
plot(f, 20*log10(abs(H)), 'LineWidth', 1.5);
grid on;
xlabel('Frequency (Hz)');
ylabel('Magnitude (dB)');
title('High-Pass Filter Frequency Response (16 kHz sampling rate)');
ylim([-60 5]);

3.濾波過程

1)串聯二級IIR濾波器

在進行濾波時,會對于每個IIR濾波器進行串聯計算

void CascadedBiQuadFilter::Process(rtc::ArrayView<float> y) {for (auto& biquad : biquads_) {ApplyBiQuad(y, y, &biquad);}
}

2)濾波計算

濾波計算實際就是根據給定的濾波器系數進行反饋回路計算,即:
y[n]=b0x[n]+b1x[n?1]+b2x[n?2]?a1y[n?1]?a2y[n?2]y[n]=b_0x[n]+b_1x[n-1]+b_2x[n-2]-a_1y[n-1]-a_2y[n-2]y[n]=b0?x[n]+b1?x[n?1]+b2?x[n?2]?a1?y[n?1]?a2?y[n?2]
代碼如下:

void CascadedBiQuadFilter::ApplyBiQuad(rtc::ArrayView<const float> x,rtc::ArrayView<float> y,CascadedBiQuadFilter::BiQuad* biquad) {RTC_DCHECK_EQ(x.size(), y.size());const auto* c_b = biquad->coefficients.b;const auto* c_a = biquad->coefficients.a;auto* m_x = biquad->x;auto* m_y = biquad->y;for (size_t k = 0; k < x.size(); ++k) {const float tmp = x[k];y[k] = c_b[0] * tmp + c_b[1] * m_x[0] + c_b[2] * m_x[1] - c_a[0] * m_y[0] -c_a[1] * m_y[1];m_x[1] = m_x[0];m_x[0] = tmp;m_y[1] = m_y[0];m_y[0] = y[k];}

這段算法整體過程如下:

每個樣本:x[n] ─┬─? b0 ──┬────┐├─? b1 ─┼────┼──? y[n] = 輸出├─? b2 ─┘    ▼└─ y[n-1] ─ a0 ──┐y[n-2] ─ a1 ─┘

而結構體BiQuad中會保存反饋回路中的輸入輸出反饋x[2],y[2]

struct BiQuad {explicit BiQuad(const BiQuadCoefficients& coefficients): coefficients(coefficients), x(), y() {}explicit BiQuad(const CascadedBiQuadFilter::BiQuadParam& param);void Reset();BiQuadCoefficients coefficients;float x[2];float y[2];};

總結

webrtc中的高通濾波器是基于二階IIR濾波器進行設計的,但是對于二階IIR濾波器不僅僅會在高通濾波器中應用,在以后的webrtc算法介紹中CascadedBiQuadFilter將還會出現。

反正收藏也不會看,不如點個贊吧!

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

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

相關文章

《sklearn機器學習——聚類性能指數》同質性,完整性和 V-measure

函數&#xff1a;homogeneity_score 參數&#xff1a; labels_true: array-like, shape [n_samples] 樣本的真實標簽。 labels_pred: array-like, shape [n_samples] 樣本的預測標簽。返回值&#xff1a; h: float 同質性得分&#xff0c;在0到1之間&#xff0c;值越大表示聚…

HarmonyOS 應用開發新范式:深入剖析 Stage 模型與 ArkTS 狀態管理

好的&#xff0c;請看這篇關于 HarmonyOS 應用開發中 Stage 模型與 ArkTS 狀態管理的技術文章。 HarmonyOS 應用開發新范式&#xff1a;深入剖析 Stage 模型與 ArkTS 狀態管理 引言 隨著 HarmonyOS 4、5 的發布以及 API 12 的迭代&#xff0c;HarmonyOS 的應用開發范式已經全面…

一個Java的main方法在JVM中的執行流程

一個Java的main方法在JVM中的執行流程可以分為??四大階段??&#xff1a;??加載 -> 鏈接 -> 初始化 -> 執行??。// HelloWorld.java public class HelloWorld {public static void main(String[] args) {String message "Hello, JVM!";System.out.p…

聚焦診斷管理(DM)的傳輸層設計、診斷服務器實現、事件與通信管理、生命周期與報告五大核心模塊

聚焦診斷管理(DM)的傳輸層設計、診斷服務器實現、事件與通信管理、生命周期與報告五大核心模塊,明確 UDS(ISO 14229-1)與 SOVD(ASAM 服務化診斷)的功能邏輯、交互流程及規范性要求(SWS_DM 系列)。 1 UDS 傳輸層(UDS Transport Layer) 作為 DM 與診斷客戶端的 UDS …

關于npm的鉤子函數

一、npm scripts 的生命周期鉤子&#xff08;Lifecycle Scripts&#xff09; npm 提供了一些 ??特殊的 script 名稱??&#xff0c;它們是 ??生命周期鉤子??&#xff0c;會在特定時機 ??自動執行??。這些鉤子包括&#xff1a; 1.prepublishOnly(在 npm publish之前執…

167.在Vue3中使用OpenLayers模仿共享單車,判斷點是否放在規劃的電子圍欄內

一、前言大家好&#xff0c;這里分享一個 Vue3 OpenLayers 的小案例&#xff1a; 模仿共享單車的電子圍欄功能&#xff0c;用戶在地圖上繪制停泊點時&#xff0c;系統會自動判斷該點是否在規劃好的電子圍欄內&#xff08;多邊形或圓形&#xff09;。這個功能在實際項目中有很大…

鍵盤上面有F3,四,R,F,V,按下沒有反應,維修記錄

打開游戲&#xff0c;按了好幾遍F&#xff0c;結果都沒反應&#xff0c;但是左右上下行走是沒問題的。一臉懵逼&#xff1f;&#xff1f;&#xff1f;打開鍵盤測試網頁&#xff0c;發現有一列沒反應&#xff0c;F1不是&#xff0c;F1我定義了一個快捷鍵&#xff0c;跟測試沖突了…

8051單片機-成為點燈大師

第三章 成為點燈大師 1. 硬件設計 上一章說到&#xff0c;怎么點亮LED燈&#xff0c;很簡單啊&#xff0c;就是把P2口設置成低電平就行了。接下來讓我們更進一步&#xff0c;完成LED閃爍、流水燈實驗2. 軟件設計 2.1 LED閃爍實驗 為了使LED閃爍&#xff0c;我們自然而然的想到要…

Rust 日志庫完全指南:從入門到精通

GitHub 倉庫: https://github.com/zhouByte-hub/rust-study ? 如果這個項目對您有幫助&#xff0c;請給我一個 star&#xff01; 在 Rust 生態系統中&#xff0c;日志處理是一個至關重要的環節。無論是開發小型應用還是大型系統&#xff0c;良好的日志記錄都能幫助我們追蹤問題…

【科研繪圖系列】R語言繪制論文合集圖

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹 數據準備與過濾 統計分析 可視化繪圖 抗藥性分析 系統發育分析 加載R包 數據下載 Supp figure 1 Fig 1a Fig 1c Fig 1d Fig 1e Fig 1f Supp figure 3 Supp figure 4 Supp figure 5…

【c++】從三個類的設計看軟件架構的哲學思考

從三個類的設計看軟件架構的哲學思考 文章目錄從三個類的設計看軟件架構的哲學思考前言一、OP類&#xff1a;系統工程的安全守護者設計特點設計哲學適用場景現實類比二、VarReviser類&#xff1a;版本控制的嚴謹管理者設計特點設計哲學適用場景現實類比三、Model類&#xff1a;…

人工智能優化SEO關鍵詞的實戰策略

本文聚焦智能技術如何革新關鍵詞優化實踐&#xff0c;系統解析提升網站排名的核心路徑。重點探討語義分析如何精準匹配用戶意圖、長尾詞智能挖掘怎樣解鎖高潛力流量&#xff0c;并詳解工具篩選高轉化關鍵詞的五大實用策略。通過實戰案例說明技術如何突破流量增長瓶頸&#xff0…

【c++】c++第一課:命名空間

文章目錄1.C的第?個程序2.命名空間2.1 namespace的價值2.2 namespace的定義2.3 命名空間使?最新的c標準&#xff08;建議收藏&#xff09; 1.C的第?個程序 C兼容C語?絕?多數的語法&#xff0c;所以C語?實現的helloworld依舊可以運?&#xff0c;C中需要把定義?件代碼后…

版本發布流程手冊:Release分支規范與Bug分級標準全解析

在軟件交付日益高頻、用戶需求快速迭代的今天&#xff0c;版本發布流程的規范性直接決定了團隊的交付效率、產品質量和用戶滿意度。然而&#xff0c;許多團隊仍面臨以下痛點&#xff1a; 發布混亂&#xff1a;分支管理隨意&#xff0c;代碼沖突頻發&#xff1b;質量失控&#…

什么是CA根證書

CA 根證書&#xff08;Certificate Authority Root Certificate&#xff09;是 數字證書體系&#xff08;PKI&#xff0c;Public Key Infrastructure&#xff09; 中的核心證書。它有幾個關鍵點&#xff1a;1. 定義 CA&#xff08;Certificate Authority&#xff09;&#xff1…

git push -u origin main 這個-u起什么作用

git push -u origin main 里的 -u 等價于 --set-upstream&#xff0c;它的作用是&#xff1a;&#x1f449; 把本地分支 main 和遠程分支 origin/main 綁定&#xff08;建立追蹤關系&#xff09;。&#x1f539; 具體效果第一次推送分支時&#xff0c;如果加了 -u&#xff1a;本…

【Unity基礎】兩個關于UGUI中Text對非英文字體支持的問題

問題1&#xff1a;Unity中為什么UGUI中的Text(Textmeshpro&#xff09;默認不支持非英文字體&#xff0c;而legacy中的text卻可以呢&#xff1f; 在Unity中&#xff0c;TextMeshPro&#xff08;TMP&#xff09;默認不支持非英文字體&#xff0c;而Legacy Text支持&#xff0c;主…

碎片時間干活的好手(requestIdleCallback)

&#x1f7e2; What —— 它是什么&#xff1f; requestIdleCallback(callback[, options]) 是瀏覽器提供的一個 API&#xff0c;用來在主線程空閑時執行一些優先級不高的任務。 它的特點&#xff1a; 異步執行&#xff1a;不會打斷關鍵的渲染、交互、動畫。節省性能&#xff1…

第三方網站測評:【WEB應用文件包含漏洞(LFI/RFI)的測試步驟】

文件包含漏洞分為本地文件包含(LFI)和遠程文件包含(RFI)兩類。LFI允許讀取服務器本地文件,RFI可執行遠程服務器上的惡意代碼。PHP應用中include()、require()等函數未正確過濾用戶輸入時易產生此類漏洞。 檢測URL中可能包含文件的參數,常見特征如下: 參數名包含file、pa…

網絡爬蟲(web crawler)

文章目錄一、什么是網絡爬蟲二、爬蟲工作流程詳解第1步&#xff1a;起始點 - URL種子庫&#xff08;Seed URLs&#xff09;第2步&#xff1a;大腦 - 調度器&#xff08;Scheduler&#xff09;第3步&#xff1a;雙手 - 網頁下載器&#xff08;Downloader&#xff09;第4步&#…