chendeben пре 6 месеци
родитељ
комит
bf1394aa2f
23 измењених фајлова са 1918 додато и 33 уклоњено
  1. 88 0
      entry/src/main/ets/common/IjkPlayerListenerImpls.ets
  2. 6 0
      entry/src/main/ets/common/constants/EventConstants.ets
  3. 324 0
      entry/src/main/ets/common/util/EqualizerManager.ets
  4. 52 0
      entry/src/main/ets/pages/SettingPage.ets
  5. 646 0
      entry/src/main/ets/view/EqualizerView.ets
  6. 243 26
      entry/src/main/ets/view/LocalMusic.ets
  7. 8 0
      ijkplayer/src/main/cpp/ijkplayer/ff_ffplay_def.h
  8. 33 0
      ijkplayer/src/main/cpp/ijkplayer/ijkplayer_android.c
  9. 4 0
      ijkplayer/src/main/cpp/ijkplayer/ijkplayer_android.h
  10. 1 0
      ijkplayer/src/main/cpp/ijksdl/CMakeLists.txt
  11. 44 1
      ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_aout_android_opensles.c
  12. 225 0
      ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_equalizer.c
  13. 106 0
      ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_equalizer.h
  14. 14 0
      ijkplayer/src/main/cpp/ijksdl/ijksdl_aout.c
  15. 9 0
      ijkplayer/src/main/cpp/ijksdl/ijksdl_aout.h
  16. 57 0
      ijkplayer/src/main/cpp/napi/ijkplayer_napi.cpp
  17. 2 0
      ijkplayer/src/main/cpp/napi/ijkplayer_napi.h
  18. 3 1
      ijkplayer/src/main/cpp/napi/ijkplayer_napi_audio_init.cpp
  19. 23 0
      ijkplayer/src/main/cpp/proxy/ijkplayer_napi_proxy.cpp
  20. 2 0
      ijkplayer/src/main/cpp/proxy/ijkplayer_napi_proxy.h
  21. 7 5
      ijkplayer/src/main/cpp/types/ijkplayer_audio_napi/index.d.ts
  22. 19 0
      ijkplayer/src/main/ets/ijkplayer/IjkMediaPlayer.ets
  23. 2 0
      ijkplayer/src/main/ets/ijkplayer/utils/IjkPlayerNapi.d.ets

+ 88 - 0
entry/src/main/ets/common/IjkPlayerListenerImpls.ets

@@ -0,0 +1,88 @@
+import { OnVideoSizeChangedListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnVideoSizeChangedListener';
+import { OnPreparedListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnPreparedListener';
+import { OnCompletionListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnCompletionListener';
+import { OnBufferingUpdateListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnBufferingUpdateListener';
+import { OnErrorListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnErrorListener';
+import { OnInfoListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnInfoListener';
+import { OnSeekCompleteListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnSeekCompleteListener';
+import { OnTimedTextListener } from '../../../../../ijkplayer/src/main/ets/ijkplayer/callback/OnTimedTextListener';
+
+export class ImplOnVideoSizeChangedListener implements OnVideoSizeChangedListener {
+  private callback: (width: number, height: number, sar_num: number, sar_den: number) => void;
+  constructor(callback: (width: number, height: number, sar_num: number, sar_den: number) => void) {
+    this.callback = callback;
+  }
+  onVideoSizeChanged(width: number, height: number, sar_num: number, sar_den: number): void {
+    this.callback(width, height, sar_num, sar_den);
+  }
+}
+
+export class ImplOnPreparedListener implements OnPreparedListener {
+  private callback: () => void;
+  constructor(callback: () => void) {
+    this.callback = callback;
+  }
+  onPrepared(): void {
+    this.callback();
+  }
+}
+
+export class ImplOnCompletionListener implements OnCompletionListener {
+  private callback: () => void;
+  constructor(callback: () => void) {
+    this.callback = callback;
+  }
+  onCompletion(): void {
+    this.callback();
+  }
+}
+
+export class ImplOnBufferingUpdateListener implements OnBufferingUpdateListener {
+  private callback: (percent: number) => void;
+  constructor(callback: (percent: number) => void) {
+    this.callback = callback;
+  }
+  onBufferingUpdate(percent: number): void {
+    this.callback(percent);
+  }
+}
+
+export class ImplOnErrorListener implements OnErrorListener {
+  private callback: (what: number, extra: number) => void;
+  constructor(callback: (what: number, extra: number) => void) {
+    this.callback = callback;
+  }
+  onError(what: number, extra: number): void {
+    this.callback(what, extra);
+  }
+}
+
+export class ImplOnInfoListener implements OnInfoListener {
+  private callback: (what: number, extra: number) => void;
+  constructor(callback: (what: number, extra: number) => void) {
+    this.callback = callback;
+  }
+  onInfo(what: number, extra: number): void {
+    this.callback(what, extra);
+  }
+}
+
+export class ImplOnSeekCompleteListener implements OnSeekCompleteListener {
+  private callback: () => void;
+  constructor(callback: () => void) {
+    this.callback = callback;
+  }
+  onSeekComplete(): void {
+    this.callback();
+  }
+}
+
+export class ImplOnTimedTextListener implements OnTimedTextListener {
+  private callback: (text: string) => void;
+  constructor(callback: (text: string) => void) {
+    this.callback = callback;
+  }
+  onTimedText(text: string): void {
+    this.callback(text);
+  }
+}

+ 6 - 0
entry/src/main/ets/common/constants/EventConstants.ets

@@ -59,4 +59,10 @@ export class EventConstants {
    * WebDAV 元数据同步事件
    */
   static readonly EVENT_WEBDAV_METADATA_UPDATED: number = 3001;
+
+  /**
+   * 均衡器相关事件
+   */
+  // 均衡器设置变更事件
+  static readonly EVENT_EQUALIZER_CHANGED: number = 4001;
 }

+ 324 - 0
entry/src/main/ets/common/util/EqualizerManager.ets

@@ -0,0 +1,324 @@
+/**
+ * 均衡器管理器
+ * 管理均衡器预设、状态和 FFmpeg 过滤器生成
+ */
+import { PreferencesUtil } from '@pura/harmony-utils';
+import { IjkMediaPlayer } from '@ohos/ijkplayer';
+import Logger from './Logger';
+
+// 频段定义
+export interface EqualizerBand {
+  frequency: number;     // 中心频率 (Hz)
+  label: string;         // 显示标签 (如 "32Hz", "1kHz")
+  gain: number;          // 增益值 (-12 到 +12 dB)
+}
+
+// 预设定义
+export interface EqualizerPreset {
+  id: string;            // 预设唯一标识
+  name: string;          // 预设名称
+  gains: number[];       // 10 个频段的增益值
+  isCustom: boolean;     // 是否为自定义预设
+}
+
+export class EqualizerManager {
+  private static instance: EqualizerManager | null = null;
+  private static readonly TAG: string = 'heanup EqualizerManager';
+
+  // 持久化键名
+  private static readonly PREF_ENABLED: string = 'equalizer_enabled';
+  private static readonly PREF_PRESET_ID: string = 'equalizer_preset_id';
+  private static readonly PREF_CUSTOM_GAINS: string = 'equalizer_custom_gains';
+  private static readonly PREF_CUSTOM_PRESETS: string = 'equalizer_custom_presets';
+
+  // 10 频段定义
+  public static readonly BANDS: EqualizerBand[] = [
+    { frequency: 32, label: '32Hz', gain: 0 },
+    { frequency: 64, label: '64Hz', gain: 0 },
+    { frequency: 125, label: '125Hz', gain: 0 },
+    { frequency: 250, label: '250Hz', gain: 0 },
+    { frequency: 500, label: '500Hz', gain: 0 },
+    { frequency: 1000, label: '1kHz', gain: 0 },
+    { frequency: 2000, label: '2kHz', gain: 0 },
+    { frequency: 4000, label: '4kHz', gain: 0 },
+    { frequency: 8000, label: '8kHz', gain: 0 },
+    { frequency: 16000, label: '16kHz', gain: 0 }
+  ];
+
+  // 内置预设
+  public static readonly BUILT_IN_PRESETS: EqualizerPreset[] = [
+    { id: 'normal', name: '正常', gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], isCustom: false },
+    { id: 'pop', name: '流行', gains: [-1, 2, 4, 4, 2, 0, -1, -1, -1, -1], isCustom: false },
+    { id: 'rock', name: '摇滚', gains: [4, 3, 1, 0, -1, 0, 2, 3, 4, 4], isCustom: false },
+    { id: 'jazz', name: '爵士', gains: [3, 2, 1, 2, -2, -2, 0, 1, 2, 3], isCustom: false },
+    { id: 'classical', name: '古典', gains: [4, 3, 2, 1, -1, -1, 0, 2, 3, 4], isCustom: false },
+    { id: 'bass_boost', name: '低音增强', gains: [6, 5, 4, 2, 0, 0, 0, 0, 0, 0], isCustom: false },
+    { id: 'treble_boost', name: '高音增强', gains: [0, 0, 0, 0, 0, 1, 2, 4, 5, 6], isCustom: false },
+    { id: 'vocal', name: '人声增强', gains: [-2, -1, 0, 2, 4, 4, 2, 0, -1, -2], isCustom: false },
+    { id: 'dance', name: '舞曲', gains: [5, 4, 2, 0, 0, -2, -1, 2, 3, 4], isCustom: false },
+    { id: 'acoustic', name: '原声', gains: [3, 2, 1, 1, 2, 2, 2, 3, 2, 1], isCustom: false }
+  ];
+
+  // 当前状态
+  private _enabled: boolean = false;
+  private _currentPresetId: string = 'normal';
+  private _customGains: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+  private _customPresets: EqualizerPreset[] = [];
+
+  private constructor() {
+    this.loadState();
+  }
+
+  public static getInstance(): EqualizerManager {
+    if (EqualizerManager.instance === null) {
+      EqualizerManager.instance = new EqualizerManager();
+    }
+    return EqualizerManager.instance;
+  }
+
+  // 类型守卫:验证是否为有效的增益数组
+  private isValidGainsArray(value: object): boolean {
+    if (!Array.isArray(value)) {
+      return false;
+    }
+    if (value.length !== 10) {
+      return false;
+    }
+    for (let i = 0; i < value.length; i++) {
+      if (typeof value[i] !== 'number') {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // 类型守卫:验证是否为有效的预设数组
+  private isValidPresetArray(value: object): boolean {
+    if (!Array.isArray(value)) {
+      return false;
+    }
+    for (let i = 0; i < value.length; i++) {
+      const item = value[i] as Record<string, object>;
+      if (typeof item !== 'object' || item === null) {
+        return false;
+      }
+      if (typeof item['id'] !== 'string' ||
+          typeof item['name'] !== 'string' ||
+          !Array.isArray(item['gains']) ||
+          typeof item['isCustom'] !== 'boolean') {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // 加载持久化状态
+  private loadState(): void {
+    this._enabled = PreferencesUtil.getBooleanSync(EqualizerManager.PREF_ENABLED, false);
+    this._currentPresetId = PreferencesUtil.getStringSync(EqualizerManager.PREF_PRESET_ID, 'normal');
+
+    const gainsStr = PreferencesUtil.getStringSync(EqualizerManager.PREF_CUSTOM_GAINS, '');
+    if (gainsStr.length > 0) {
+      try {
+        const parsed: object = JSON.parse(gainsStr) as object;
+        if (this.isValidGainsArray(parsed)) {
+          this._customGains = parsed as number[];
+        }
+      } catch (e) {
+        Logger.error(EqualizerManager.TAG, `解析自定义增益失败: ${(e as Error).message}`);
+      }
+    }
+
+    const presetsStr = PreferencesUtil.getStringSync(EqualizerManager.PREF_CUSTOM_PRESETS, '');
+    if (presetsStr.length > 0) {
+      try {
+        const parsed: object = JSON.parse(presetsStr) as object;
+        if (this.isValidPresetArray(parsed)) {
+          this._customPresets = parsed as EqualizerPreset[];
+        }
+      } catch (e) {
+        Logger.error(EqualizerManager.TAG, `解析自定义预设失败: ${(e as Error).message}`);
+      }
+    }
+
+    Logger.info(EqualizerManager.TAG, `状态已加载: enabled=${this._enabled}, preset=${this._currentPresetId}`);
+  }
+
+  // 保存状态
+  private saveState(): void {
+    PreferencesUtil.putSync(EqualizerManager.PREF_ENABLED, this._enabled);
+    PreferencesUtil.putSync(EqualizerManager.PREF_PRESET_ID, this._currentPresetId);
+    PreferencesUtil.putSync(EqualizerManager.PREF_CUSTOM_GAINS, JSON.stringify(this._customGains));
+    PreferencesUtil.putSync(EqualizerManager.PREF_CUSTOM_PRESETS, JSON.stringify(this._customPresets));
+  }
+
+  // Getter/Setter
+  get enabled(): boolean {
+    return this._enabled;
+  }
+
+  set enabled(value: boolean) {
+    this._enabled = value;
+    this.saveState();
+  }
+
+  get currentPresetId(): string {
+    return this._currentPresetId;
+  }
+
+  get customGains(): number[] {
+    return this._customGains.slice();
+  }
+
+  // 获取所有预设(内置 + 自定义)
+  getAllPresets(): EqualizerPreset[] {
+    const result: EqualizerPreset[] = [];
+    for (let i = 0; i < EqualizerManager.BUILT_IN_PRESETS.length; i++) {
+      result.push(EqualizerManager.BUILT_IN_PRESETS[i]);
+    }
+    for (let i = 0; i < this._customPresets.length; i++) {
+      result.push(this._customPresets[i]);
+    }
+    return result;
+  }
+
+  // 获取当前预设
+  getCurrentPreset(): EqualizerPreset | null {
+    const allPresets = this.getAllPresets();
+    for (let i = 0; i < allPresets.length; i++) {
+      if (allPresets[i].id === this._currentPresetId) {
+        return allPresets[i];
+      }
+    }
+    return null;
+  }
+
+  // 获取当前增益值
+  getCurrentGains(): number[] {
+    if (this._currentPresetId === 'custom') {
+      return this._customGains.slice();
+    }
+    const preset = this.getCurrentPreset();
+    if (preset !== null) {
+      return preset.gains.slice();
+    }
+    return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+  }
+
+  // 设置预设
+  setPreset(presetId: string): void {
+    this._currentPresetId = presetId;
+    this.saveState();
+    Logger.info(EqualizerManager.TAG, `预设已更改: ${presetId}`);
+  }
+
+  // 设置自定义增益
+  setCustomGain(bandIndex: number, gain: number): void {
+    if (bandIndex < 0 || bandIndex >= 10) {
+      return;
+    }
+    // 限制增益范围 -12 到 +12
+    let clampedGain = gain;
+    if (clampedGain < -12) {
+      clampedGain = -12;
+    }
+    if (clampedGain > 12) {
+      clampedGain = 12;
+    }
+    this._customGains[bandIndex] = clampedGain;
+    this._currentPresetId = 'custom';
+    this.saveState();
+  }
+
+  // 设置所有自定义增益
+  setAllCustomGains(gains: number[]): void {
+    if (gains.length !== 10) {
+      return;
+    }
+    for (let i = 0; i < 10; i++) {
+      let g = gains[i];
+      if (g < -12) {
+        g = -12;
+      }
+      if (g > 12) {
+        g = 12;
+      }
+      this._customGains[i] = g;
+    }
+    this._currentPresetId = 'custom';
+    this.saveState();
+  }
+
+  // 重置为平坦
+  resetToFlat(): void {
+    this._customGains = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+    this._currentPresetId = 'normal';
+    this.saveState();
+  }
+
+  // 保存当前设置为新预设
+  saveAsPreset(name: string): EqualizerPreset {
+    const id = `custom_${Date.now()}`;
+    const currentGains = this.getCurrentGains();
+    const newPreset: EqualizerPreset = {
+      id: id,
+      name: name,
+      gains: currentGains,
+      isCustom: true
+    };
+    this._customPresets.push(newPreset);
+    this._currentPresetId = id;
+    this.saveState();
+    return newPreset;
+  }
+
+  // 删除自定义预设
+  deleteCustomPreset(presetId: string): boolean {
+    let index = -1;
+    for (let i = 0; i < this._customPresets.length; i++) {
+      if (this._customPresets[i].id === presetId) {
+        index = i;
+        break;
+      }
+    }
+    if (index >= 0) {
+      this._customPresets.splice(index, 1);
+      if (this._currentPresetId === presetId) {
+        this._currentPresetId = 'normal';
+      }
+      this.saveState();
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * 应用均衡器到播放器(OHAudioSuite 实时生效)
+   */
+  applyToPlayer(player: IjkMediaPlayer): void {
+    const gains = this.getCurrentGains();
+    player.setEqualizerEnabled(this._enabled);
+    player.setEqualizerGains(gains);
+    Logger.info(EqualizerManager.TAG, `均衡器已下发: enabled=${this._enabled}, gains=${JSON.stringify(gains)}`);
+  }
+
+  /**
+   * 获取预设的显示名称
+   */
+  getPresetDisplayName(presetId: string): string {
+    if (presetId === 'custom') {
+      return '自定义';
+    }
+    const preset = this.getCurrentPreset();
+    if (preset !== null && preset.id === presetId) {
+      return preset.name;
+    }
+    const allPresets = this.getAllPresets();
+    for (let i = 0; i < allPresets.length; i++) {
+      if (allPresets[i].id === presetId) {
+        return allPresets[i].name;
+      }
+    }
+    return '自定义';
+  }
+}

+ 52 - 0
entry/src/main/ets/pages/SettingPage.ets

@@ -1,4 +1,5 @@
 import TitleBar from '../view/TitleBar'
+import { EqualizerView } from '../view/EqualizerView'
 import { promptAction, router } from '@kit.ArkUI'
 import { CommonConstants } from '../common/constants/CommonConstants'
 import { EventConstants } from '../common/constants/EventConstants'
@@ -193,6 +194,7 @@ export struct SettingPage {
   @State showColorPicker: boolean = false
   @State customColor: string = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
   @State isThemeSheet: boolean = false // 主题设置弹窗显示状态
+  @State isEqualizerSheet: boolean = false // 均衡器设置弹窗显示状态
   @State colorRows: Array<Array<ThemeColorItem>> = chunkArray(SettingPage.THEME_COLOR_LIST, 2);
   @State colorGroup: string = "group"
   @StorageProp('topRectHeight') topRectHeight: number = 0;
@@ -590,6 +592,19 @@ export struct SettingPage {
     .height(300)
   }
 
+  @Builder
+  equalizerSheetBuilder() {
+    Scroll() {
+      Column() {
+        EqualizerView()
+          .margin({ top: 8 })
+      }
+      .padding({ left: 12, right: 12, bottom: 20 })
+    }
+    .width('100%')
+    .height('100%')
+  }
+
   @Builder
   themeSheetBuilder() {
     Column() {
@@ -1605,6 +1620,43 @@ export struct SettingPage {
             .clickEffect({ level: ClickEffectLevel.HEAVY })
             Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
 
+            // 均衡器设置入口
+            Button({ type: ButtonType.Normal, stateEffect: true }) {
+              Row() {
+                SymbolGlyph($r('sys.symbol.waveform'))
+                  .fontSize(20)
+                  .fontColor([this.themeColor])
+                  .alignSelf(ItemAlign.Center)
+                  .margin({ left: 15 })
+                Text('均衡器')
+                  .margin({ left: 8 })
+                  .fontSize(15)
+                  .fontColor(Color.Gray)
+                  .fontWeight(480)
+                  .layoutWeight(1)
+                Image($r('app.media.arrow_right'))
+                  .width(22)
+                  .height(22)
+                  .margin({ right: 18 })
+              }
+            }
+            .backgroundColor(Color.Transparent)
+            .height(55)
+            .clickEffect({ level: ClickEffectLevel.HEAVY })
+            .onClick(() => {
+              this.isEqualizerSheet = true;
+            })
+            .bindSheet($$this.isEqualizerSheet, this.equalizerSheetBuilder(), {
+              height: '70%',
+              dragBar: true,
+              preferType: this.currentBreakpoint !== BreakpointTypeEnum.SM ? SheetType.CENTER : SheetType.BOTTOM,
+              showClose: true,
+              blurStyle: BlurStyle.Thin,
+              backgroundColor: Color.Transparent,
+              title: { title: '均衡器设置' }
+            })
+            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+
             // 长按默认倍数
             Row() {
               SymbolGlyph($r('sys.symbol.timer'))

+ 646 - 0
entry/src/main/ets/view/EqualizerView.ets

@@ -0,0 +1,646 @@
+/**
+ * 均衡器视图组件
+ * 提供均衡器设置的 UI 界面
+ */
+import { ToastUtil } from '@pura/harmony-utils';
+import { emitter } from '@kit.BasicServicesKit';
+import { EqualizerManager, EqualizerBand, EqualizerPreset } from '../common/util/EqualizerManager';
+import { EventConstants } from '../common/constants/EventConstants';
+import { AnimationHelper, DialogAction, DialogHelper } from '@pura/harmony-dialog';
+import Logger from '../common/util/Logger';
+import { CommonConstants } from '../common/constants/CommonConstants';
+
+@Component
+export struct EqualizerView {
+  @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
+  @StorageProp('isDarkMode') isDarkMode: boolean = false;
+
+  @State private equalizerEnabled: boolean = false;
+  @State private currentPresetId: string = 'normal';
+  @State private gains: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+  @State private presets: EqualizerPreset[] = [];
+  @State private selectedPresetIndex: number = 0;
+
+  private equalizerManager: EqualizerManager = EqualizerManager.getInstance();
+  private bands: EqualizerBand[] = EqualizerManager.BANDS;
+  private static readonly TAG: string = 'heanup EqualizerView';
+
+  aboutToAppear(): void {
+    this.loadState();
+  }
+
+  private loadState(): void {
+    this.equalizerEnabled = this.equalizerManager.enabled;
+    this.currentPresetId = this.equalizerManager.currentPresetId;
+    this.gains = this.equalizerManager.getCurrentGains();
+    this.presets = this.equalizerManager.getAllPresets();
+    this.updateSelectedPresetIndex();
+  }
+
+  private updateSelectedPresetIndex(): void {
+    for (let i = 0; i < this.presets.length; i++) {
+      if (this.presets[i].id === this.currentPresetId) {
+        this.selectedPresetIndex = i;
+        return;
+      }
+    }
+    // 如果是自定义,设为 -1
+    if (this.currentPresetId === 'custom') {
+      this.selectedPresetIndex = -1;
+    } else {
+      this.selectedPresetIndex = 0;
+    }
+  }
+
+  private notifyEqualizerChanged(): void {
+    // 发送均衡器变更事件
+    const eventData: emitter.EventData = {
+      data: {
+        enabled: this.equalizerEnabled,
+        presetId: this.currentPresetId
+      }
+    };
+    emitter.emit({ eventId: EventConstants.EVENT_EQUALIZER_CHANGED }, eventData);
+  }
+
+  private onEnabledChange(enabled: boolean): void {
+    this.equalizerEnabled = enabled;
+    this.equalizerManager.enabled = enabled;
+    this.notifyEqualizerChanged();
+    Logger.info(EqualizerView.TAG, `均衡器 ${enabled ? '已启用' : '已禁用'}`);
+  }
+
+  private onPresetSelect(index: number): void {
+    if (index >= 0 && index < this.presets.length) {
+      const preset = this.presets[index];
+      this.currentPresetId = preset.id;
+      this.selectedPresetIndex = index;
+      this.equalizerManager.setPreset(preset.id);
+      this.gains = this.equalizerManager.getCurrentGains();
+      this.notifyEqualizerChanged();
+    }
+  }
+
+  private onGainChange(bandIndex: number, gain: number): void {
+    const newGains = this.gains.slice();
+    newGains[bandIndex] = gain;
+    this.gains = newGains;
+    this.equalizerManager.setCustomGain(bandIndex, gain);
+    this.currentPresetId = 'custom';
+    this.selectedPresetIndex = -1;
+    this.notifyEqualizerChanged();
+  }
+
+  private onResetClick(): void {
+    this.equalizerManager.resetToFlat();
+    this.loadState();
+    this.notifyEqualizerChanged();
+    ToastUtil.showToast('已重置为平坦');
+  }
+
+  private onSavePresetClick(): void {
+    const that = this;
+    // 显示保存预设对话框
+    DialogHelper.showTextInputDialog({
+      title: '保存预设',
+      text: '',
+      maskColor: Color.Transparent,
+      transition: AnimationHelper.transitionInDown(300),
+      onAction: (action: DialogAction, dialogId: string, content: string) => {
+        if (action === DialogAction.TWO) {
+          const name = content.trim();
+          if (name.length > 0) {
+            that.equalizerManager.saveAsPreset(name);
+            that.presets = that.equalizerManager.getAllPresets();
+            that.currentPresetId = that.equalizerManager.currentPresetId;
+            that.updateSelectedPresetIndex();
+            ToastUtil.showToast('预设已保存');
+          } else {
+            ToastUtil.showToast('请输入预设名称');
+          }
+        }
+        DialogHelper.closeDialog(dialogId);
+      }
+    });
+  }
+
+  private getPresetSelectOptions(): SelectOption[] {
+    const options: SelectOption[] = [];
+    for (let i = 0; i < this.presets.length; i++) {
+      options.push({ value: this.presets[i].name });
+    }
+    return options;
+  }
+
+  private getCurrentPresetName(): string {
+    if (this.currentPresetId === 'custom') {
+      return '自定义';
+    }
+    for (let i = 0; i < this.presets.length; i++) {
+      if (this.presets[i].id === this.currentPresetId) {
+        return this.presets[i].name;
+      }
+    }
+    return '正常';
+  }
+
+  @Builder
+  private PresetSelector(): void {
+    Row() {
+      Text('预设')
+        .fontSize(15)
+        .fontColor($r('app.color.text_color'))
+        .fontWeight(500)
+      Blank()
+      Select(this.getPresetSelectOptions())
+        .selected(this.selectedPresetIndex >= 0 ? this.selectedPresetIndex : 0)
+        .value(this.getCurrentPresetName())
+        .font({ size: 14 })
+        .fontColor(this.themeColor)
+        .selectedOptionFont({ size: 14, weight: FontWeight.Medium })
+        .selectedOptionFontColor(this.themeColor)
+        .onSelect((index: number) => {
+          this.onPresetSelect(index);
+        })
+    }
+    .width('100%')
+    .padding({ left: 16, right: 16, top: 12, bottom: 12 })
+  }
+
+  @Builder
+  private BandSlider(band: EqualizerBand, index: number): void {
+    Column() {
+      // 增益值显示
+      Text(`${this.gains[index] > 0 ? '+' : ''}${this.gains[index]}`)
+        .fontSize(11)
+        .fontColor(this.themeColor)
+        .fontWeight(500)
+        .margin({ bottom: 4 })
+
+      // 垂直滑块
+      Column() {
+        Slider({
+          value: this.gains[index],
+          min: -12,
+          max: 12,
+          step: 1,
+          style: SliderStyle.OutSet,
+          direction: Axis.Vertical,
+          reverse: true
+        })
+          .width(24)
+          .height(120)
+          .selectedColor(this.themeColor)
+          .trackColor(this.isDarkMode ? '#333333' : '#E0E0E0')
+          .blockColor(this.themeColor)
+          .trackThickness(4)
+          .blockSize({ width: 16, height: 16 })
+          .onChange((value: number) => {
+            this.onGainChange(index, Math.round(value));
+          })
+      }
+      .height(130)
+
+      // 频率标签
+      Text(band.label)
+        .fontSize(9)
+        .fontColor(Color.Gray)
+        .margin({ top: 4 })
+    }
+    .alignItems(HorizontalAlign.Center)
+    .width('10%')
+  }
+
+  @Builder
+  private EqualizerSliders(): void {
+    Column() {
+      // 刻度标签
+      Row() {
+        Text('+12')
+          .fontSize(10)
+          .fontColor(Color.Gray)
+        Blank()
+        Text('0')
+          .fontSize(10)
+          .fontColor(Color.Gray)
+        Blank()
+        Text('-12')
+          .fontSize(10)
+          .fontColor(Color.Gray)
+      }
+      .width('100%')
+      .padding({ left: 8, right: 8 })
+      .justifyContent(FlexAlign.SpaceBetween)
+
+      // 滑块区域
+      Row() {
+        ForEach(this.bands, (band: EqualizerBand, index: number) => {
+          this.BandSlider(band, index)
+        })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .padding({ top: 8, bottom: 8 })
+    }
+    .width('100%')
+    .padding(12)
+  }
+
+  @Builder
+  private ActionButtons(): void {
+    Row() {
+      Button('重置')
+        .fontSize(14)
+        .fontColor(this.themeColor)
+        .backgroundColor(Color.Transparent)
+        .border({ width: 1, color: this.themeColor, radius: 20 })
+        .height(36)
+        .padding({ left: 24, right: 24 })
+        .onClick(() => this.onResetClick())
+
+      Blank().width(20)
+
+      Button('保存预设')
+        .fontSize(14)
+        .fontColor(Color.White)
+        .backgroundColor(this.themeColor)
+        .borderRadius(20)
+        .height(36)
+        .padding({ left: 24, right: 24 })
+        .onClick(() => this.onSavePresetClick())
+    }
+    .width('100%')
+    .justifyContent(FlexAlign.Center)
+    .padding({ top: 16, bottom: 20 })
+  }
+
+  build() {
+    Column() {
+      // 启用开关
+      Row() {
+        SymbolGlyph($r('sys.symbol.waveform'))
+          .fontSize(22)
+          .fontColor([this.themeColor])
+        Text('均衡器')
+          .fontSize(16)
+          .fontWeight(500)
+          .fontColor($r('app.color.text_color'))
+          .margin({ left: 10 })
+        Blank()
+        Toggle({ type: ToggleType.Switch, isOn: this.equalizerEnabled })
+          .selectedColor(this.themeColor)
+          .switchPointColor(Color.White)
+          .onChange((isOn: boolean) => this.onEnabledChange(isOn))
+          .width(50)
+          .height(28)
+      }
+      .width('100%')
+      .padding({ left: 16, right: 16, top: 16, bottom: 8 })
+
+      // 内容区域(仅在启用时显示)
+      if (this.equalizerEnabled) {
+        Line()
+          .width('90%')
+          .height(0.5)
+          .backgroundColor($r('app.color.divider_color'))
+
+        this.PresetSelector()
+
+        Line()
+          .width('90%')
+          .height(0.5)
+          .backgroundColor($r('app.color.divider_color'))
+
+        this.EqualizerSliders()
+
+        this.ActionButtons()
+      } else {
+        // 禁用时显示提示
+        Text('开启均衡器后可调节音效')
+          .fontSize(13)
+          .fontColor(Color.Gray)
+          .margin({ top: 12, bottom: 20 })
+      }
+    }
+    .width('100%')
+    .backgroundColor($r('app.color.settings_background_main'))
+    .borderRadius(16)
+  }
+}
+
+/**
+ * 带回调的均衡器视图组件
+ * 用于在播放页面实时应用均衡器效果
+ */
+@Component
+export struct EqualizerViewWithCallback {
+  @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
+  @StorageProp('isDarkMode') isDarkMode: boolean = false;
+
+  @State private equalizerEnabled: boolean = false;
+  @State private currentPresetId: string = 'normal';
+  @State private gains: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+  @State private presets: EqualizerPreset[] = [];
+  @State private selectedPresetIndex: number = 0;
+
+  // 回调函数
+  onEqualizerChanged: () => void = () => {};
+
+  private equalizerManager: EqualizerManager = EqualizerManager.getInstance();
+  private bands: EqualizerBand[] = EqualizerManager.BANDS;
+  private static readonly TAG: string = 'heanup EqualizerViewWithCallback';
+
+  aboutToAppear(): void {
+    this.loadState();
+  }
+
+  private loadState(): void {
+    this.equalizerEnabled = this.equalizerManager.enabled;
+    this.currentPresetId = this.equalizerManager.currentPresetId;
+    this.gains = this.equalizerManager.getCurrentGains();
+    this.presets = this.equalizerManager.getAllPresets();
+    this.updateSelectedPresetIndex();
+  }
+
+  private updateSelectedPresetIndex(): void {
+    for (let i = 0; i < this.presets.length; i++) {
+      if (this.presets[i].id === this.currentPresetId) {
+        this.selectedPresetIndex = i;
+        return;
+      }
+    }
+    if (this.currentPresetId === 'custom') {
+      this.selectedPresetIndex = -1;
+    } else {
+      this.selectedPresetIndex = 0;
+    }
+  }
+
+  private notifyChange(): void {
+    // 发送事件
+    const eventData: emitter.EventData = {
+      data: {
+        enabled: this.equalizerEnabled,
+        presetId: this.currentPresetId
+      }
+    };
+    emitter.emit({ eventId: EventConstants.EVENT_EQUALIZER_CHANGED }, eventData);
+    // 调用回调
+    this.onEqualizerChanged();
+  }
+
+  private onEnabledChange(enabled: boolean): void {
+    this.equalizerEnabled = enabled;
+    this.equalizerManager.enabled = enabled;
+    this.notifyChange();
+    Logger.info(EqualizerViewWithCallback.TAG, `均衡器 ${enabled ? '已启用' : '已禁用'}`);
+  }
+
+  private onPresetSelect(index: number): void {
+    if (index >= 0 && index < this.presets.length) {
+      const preset = this.presets[index];
+      this.currentPresetId = preset.id;
+      this.selectedPresetIndex = index;
+      this.equalizerManager.setPreset(preset.id);
+      this.gains = this.equalizerManager.getCurrentGains();
+      this.notifyChange();
+    }
+  }
+
+  private onGainChange(bandIndex: number, gain: number): void {
+    const newGains = this.gains.slice();
+    newGains[bandIndex] = gain;
+    this.gains = newGains;
+    this.equalizerManager.setCustomGain(bandIndex, gain);
+    this.currentPresetId = 'custom';
+    this.selectedPresetIndex = -1;
+    this.notifyChange();
+  }
+
+  private onResetClick(): void {
+    this.equalizerManager.resetToFlat();
+    this.loadState();
+    this.notifyChange();
+    ToastUtil.showToast('已重置为平坦');
+  }
+
+  private onSavePresetClick(): void {
+    const that = this;
+    DialogHelper.showTextInputDialog({
+      title: '保存预设',
+      text: '',
+      maskColor: Color.Transparent,
+      transition: AnimationHelper.transitionInDown(300),
+      onAction: (action: DialogAction, dialogId: string, content: string) => {
+        if (action === DialogAction.TWO) {
+          const name = content.trim();
+          if (name.length > 0) {
+            that.equalizerManager.saveAsPreset(name);
+            that.presets = that.equalizerManager.getAllPresets();
+            that.currentPresetId = that.equalizerManager.currentPresetId;
+            that.updateSelectedPresetIndex();
+            ToastUtil.showToast('预设已保存');
+          } else {
+            ToastUtil.showToast('请输入预设名称');
+          }
+        }
+        DialogHelper.closeDialog(dialogId);
+      }
+    });
+  }
+
+  private getPresetSelectOptions(): SelectOption[] {
+    const options: SelectOption[] = [];
+    for (let i = 0; i < this.presets.length; i++) {
+      options.push({ value: this.presets[i].name });
+    }
+    return options;
+  }
+
+  private getCurrentPresetName(): string {
+    if (this.currentPresetId === 'custom') {
+      return '自定义';
+    }
+    for (let i = 0; i < this.presets.length; i++) {
+      if (this.presets[i].id === this.currentPresetId) {
+        return this.presets[i].name;
+      }
+    }
+    return '正常';
+  }
+
+  @Builder
+  private PresetSelector(): void {
+    Row() {
+      Text('预设')
+        .fontSize(15)
+        .fontColor($r('app.color.text_color'))
+        .fontWeight(500)
+      Blank()
+      Select(this.getPresetSelectOptions())
+        .selected(this.selectedPresetIndex >= 0 ? this.selectedPresetIndex : 0)
+        .value(this.getCurrentPresetName())
+        .font({ size: 14 })
+        .fontColor(this.themeColor)
+        .selectedOptionFont({ size: 14, weight: FontWeight.Medium })
+        .selectedOptionFontColor(this.themeColor)
+        .onSelect((index: number) => {
+          this.onPresetSelect(index);
+        })
+    }
+    .width('100%')
+    .padding({ left: 16, right: 16, top: 12, bottom: 12 })
+  }
+
+  @Builder
+  private BandSlider(band: EqualizerBand, index: number): void {
+    Column() {
+      Text(`${this.gains[index] > 0 ? '+' : ''}${this.gains[index]}`)
+        .fontSize(11)
+        .fontColor(this.themeColor)
+        .fontWeight(500)
+        .margin({ bottom: 4 })
+
+      Column() {
+        Slider({
+          value: this.gains[index],
+          min: -12,
+          max: 12,
+          step: 1,
+          style: SliderStyle.OutSet,
+          direction: Axis.Vertical,
+          reverse: true
+        })
+          .width(24)
+          .height(120)
+          .selectedColor(this.themeColor)
+          .trackColor(this.isDarkMode ? '#333333' : '#E0E0E0')
+          .blockColor(this.themeColor)
+          .trackThickness(4)
+          .blockSize({ width: 16, height: 16 })
+          .onChange((value: number) => {
+            this.onGainChange(index, Math.round(value));
+          })
+      }
+      .height(130)
+
+      Text(band.label)
+        .fontSize(9)
+        .fontColor(Color.Gray)
+        .margin({ top: 4 })
+    }
+    .alignItems(HorizontalAlign.Center)
+    .width('10%')
+  }
+
+  @Builder
+  private EqualizerSliders(): void {
+    Column() {
+      Row() {
+        Text('+12')
+          .fontSize(10)
+          .fontColor(Color.Gray)
+        Blank()
+        Text('0')
+          .fontSize(10)
+          .fontColor(Color.Gray)
+        Blank()
+        Text('-12')
+          .fontSize(10)
+          .fontColor(Color.Gray)
+      }
+      .width('100%')
+      .padding({ left: 8, right: 8 })
+      .justifyContent(FlexAlign.SpaceBetween)
+
+      Row() {
+        ForEach(this.bands, (band: EqualizerBand, index: number) => {
+          this.BandSlider(band, index)
+        })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .padding({ top: 8, bottom: 8 })
+    }
+    .width('100%')
+    .padding(12)
+  }
+
+  @Builder
+  private ActionButtons(): void {
+    Row() {
+      Button('重置')
+        .fontSize(14)
+        .fontColor(this.themeColor)
+        .backgroundColor(Color.Transparent)
+        .border({ width: 1, color: this.themeColor, radius: 20 })
+        .height(36)
+        .padding({ left: 24, right: 24 })
+        .onClick(() => this.onResetClick())
+
+      Blank().width(20)
+
+      Button('保存预设')
+        .fontSize(14)
+        .fontColor(Color.White)
+        .backgroundColor(this.themeColor)
+        .borderRadius(20)
+        .height(36)
+        .padding({ left: 24, right: 24 })
+        .onClick(() => this.onSavePresetClick())
+    }
+    .width('100%')
+    .justifyContent(FlexAlign.Center)
+    .padding({ top: 16, bottom: 20 })
+  }
+
+  build() {
+    Column() {
+      Row() {
+        SymbolGlyph($r('sys.symbol.waveform'))
+          .fontSize(22)
+          .fontColor([this.themeColor])
+        Text('均衡器')
+          .fontSize(16)
+          .fontWeight(500)
+          .fontColor($r('app.color.text_color'))
+          .margin({ left: 10 })
+        Blank()
+        Toggle({ type: ToggleType.Switch, isOn: this.equalizerEnabled })
+          .selectedColor(this.themeColor)
+          .switchPointColor(Color.White)
+          .onChange((isOn: boolean) => this.onEnabledChange(isOn))
+          .width(50)
+          .height(28)
+      }
+      .width('100%')
+      .padding({ left: 16, right: 16, top: 16, bottom: 8 })
+
+      if (this.equalizerEnabled) {
+        Line()
+          .width('90%')
+          .height(0.5)
+          .backgroundColor($r('app.color.divider_color'))
+
+        this.PresetSelector()
+
+        Line()
+          .width('90%')
+          .height(0.5)
+          .backgroundColor($r('app.color.divider_color'))
+
+        this.EqualizerSliders()
+
+        this.ActionButtons()
+      } else {
+        Text('开启均衡器后可调节音效')
+          .fontSize(13)
+          .fontColor(Color.Gray)
+          .margin({ top: 12, bottom: 20 })
+      }
+    }
+    .width('100%')
+    .backgroundColor($r('app.color.settings_background_main'))
+    .borderRadius(16)
+  }
+}

+ 243 - 26
entry/src/main/ets/view/LocalMusic.ets

@@ -4,6 +4,16 @@ import { VideoItem } from '../viewmodel/VideoItem';
 import {  LengthMetrics, SegmentButton,SegmentButtonOptions } from '@kit.ArkUI';
 import { getWebdavVideoItems, getWebdavCurrentPlayIndex } from '../pages/WebDavMainPage';
 import { getNavidromeVideoItems, getNavidromeCurrentPlayIndex } from '../common/util/NavidromePlaylistStore';
+import {
+  ImplOnBufferingUpdateListener,
+  ImplOnCompletionListener,
+  ImplOnErrorListener,
+  ImplOnInfoListener,
+  ImplOnPreparedListener,
+  ImplOnSeekCompleteListener,
+  ImplOnTimedTextListener,
+  ImplOnVideoSizeChangedListener
+} from '../common/IjkPlayerListenerImpls';
 import {
   ButtonFancyModifier,
   ImageFancyModifier,
@@ -48,6 +58,8 @@ import { effectKit } from '@kit.ArkGraphics2D';
 import { ColorConversion } from '../common/util/ColorConversion';
 import { Lyric, LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
 import { AvSessionController } from '../controller/AvSessionController';
+import { EqualizerManager } from '../common/util/EqualizerManager';
+import { EqualizerViewWithCallback } from './EqualizerView';
 import { CastController, CastControllerCallbacks } from '../controller/CastController';
 import { repairAudioMetadata, convertDsfToWav, getApiLyric,changeMusicCover} from '../common/util/MusicTagUtils';
 import { extractHwMediaMetadata, FFMpegTags,Utility } from '../common/util/Utility';
@@ -810,6 +822,7 @@ export struct LocalMusic {
       PreferencesUtil.putSync('isFirstTiped',false)
     }
     this.initSetting()
+    this.syncEqualizerUi();
     this.UNKONWN = Utility.resourceToString(this.context, $r('app.string.unknown'));
     Utility.getAppName(getContext(this)).then((appName: string) => {
       this.appName = appName
@@ -850,6 +863,16 @@ export struct LocalMusic {
       this.doUpdateData()
     });
 
+    let eventEqualizerChanged: emitter.InnerEvent = { eventId: EventConstants.EVENT_EQUALIZER_CHANGED }
+    emitter.on(eventEqualizerChanged, () => {
+      this.syncEqualizerUi();
+      if (this.mIjkMediaPlayer) {
+        const equalizerManager = EqualizerManager.getInstance();
+        equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+        this.markEqualizerApplied();
+      }
+    });
+
     // 监听歌单播放请求事件
     let eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }
     emitter.on(eventPlaylistPlay, async (eventData: emitter.EventData) => {
@@ -1872,6 +1895,7 @@ export struct LocalMusic {
     emitter.off(EventConstants.EVENT_SWIPE_BACK_UPDATE);
     emitter.off(EventConstants.EVENT_SETTING_UPDATE);
     emitter.off(EventConstants.EVENT_WEBDAV_METADATA_UPDATED);
+    emitter.off(EventConstants.EVENT_EQUALIZER_CHANGED);
     this.mDestroyPage = true;
     this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
     if (this.CONTROL_PlayStatus != PlayStatus.INIT) {
@@ -3162,6 +3186,15 @@ export struct LocalMusic {
             backgroundColor:Color.Transparent,
             title: { title: 'AB循环' }
           })
+          .bindSheet($$this.isEqualizerSheet, this.EqualizerSheet(), {
+            height: this.isLandscape ? '90%' : '65%',
+            dragBar: true,
+            showClose: true,
+            preferType: this.currentBreakpoint !== BreakpointTypeEnum.SM ? SheetType.CENTER : SheetType.BOTTOM,
+            blurStyle: BlurStyle.Thin,
+            backgroundColor: Color.Transparent,
+            title: { title: '均衡器' }
+          })
 
           Stack() {
 
@@ -10683,6 +10716,9 @@ export struct LocalMusic {
   @Builder
   BottomControl() {
     Column() {
+      if (!this.isPhoneLan() && !this.curDisplayIsHiCar) {
+        this.EqualizerQuickBar()
+      }
       this.TopPlayProgressView()
       //播放 暂停,下一首,上一首
       this.playCenterView()
@@ -10698,6 +10734,67 @@ export struct LocalMusic {
 
   }
 
+  @Builder
+  private EqualizerQuickBar(): void {
+    Row() {
+      Row() {
+        SymbolGlyph($r('sys.symbol.waveform'))
+          .fontSize(16)
+          .fontColor([this.themeColor])
+        Text('均衡器')
+          .fontSize(12)
+          .fontColor(Color.White)
+          .margin({ left: 6 })
+      }
+      .onClick(() => {
+        this.isEqualizerSheet = true;
+      })
+
+      Blank()
+
+      Toggle({ type: ToggleType.Switch, isOn: this.eqCurrentEnabled })
+        .selectedColor(this.themeColor)
+        .switchPointColor(Color.White)
+        .width(42)
+        .height(22)
+        .onChange((isOn: boolean) => {
+          const equalizerManager = EqualizerManager.getInstance();
+          equalizerManager.enabled = isOn;
+          this.syncEqualizerUi();
+          if (this.mIjkMediaPlayer) {
+            equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+            this.markEqualizerApplied();
+          }
+        })
+
+      Text(this.eqCurrentPresetName)
+        .fontSize(10)
+        .fontColor(Color.White)
+        .margin({ left: 8 })
+
+      Text(this.eqAppliedAt > 0 ? '已生效' : '未应用')
+        .fontSize(10)
+        .fontColor(this.eqAppliedAt > 0 ? this.themeColor : Color.White)
+        .margin({ left: 8 })
+
+      Button('应用')
+        .fontSize(11)
+        .fontColor(Color.White)
+        .backgroundColor(this.eqAppliedAt > 0 ? this.themeColor : '#666666')
+        .height(24)
+        .padding({ left: 10, right: 10 })
+        .borderRadius(12)
+        .enabled(!this.eqApplying && this.CONTROL_PlayStatus !== PlayStatus.INIT)
+        .onClick(() => {
+          this.applyEqualizerToCurrentPlayback();
+        })
+    }
+    .width(this.isLandscape ? '88%' : '95%')
+    .padding({ left: 12, right: 12, top: 6, bottom: 6 })
+    .backgroundColor('rgba(0,0,0,0.32)')
+    .borderRadius(12)
+  }
+
   @Builder
   playMenuBuilder(){
     Image($r('app.media.menu'))
@@ -12875,6 +12972,8 @@ export struct LocalMusic {
 
     { id: 9, image: $r('app.media.volume_white'), title: '调整音量' },
 
+    { id: 24, image: $r('app.media.volume_white'), title: '均衡器' },
+
     // { id: 6, image: $r('app.media.memory'), title: '记忆播放' },
 
 
@@ -12978,6 +13077,9 @@ export struct LocalMusic {
       case 20://AB循环
         this.isABSheet =  !this.isABSheet
         break;
+      case 24://均衡器
+        this.isEqualizerSheet = !this.isEqualizerSheet
+        break;
     }
 
   }
@@ -12988,6 +13090,11 @@ export struct LocalMusic {
   @State jumpATime:number = 0
   @State jumpBTime:number  = 0
   @State isABSheet:boolean = false
+  @State isEqualizerSheet: boolean = false
+  @State eqApplying: boolean = false
+  @State eqAppliedAt: number = 0
+  @State eqCurrentPresetName: string = '正常'
+  @State eqCurrentEnabled: boolean = false
 
   @Builder
   ABLoopSheet() {
@@ -13026,6 +13133,105 @@ export struct LocalMusic {
 
   }
 
+  private syncEqualizerUi(): void {
+    const equalizerManager = EqualizerManager.getInstance();
+    this.eqCurrentEnabled = equalizerManager.enabled;
+    this.eqCurrentPresetName = equalizerManager.getPresetDisplayName(equalizerManager.currentPresetId);
+    if (this.CONTROL_PlayStatus === PlayStatus.INIT) {
+      this.eqAppliedAt = 0;
+    }
+  }
+
+  private markEqualizerApplied(): void {
+    const equalizerManager = EqualizerManager.getInstance();
+    this.eqCurrentEnabled = equalizerManager.enabled;
+    this.eqCurrentPresetName = equalizerManager.getPresetDisplayName(equalizerManager.currentPresetId);
+    this.eqAppliedAt = Date.now();
+    this.eqApplying = false;
+  }
+
+  private applyEqualizerToCurrentPlayback(): void {
+    if (this.eqApplying) {
+      return;
+    }
+    if (this.castControllerWrapper && this.isCastPlaying) {
+      ToastUtil.showToast('投播模式暂不支持实时均衡器');
+      return;
+    }
+    if (!this.currentSong || !this.mIjkMediaPlayer) {
+      ToastUtil.showToast('暂无可应用的播放');
+      return;
+    }
+    this.eqApplying = true;
+    try {
+      const equalizerManager = EqualizerManager.getInstance();
+      equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+      this.markEqualizerApplied();
+      ToastUtil.showToast('均衡器已实时生效');
+    } catch (error) {
+      this.eqApplying = false;
+      Logger.warn(TAG, `heanup 应用均衡器失败: ${(error as Error).message}`);
+    }
+  }
+
+  @Builder
+  private EqualizerApplyStatusBar(): void {
+    Row() {
+      Column() {
+        Text(`均衡器:${this.eqCurrentEnabled ? '开' : '关'} · ${this.eqCurrentPresetName}`)
+          .fontSize(12)
+          .fontColor($r('app.color.text_color'))
+        Text(this.eqAppliedAt > 0 ? '当前播放:已生效' : '当前播放:未应用')
+          .fontSize(10)
+          .fontColor(this.eqAppliedAt > 0 ? this.themeColor : Color.Gray)
+      }
+      .alignItems(HorizontalAlign.Start)
+
+      Blank()
+
+      Button('重新应用')
+        .fontSize(12)
+        .fontColor(Color.White)
+        .backgroundColor('#666666')
+        .height(28)
+        .padding({ left: 14, right: 14 })
+        .borderRadius(14)
+        .enabled(!this.eqApplying && this.CONTROL_PlayStatus !== PlayStatus.INIT)
+        .onClick(() => {
+          this.applyEqualizerToCurrentPlayback();
+        })
+    }
+    .width('100%')
+    .padding({ left: 12, right: 12, top: 8, bottom: 8 })
+    .backgroundColor($r('app.color.settings_background_main'))
+    .borderRadius(12)
+  }
+
+  @Builder
+  EqualizerSheet() {
+    Column() {
+      Column() {
+        EqualizerViewWithCallback({
+          onEqualizerChanged: () => {
+            this.syncEqualizerUi();
+            if (this.mIjkMediaPlayer) {
+              const equalizerManager = EqualizerManager.getInstance();
+              equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+              this.markEqualizerApplied();
+            }
+          }
+        })
+      }
+      .margin({ top: 8 })
+
+      Column() {
+        this.EqualizerApplyStatusBar()
+      }
+      .margin({ top: 8 })
+    }
+    .padding({ left: 12, right: 12, bottom: 20 })
+    .width('100%')
+  }
 
   //开始AB循环
   startABLoop(){
@@ -13745,8 +13951,8 @@ export struct LocalMusic {
       this.mIjkMediaPlayer.reset();
     }
     this.CONTROL_PlayStatus = PlayStatus.PLAY;
-    //设置debug模式
-    this.mIjkMediaPlayer.setDebug(false);
+    //设置debug模式(用于EQ日志采集)
+    this.mIjkMediaPlayer.setDebug(true);
     //初始化配置
     this.mIjkMediaPlayer.native_setup();
     // 初始化配置后需要重新设置音频流音量,否则音量为默认值1.0 针对dsf格式设置最大值1.0
@@ -13899,8 +14105,8 @@ export struct LocalMusic {
     LogUtils.getInstance().LOGI('getSpeed--' + Speed)
     //是否开启循环播放
     // this.mIjkMediaPlayer.setLoopCount(this.loop);
-    let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = {
-      onVideoSizeChanged: (width: number, height: number, sar_num: number, sar_den: number) => {
+    let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = new ImplOnVideoSizeChangedListener(
+      (width: number, height: number, sar_num: number, sar_den: number) => {
         if (height === 0) {
           return;
         }
@@ -13919,10 +14125,10 @@ export struct LocalMusic {
 
         that.hideLoadIng();
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
-    let mOnPreparedListener: OnPreparedListener = {
-      onPrepared: async () => {
+    let mOnPreparedListener: OnPreparedListener = new ImplOnPreparedListener(
+      async () => {
         LogUtils.getInstance().LOGI("setOnPreparedListener-->go");
         // 隐藏加载进度
         this.isPlayerLoading = false;
@@ -14149,17 +14355,17 @@ export struct LocalMusic {
 
 
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnPreparedListener(mOnPreparedListener);
 
-    let mOnTimedTextListener: OnTimedTextListener = {
-      onTimedText: () => {
+    let mOnTimedTextListener: OnTimedTextListener = new ImplOnTimedTextListener(
+      (text: string) => {
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnTimedTextListener(mOnTimedTextListener)
 
-    let mOnCompletionListener: OnCompletionListener = {
-      onCompletion: () => {
+    let mOnCompletionListener: OnCompletionListener = new ImplOnCompletionListener(
+      () => {
 
         LogUtils.getInstance().LOGI("OnCompletionListener-->go")
         if (this.playType == 0) { //0:连续播放  1:单片重复播放 2:正常播放
@@ -14189,11 +14395,11 @@ export struct LocalMusic {
 
 
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnCompletionListener(mOnCompletionListener);
 
-    let mOnBufferingUpdateListener: OnBufferingUpdateListener = {
-      onBufferingUpdate: (percent: number) => {
+    let mOnBufferingUpdateListener: OnBufferingUpdateListener = new ImplOnBufferingUpdateListener(
+      (percent: number) => {
 
         LogUtils.getInstance().LOGI("OnBufferingUpdateListener-->go:" + percent);
         let MediaInfo = this.mIjkMediaPlayer.getMediaInfo()
@@ -14216,11 +14422,11 @@ export struct LocalMusic {
         let Looping = this.mIjkMediaPlayer.isLooping()
         LogUtils.getInstance().LOGI('isLooping---' + Looping);
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
 
-    let mOnSeekCompleteListener: OnSeekCompleteListener = {
-      onSeekComplete: () => {
+    let mOnSeekCompleteListener: OnSeekCompleteListener = new ImplOnSeekCompleteListener(
+      () => {
         LogUtils.getInstance().LOGI("OnSeekCompleteListener-->go");
         console.info('onecold startPlayOrResumePlay 10643')
         // seek 完成,延迟恢复播放,确保 ijkplayer 内部状态完全稳定
@@ -14232,20 +14438,20 @@ export struct LocalMusic {
         }, 200); // 延迟200ms,确保ijkplayer内部状态稳定
 
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
 
-    let mOnInfoListener: OnInfoListener = {
-      onInfo: (what: number, extra: number) => {
+    let mOnInfoListener: OnInfoListener = new ImplOnInfoListener(
+      (what: number, extra: number) => {
         LogUtils.getInstance().LOGI("OnInfoListener-->go:" + what + "===" + extra);
         that.hideLoadIng();
       }
-    }
+    );
     this.mIjkMediaPlayer.setOnInfoListener(mOnInfoListener);
 
 
-    let mOnErrorListener: OnErrorListener = {
-      onError: (what: number, extra: number) => {
+    let mOnErrorListener: OnErrorListener = new ImplOnErrorListener(
+      (what: number, extra: number) => {
         // 隐藏加载进度
         this.isPlayerLoading = false;
         this.stopProgressTask();
@@ -14297,13 +14503,24 @@ export struct LocalMusic {
         }
         that.hideLoadIng();
       }
-    }
+    );
 
 
     this.mIjkMediaPlayer.setOnErrorListener(mOnErrorListener);
 
     this.mIjkMediaPlayer.setMessageListener();
 
+    // 应用均衡器设置
+    try {
+      const equalizerManager = EqualizerManager.getInstance();
+      equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+      Logger.info(TAG, 'heanup 均衡器设置已应用');
+      this.markEqualizerApplied();
+    } catch (eqError) {
+      Logger.warn(TAG, `heanup 应用均衡器失败: ${(eqError as Error).message}`);
+      this.eqApplying = false;
+    }
+
     this.mIjkMediaPlayer.prepareAsync();
 
     this.mIjkMediaPlayer.start();

+ 8 - 0
ijkplayer/src/main/cpp/ijkplayer/ff_ffplay_def.h

@@ -686,6 +686,11 @@ typedef struct FFPlayer {
     int opensles;
     int soundtouch_enable;
 
+    // Equalizer state (persist across audio output re-create)
+    int equalizer_enabled;
+    int equalizer_has_gains;
+    float equalizer_gains[10];
+
     char *iformat_name;
 
     int no_time_adjust;
@@ -826,6 +831,9 @@ inline static void ffp_reset_internal(FFPlayer *ffp)
 
     ffp->opensles                       = 0; // option
     ffp->soundtouch_enable              = 0; // option
+    ffp->equalizer_enabled              = 0;
+    ffp->equalizer_has_gains            = 0;
+    memset(ffp->equalizer_gains, 0, sizeof(ffp->equalizer_gains));
 
     ffp->iformat_name                   = NULL; // option
 

+ 33 - 0
ijkplayer/src/main/cpp/ijkplayer/ijkplayer_android.c

@@ -26,6 +26,9 @@
 #include "ff_fferror.h"
 #include "ff_ffplay.h"
 #include "ijkplayer_internal.h"
+#include <stddef.h>
+#include <string.h>
+#include <hilog/log.h>
 #include "pipeline/ffpipeline_android.h"
 #include "../ijksdl/video/ijksdl_vout_android_surface.h"
 
@@ -94,3 +97,33 @@ int ijkmp_android_get_audio_session_id(IjkMediaPlayer *mp)
 
     return audio_session_id;
 }
+
+void ijkmp_android_set_equalizer_enabled(IjkMediaPlayer *mp, bool enabled)
+{
+    if (!mp) return;
+    OH_LOG_Print(LOG_APP, LOG_INFO, 0xB000, "IjkAudioEq", "ijkmp_android_set_equalizer_enabled: %{public}d", enabled);
+
+    pthread_mutex_lock(&mp->mutex);
+    if (mp && mp->ffplayer) {
+        mp->ffplayer->equalizer_enabled = enabled ? 1 : 0;
+        if (mp->ffplayer->aout) {
+            SDL_AoutSetEqualizerEnabled(mp->ffplayer->aout, enabled);
+        }
+    }
+    pthread_mutex_unlock(&mp->mutex);
+}
+
+void ijkmp_android_set_equalizer_gains(IjkMediaPlayer *mp, const float *gains)
+{
+    if (!mp || !gains) return;
+
+    pthread_mutex_lock(&mp->mutex);
+    if (mp && mp->ffplayer) {
+        memcpy(mp->ffplayer->equalizer_gains, gains, sizeof(mp->ffplayer->equalizer_gains));
+        mp->ffplayer->equalizer_has_gains = 1;
+        if (mp->ffplayer->aout) {
+            SDL_AoutSetEqualizerGains(mp->ffplayer->aout, gains);
+        }
+    }
+    pthread_mutex_unlock(&mp->mutex);
+}

+ 4 - 0
ijkplayer/src/main/cpp/ijkplayer/ijkplayer_android.h

@@ -42,4 +42,8 @@ void ijkmp_android_set_volume(IjkMediaPlayer *mp, float left, float right);
 
 int  ijkmp_android_get_audio_session_id(IjkMediaPlayer *mp);
 
+// Equalizer support
+void ijkmp_android_set_equalizer_enabled(IjkMediaPlayer *mp, bool enabled);
+void ijkmp_android_set_equalizer_gains(IjkMediaPlayer *mp, const float *gains);
+
 #endif

+ 1 - 0
ijkplayer/src/main/cpp/ijksdl/CMakeLists.txt

@@ -33,6 +33,7 @@ add_library(ijksdl  SHARED    ijksdl_aout.c
                               ffmpeg/ijksdl_vout_overlay_ffmpeg.c
                               ffmpeg/abi_all/image_convert.c
                               audio/ijksdl_aout_android_opensles.c
+                              audio/ijksdl_equalizer.c
                               dummy/ijksdl_vout_dummy.c
                               ${CMAKE_CURRENT_SOURCE_DIR}/../utils/ohoslog/ohos_log.cpp
                                )

+ 44 - 1
ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_aout_android_opensles.c

@@ -23,6 +23,7 @@
  */
  
 #include "ijksdl_aout_android_opensles.h"
+#include "ijksdl_equalizer.h"
 #include "ohaudio/native_audiorenderer.h"
 #include "ohaudio/native_audiostreambuilder.h"
 #include "ohaudio/native_audiostream_base.h"
@@ -89,6 +90,9 @@ typedef struct SDL_Aout_Opaque {
 
     uint8_t       * buffer;
     size_t         buffer_capacity;
+
+    // Equalizer
+    Equalizer      equalizer;
 } SDL_Aout_Opaque;
 
 static void AudioCalcFramesWrittenNeedTime(OH_AudioRenderer *renderer, SDL_Aout_Opaque *opaque)
@@ -134,6 +138,12 @@ static int32_t AudioRendererOnWriteData(OH_AudioRenderer *renderer, void *userDa
     }
     AudioCalcFramesWrittenNeedTime(renderer, opaque);
     audioCallback(opaque->spec.userdata, (uint8_t *)buffer, bufferLen);
+    
+    // 均衡器处理
+    if (opaque->equalizer.enabled && opaque->equalizer.initialized) {
+        equalizer_process_s16(&opaque->equalizer, (int16_t *)buffer, bufferLen);
+    }
+    
     opaque->writtenLen += bufferLen;
     return 0;
 }
@@ -234,6 +244,15 @@ static int AoutOpenAudio(SDL_Aout *aout, const SDL_AudioSpec *desired, SDL_Audio
     opaque->abort_request = false;
     opaque->writtenLen = 0;
 
+    // 初始化均衡器
+    equalizer_init(&opaque->equalizer, desired->freq, desired->channels);
+    if (opaque->ffp) {
+        if (opaque->ffp->equalizer_has_gains) {
+            equalizer_set_gains(&opaque->equalizer, opaque->ffp->equalizer_gains);
+        }
+        equalizer_set_enabled(&opaque->equalizer, opaque->ffp->equalizer_enabled != 0);
+    }
+
     rendererCallbacks.OH_AudioRenderer_OnWriteData = AudioRendererOnWriteData; // 看下数据写入OnWriteData
     rendererCallbacks.OH_AudioRenderer_OnStreamEvent = AudioRendererOnStreamEvent;  // 自定义音频流事件函数
     rendererCallbacks.OH_AudioRenderer_OnInterruptEvent = AudioRendererOnInterrupt; // 自定义音频中断事件函数
@@ -344,6 +363,26 @@ static void AoutSetVolume(SDL_Aout *aout, float leftVolume, float rightVolume)
     }
 }
 
+// 均衡器控制函数
+static void AoutSetEqualizerEnabled(SDL_Aout *aout, bool enabled)
+{
+    if ((aout == NULL) || (aout->opaque == NULL)) {
+        return;
+    }
+    SDL_Aout_Opaque *opaque = aout->opaque;
+    equalizer_set_enabled(&opaque->equalizer, enabled);
+    LOGI("audio->equalizer enabled: %d", enabled);
+}
+
+static void AoutSetEqualizerGains(SDL_Aout *aout, const float *gains)
+{
+    if ((aout == NULL) || (aout->opaque == NULL) || (gains == NULL)) {
+        return;
+    }
+    SDL_Aout_Opaque *opaque = aout->opaque;
+    equalizer_set_gains(&opaque->equalizer, gains);
+}
+
 SDL_Aout *SDLAoutCreateForOpenSLES(FFPlayer *ffp) // 实现音频播放功能;
 {
     LOGI("audio->SDL_AoutAndroid_CreateForOpenSLES");
@@ -366,6 +405,10 @@ SDL_Aout *SDLAoutCreateForOpenSLES(FFPlayer *ffp) // 实现音频播放功能;
     aout->set_volume = AoutSetVolume;       // 设置音量
     aout->func_get_latency_seconds = AoutGetLatencySeconds;
 
+    // 均衡器控制
+    aout->set_equalizer_enabled = AoutSetEqualizerEnabled;
+    aout->set_equalizer_gains = AoutSetEqualizerGains;
+
     return aout;
 }
 
@@ -376,4 +419,4 @@ bool SDL_AoutAndroid_IsObjectOfOpenSLES(SDL_Aout * aout)
         return false;
     }
     return aout->opaque_class == &g_opensles_class;
-}
+}

+ 225 - 0
ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_equalizer.c

@@ -0,0 +1,225 @@
+/*
+ * ijksdl_equalizer.c
+ * 10-band Biquad IIR Equalizer for ijkplayer
+ *
+ * Copyright (c) 2024
+ * Licensed under the Apache License, Version 2.0
+ *
+ * 基于 Audio EQ Cookbook by Robert Bristow-Johnson 实现
+ * Peaking EQ 滤波器设计
+ */
+
+#include "ijksdl_equalizer.h"
+#include <math.h>
+#include <string.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+// 限制增益范围
+static inline float clamp_gain(float gain_db) {
+    if (gain_db < -12.0f) return -12.0f;
+    if (gain_db > 12.0f) return 12.0f;
+    return gain_db;
+}
+
+// 限制采样值范围
+static inline int16_t clamp_sample(float sample) {
+    if (sample > 32767.0f) return 32767;
+    if (sample < -32768.0f) return -32768;
+    return (int16_t)sample;
+}
+
+/**
+ * 计算 Peaking EQ Biquad 滤波器系数
+ * 基于 Audio EQ Cookbook
+ */
+static void calculate_peaking_eq_coefficients(BiquadFilter *filter, int sample_rate) {
+    float gain_db = filter->gain_db;
+    float frequency = filter->frequency;
+    float q = filter->q;
+    
+    // 如果增益接近0,设置为直通滤波器
+    if (fabsf(gain_db) < 0.1f) {
+        filter->b0 = 1.0f;
+        filter->b1 = 0.0f;
+        filter->b2 = 0.0f;
+        filter->a1 = 0.0f;
+        filter->a2 = 0.0f;
+        return;
+    }
+    
+    // 确保频率不超过 Nyquist 频率
+    float nyquist = (float)sample_rate / 2.0f;
+    if (frequency >= nyquist) {
+        frequency = nyquist * 0.9f;
+    }
+    
+    float A = powf(10.0f, gain_db / 40.0f);
+    float omega = 2.0f * (float)M_PI * frequency / (float)sample_rate;
+    float sin_omega = sinf(omega);
+    float cos_omega = cosf(omega);
+    float alpha = sin_omega / (2.0f * q);
+    
+    float b0 = 1.0f + alpha * A;
+    float b1 = -2.0f * cos_omega;
+    float b2 = 1.0f - alpha * A;
+    float a0 = 1.0f + alpha / A;
+    float a1 = -2.0f * cos_omega;
+    float a2 = 1.0f - alpha / A;
+    
+    // 归一化系数
+    filter->b0 = b0 / a0;
+    filter->b1 = b1 / a0;
+    filter->b2 = b2 / a0;
+    filter->a1 = a1 / a0;
+    filter->a2 = a2 / a0;
+}
+
+/**
+ * 重置单个滤波器状态
+ */
+static void reset_filter_state(BiquadFilter *filter) {
+    filter->x1_l = 0.0f;
+    filter->x2_l = 0.0f;
+    filter->y1_l = 0.0f;
+    filter->y2_l = 0.0f;
+    filter->x1_r = 0.0f;
+    filter->x2_r = 0.0f;
+    filter->y1_r = 0.0f;
+    filter->y2_r = 0.0f;
+}
+
+/**
+ * 处理单个样本 (左声道)
+ */
+static inline float process_sample_left(BiquadFilter *filter, float input) {
+    float output = filter->b0 * input 
+                 + filter->b1 * filter->x1_l 
+                 + filter->b2 * filter->x2_l
+                 - filter->a1 * filter->y1_l 
+                 - filter->a2 * filter->y2_l;
+    
+    filter->x2_l = filter->x1_l;
+    filter->x1_l = input;
+    filter->y2_l = filter->y1_l;
+    filter->y1_l = output;
+    
+    return output;
+}
+
+/**
+ * 处理单个样本 (右声道)
+ */
+static inline float process_sample_right(BiquadFilter *filter, float input) {
+    float output = filter->b0 * input 
+                 + filter->b1 * filter->x1_r 
+                 + filter->b2 * filter->x2_r
+                 - filter->a1 * filter->y1_r 
+                 - filter->a2 * filter->y2_r;
+    
+    filter->x2_r = filter->x1_r;
+    filter->x1_r = input;
+    filter->y2_r = filter->y1_r;
+    filter->y1_r = output;
+    
+    return output;
+}
+
+void equalizer_init(Equalizer *eq, int sample_rate, int channels) {
+    if (eq == NULL) return;
+    
+    memset(eq, 0, sizeof(Equalizer));
+    eq->sample_rate = sample_rate;
+    eq->channels = channels;
+    eq->enabled = false;
+    eq->initialized = true;
+    
+    // 初始化每个频段
+    for (int i = 0; i < EQ_BAND_COUNT; i++) {
+        BiquadFilter *filter = &eq->bands[i];
+        filter->frequency = EQ_CENTER_FREQUENCIES[i];
+        filter->gain_db = 0.0f;
+        filter->q = EQ_DEFAULT_Q;
+        reset_filter_state(filter);
+        calculate_peaking_eq_coefficients(filter, sample_rate);
+    }
+}
+
+void equalizer_set_enabled(Equalizer *eq, bool enabled) {
+    if (eq == NULL) return;
+    eq->enabled = enabled;
+}
+
+void equalizer_set_gains(Equalizer *eq, const float *gains) {
+    if (eq == NULL || gains == NULL) return;
+    
+    for (int i = 0; i < EQ_BAND_COUNT; i++) {
+        equalizer_set_band_gain(eq, i, gains[i]);
+    }
+}
+
+void equalizer_set_band_gain(Equalizer *eq, int band_index, float gain_db) {
+    if (eq == NULL || band_index < 0 || band_index >= EQ_BAND_COUNT) return;
+    
+    BiquadFilter *filter = &eq->bands[band_index];
+    float clamped_gain = clamp_gain(gain_db);
+    
+    // 只有增益变化时才重新计算系数
+    if (fabsf(filter->gain_db - clamped_gain) > 0.01f) {
+        filter->gain_db = clamped_gain;
+        calculate_peaking_eq_coefficients(filter, eq->sample_rate);
+    }
+}
+
+void equalizer_process_s16(Equalizer *eq, int16_t *buffer, int buffer_size) {
+    if (eq == NULL || buffer == NULL || buffer_size <= 0) return;
+    if (!eq->enabled || !eq->initialized) return;
+    
+    int channels = eq->channels;
+    int sample_count = buffer_size / (sizeof(int16_t) * channels);
+    
+    if (channels == 2) {
+        // 立体声处理
+        for (int i = 0; i < sample_count; i++) {
+            float left = (float)buffer[i * 2];
+            float right = (float)buffer[i * 2 + 1];
+            
+            // 通过所有频段滤波器
+            for (int b = 0; b < EQ_BAND_COUNT; b++) {
+                BiquadFilter *filter = &eq->bands[b];
+                // 跳过增益为0的滤波器
+                if (fabsf(filter->gain_db) < 0.1f) continue;
+                
+                left = process_sample_left(filter, left);
+                right = process_sample_right(filter, right);
+            }
+            
+            buffer[i * 2] = clamp_sample(left);
+            buffer[i * 2 + 1] = clamp_sample(right);
+        }
+    } else {
+        // 单声道处理
+        for (int i = 0; i < sample_count; i++) {
+            float sample = (float)buffer[i];
+            
+            for (int b = 0; b < EQ_BAND_COUNT; b++) {
+                BiquadFilter *filter = &eq->bands[b];
+                if (fabsf(filter->gain_db) < 0.1f) continue;
+                
+                sample = process_sample_left(filter, sample);
+            }
+            
+            buffer[i] = clamp_sample(sample);
+        }
+    }
+}
+
+void equalizer_reset(Equalizer *eq) {
+    if (eq == NULL) return;
+    
+    for (int i = 0; i < EQ_BAND_COUNT; i++) {
+        reset_filter_state(&eq->bands[i]);
+    }
+}

+ 106 - 0
ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_equalizer.h

@@ -0,0 +1,106 @@
+/*
+ * ijksdl_equalizer.h
+ * 10-band Biquad IIR Equalizer for ijkplayer
+ *
+ * Copyright (c) 2024
+ * Licensed under the Apache License, Version 2.0
+ */
+
+#ifndef IJKSDL_EQUALIZER_H
+#define IJKSDL_EQUALIZER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 均衡器频段数量
+#define EQ_BAND_COUNT 10
+
+// 10频段中心频率 (Hz)
+static const float EQ_CENTER_FREQUENCIES[EQ_BAND_COUNT] = {
+    32.0f, 64.0f, 125.0f, 250.0f, 500.0f,
+    1000.0f, 2000.0f, 4000.0f, 8000.0f, 16000.0f
+};
+
+// 滤波器带宽 (Q factor)
+#define EQ_DEFAULT_Q 1.41f
+
+// 单个 Biquad 滤波器状态
+typedef struct BiquadFilter {
+    // 滤波器系数
+    float b0, b1, b2;
+    float a1, a2;
+    
+    // 滤波器状态 (双通道)
+    float x1_l, x2_l;  // 左声道输入延迟
+    float y1_l, y2_l;  // 左声道输出延迟
+    float x1_r, x2_r;  // 右声道输入延迟
+    float y1_r, y2_r;  // 右声道输出延迟
+    
+    // 滤波器参数
+    float frequency;   // 中心频率
+    float gain_db;     // 增益 (dB)
+    float q;           // Q值
+} BiquadFilter;
+
+// 均衡器主结构
+typedef struct Equalizer {
+    BiquadFilter bands[EQ_BAND_COUNT];
+    bool enabled;
+    int sample_rate;
+    int channels;
+    bool initialized;
+} Equalizer;
+
+/**
+ * 初始化均衡器
+ * @param eq 均衡器结构指针
+ * @param sample_rate 采样率
+ * @param channels 声道数 (1 或 2)
+ */
+void equalizer_init(Equalizer *eq, int sample_rate, int channels);
+
+/**
+ * 启用/禁用均衡器
+ * @param eq 均衡器结构指针
+ * @param enabled true=启用, false=禁用
+ */
+void equalizer_set_enabled(Equalizer *eq, bool enabled);
+
+/**
+ * 设置均衡器增益
+ * @param eq 均衡器结构指针
+ * @param gains 10个频段的增益值数组 (-12.0 ~ +12.0 dB)
+ */
+void equalizer_set_gains(Equalizer *eq, const float *gains);
+
+/**
+ * 设置单个频段增益
+ * @param eq 均衡器结构指针
+ * @param band_index 频段索引 (0-9)
+ * @param gain_db 增益值 (-12.0 ~ +12.0 dB)
+ */
+void equalizer_set_band_gain(Equalizer *eq, int band_index, float gain_db);
+
+/**
+ * 处理音频数据 (S16LE 格式)
+ * @param eq 均衡器结构指针
+ * @param buffer 音频数据缓冲区
+ * @param buffer_size 缓冲区大小 (字节)
+ */
+void equalizer_process_s16(Equalizer *eq, int16_t *buffer, int buffer_size);
+
+/**
+ * 重置均衡器状态
+ * @param eq 均衡器结构指针
+ */
+void equalizer_reset(Equalizer *eq);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IJKSDL_EQUALIZER_H

+ 14 - 0
ijkplayer/src/main/cpp/ijksdl/ijksdl_aout.c

@@ -133,3 +133,17 @@ int SDL_AoutGetAudioPerSecondCallBacks(SDL_Aout *aout)
     }
     return SDL_AUDIO_MAX_CALLBACKS_PER_SEC;
 }
+
+void SDL_AoutSetEqualizerEnabled(SDL_Aout *aout, bool enabled)
+{
+    if (aout && aout->set_equalizer_enabled) {
+        aout->set_equalizer_enabled(aout, enabled);
+    }
+}
+
+void SDL_AoutSetEqualizerGains(SDL_Aout *aout, const float *gains)
+{
+    if (aout && aout->set_equalizer_gains && gains) {
+        aout->set_equalizer_gains(aout, gains);
+    }
+}

+ 9 - 0
ijkplayer/src/main/cpp/ijksdl/ijksdl_aout.h

@@ -26,6 +26,7 @@
 #define IJKSDL__IJKSDL_AOUT_H
 #define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
 
+#include <stdbool.h>
 #include "ijksdl_audio.h"
 #include "ijksdl_class.h"
 #include "ijksdl_mutex.h"
@@ -55,6 +56,10 @@ struct SDL_Aout {
 
     // Android only
     int    (*func_get_audio_session_id)(SDL_Aout *aout);
+
+    // Equalizer support
+    void   (*set_equalizer_enabled)(SDL_Aout *aout, bool enabled);
+    void   (*set_equalizer_gains)(SDL_Aout *aout, const float *gains);
 };
 
 int SDL_AoutOpenAudio(SDL_Aout *aout, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained);
@@ -76,4 +81,8 @@ void   SDL_AoutSetPlaybackVolume(SDL_Aout *aout, float volume);
 // android only
 int    SDL_AoutGetAudioSessionId(SDL_Aout *aout);
 
+// equalizer
+void   SDL_AoutSetEqualizerEnabled(SDL_Aout *aout, bool enabled);
+void   SDL_AoutSetEqualizerGains(SDL_Aout *aout, const float *gains);
+
 #endif

+ 57 - 0
ijkplayer/src/main/cpp/napi/ijkplayer_napi.cpp

@@ -13,7 +13,9 @@
  * limitations under the License.
  */
 
+#include <unistd.h>
 #include "ijkplayer_napi.h"
+#include <hilog/log.h>
 
 const int32_t STR_DEFAULT_SIZE = 2048;
 const int32_t INDEX_0 = 0;
@@ -651,6 +653,59 @@ napi_value IJKPlayerNapi::nativeOpenlog(napi_env env, napi_callback_info info) {
     return nullptr;
 }
 
+napi_value IJKPlayerNapi::setEqualizerEnabled(napi_env env, napi_callback_info info) {
+    LOGI("napi-->setEqualizerEnabled");
+    size_t argc = PARAM_COUNT_2;
+    napi_value args[PARAM_COUNT_2] = {nullptr};
+    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
+    std::string xcomponentId;
+    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, xcomponentId);
+    if (xcomponentId == "") {
+        xcomponentId = IJKPlayerNapi::getXComponentId(env, info);
+    }
+    bool enabled = false;
+    napi_get_value_bool(env, args[INDEX_1], &enabled);
+    IJKPlayerNapi::getInstance(xcomponentId)->ijkPlayerNapiProxy_->IjkMediaPlayer_setEqualizerEnabled(enabled);
+    return nullptr;
+}
+
+napi_value IJKPlayerNapi::setEqualizerGains(napi_env env, napi_callback_info info) {
+    size_t argc = PARAM_COUNT_2;
+    napi_value args[PARAM_COUNT_2] = {nullptr};
+    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
+    std::string xcomponentId;
+    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, xcomponentId);
+    if (xcomponentId == "") {
+        xcomponentId = IJKPlayerNapi::getXComponentId(env, info);
+    }
+    
+    // Check if args[1] is array
+    bool isArray = false;
+    napi_is_array(env, args[INDEX_1], &isArray);
+    if (!isArray) {
+        return nullptr;
+    }
+    
+    // Parse the gains array (10 bands)
+    napi_value gainsArray = args[INDEX_1];
+    uint32_t length = 0;
+    napi_get_array_length(env, gainsArray, &length);
+    
+    const int EQ_BAND_COUNT = 10;
+    float gains[EQ_BAND_COUNT] = {0.0f};
+    for (uint32_t i = 0; i < length && i < EQ_BAND_COUNT; i++) {
+        napi_value element;
+        napi_get_element(env, gainsArray, i, &element);
+        double value = 0.0;
+        napi_get_value_double(env, element, &value);
+        gains[i] = (float)value;
+    }
+    
+    IJKPlayerNapi::getInstance(xcomponentId)->ijkPlayerNapiProxy_->IjkMediaPlayer_setEqualizerGains(gains);
+    return nullptr;
+}
+
+
 napi_value IJKPlayerNapi::JsConstructor(napi_env env, napi_callback_info info) {
     LOGI("napi-->JsConstructor");
     napi_value targetObj = nullptr;
@@ -868,6 +923,8 @@ napi_value IJKPlayerNapi::Export(napi_env env, napi_value exports) {
         DECLARE_NAPI_FUNCTION("_getMediaMeta", IJKPlayerNapi::getMediaMeta),
         DECLARE_NAPI_FUNCTION("_nativeOpenlog", IJKPlayerNapi::nativeOpenlog),
         DECLARE_NAPI_FUNCTION("_native_setup", IJKPlayerNapi::native_setup),
+        DECLARE_NAPI_FUNCTION("_setEqualizerEnabled", IJKPlayerNapi::setEqualizerEnabled),
+        DECLARE_NAPI_FUNCTION("_setEqualizerGains", IJKPlayerNapi::setEqualizerGains),
     };
     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
     return exports;

+ 2 - 0
ijkplayer/src/main/cpp/napi/ijkplayer_napi.h

@@ -55,6 +55,8 @@ class IJKPlayerNapi {
     static napi_value setStreamSelected(napi_env env, napi_callback_info info);
     static napi_value getMediaMeta(napi_env env, napi_callback_info info);
     static napi_value nativeOpenlog(napi_env env, napi_callback_info info);
+    static napi_value setEqualizerEnabled(napi_env env, napi_callback_info info);
+    static napi_value setEqualizerGains(napi_env env, napi_callback_info info);
     static napi_value native_setup(napi_env env, napi_callback_info info);
     static napi_value native_setup_audio(napi_env env, napi_callback_info info);
     static napi_value JsConstructor(napi_env env, napi_callback_info info);

+ 3 - 1
ijkplayer/src/main/cpp/napi/ijkplayer_napi_audio_init.cpp

@@ -46,7 +46,9 @@ static napi_value Init(napi_env env, napi_value exports) {
         DECLARE_NAPI_FUNCTION("_getMediaMeta", IJKPlayerNapi::getMediaMeta),
         DECLARE_NAPI_FUNCTION("_nativeOpenlog", IJKPlayerNapi::nativeOpenlog),
         DECLARE_NAPI_FUNCTION("_native_setup", IJKPlayerNapi::native_setup),
-        DECLARE_NAPI_FUNCTION("_native_setup_audio", IJKPlayerNapi::native_setup_audio)
+        DECLARE_NAPI_FUNCTION("_native_setup_audio", IJKPlayerNapi::native_setup_audio),
+        DECLARE_NAPI_FUNCTION("_setEqualizerEnabled", IJKPlayerNapi::setEqualizerEnabled),
+        DECLARE_NAPI_FUNCTION("_setEqualizerGains", IJKPlayerNapi::setEqualizerGains)
     };
     napi_value ijkAudio = nullptr;
     const char *classBindName = "IJKPlayerNapi";

+ 23 - 0
ijkplayer/src/main/cpp/proxy/ijkplayer_napi_proxy.cpp

@@ -14,6 +14,7 @@
  */
 
 #include "ijkplayer_napi_proxy.h"
+#include <hilog/log.h>
 static std::unordered_map<std::string, IjkMediaPlayer *> GLOBAL_IJKMP;
 void (*post_event)(int what, int arg1, int arg2, char *obj, std::string id);
 
@@ -589,4 +590,26 @@ HashMap IJKPlayerNapiProxy::IjkMediaPlayer_getMediaMeta() {
 void IJKPlayerNapiProxy::IjkMediaPlayer_native_openlog() {
     OHOS_LOG_ON = true;
     open_custom_ffmpeg_log_print();
+}
+
+void IJKPlayerNapiProxy::IjkMediaPlayer_setEqualizerEnabled(bool enabled) {
+    OH_LOG_Print(LOG_APP, LOG_INFO, 0xB000, "IjkAudioEq", "Proxy setEqualizerEnabled: %{public}d", enabled);
+    IjkMediaPlayer *mp = IJKPlayerNapiProxy::get_media_player(id_);
+    if (!mp) {
+        LOGE("napi_proxy->IjkMediaPlayer_setEqualizerEnabled mp NULL");
+        return;
+    }
+    ijkmp_android_set_equalizer_enabled(mp, enabled);
+    ijkmp_dec_ref_p(&mp);
+}
+
+void IJKPlayerNapiProxy::IjkMediaPlayer_setEqualizerGains(const float *gains) {
+    OH_LOG_Print(LOG_APP, LOG_INFO, 0xB000, "IjkAudioEq", "Proxy setEqualizerGains band0: %{public}f", gains[0]);
+    IjkMediaPlayer *mp = IJKPlayerNapiProxy::get_media_player(id_);
+    if (!mp) {
+        LOGE("napi_proxy->IjkMediaPlayer_setEqualizerGains mp NULL");
+        return;
+    }
+    ijkmp_android_set_equalizer_gains(mp, gains);
+    ijkmp_dec_ref_p(&mp);
 }

+ 2 - 0
ijkplayer/src/main/cpp/proxy/ijkplayer_napi_proxy.h

@@ -63,6 +63,8 @@ class IJKPlayerNapiProxy {
     void ijkMediaPlayer_setStreamSelected(int stream, bool selected);
     HashMap IjkMediaPlayer_getMediaMeta();
     void IjkMediaPlayer_native_openlog();
+    void IjkMediaPlayer_setEqualizerEnabled(bool enabled);
+    void IjkMediaPlayer_setEqualizerGains(const float *gains);
     IjkMediaPlayer *set_media_player(std::string id, IjkMediaPlayer *mp);
     IjkMediaPlayer *get_media_player(std::string id);
     void delete_media_player(std::string id);

+ 7 - 5
ijkplayer/src/main/cpp/types/ijkplayer_audio_napi/index.d.ts

@@ -18,19 +18,19 @@ export declare class newIjkPlayerAudio {
   constructor(Id: string, isVideo: boolean);
   _native_setup_audio(xcomponentId: string): void;
   _nativeOpenlog(xcomponentId: string): void;
-  _setDataSource(xcomponentId: string, url:string): void;
-  _setOption(xcomponentId: string, category:string,key:string,value:string): void;
-  _setOptionLong(xcomponentId: string, category:string,key:string,value:string): void;
+  _setDataSource(xcomponentId: string, url: string): void;
+  _setOption(xcomponentId: string, category: string, key: string, value: string): void;
+  _setOptionLong(xcomponentId: string, category: string, key: string, value: string): void;
   _prepareAsync(xcomponentId: string): void;
   _start(xcomponentId: string): void;
   _stop(xcomponentId: string): void;
   _pause(xcomponentId: string): void;
   _reset(xcomponentId: string): void;
   _release(xcomponentId: string): void;
-  _seekTo(xcomponentId: string, msec:string): void;
+  _seekTo(xcomponentId: string, msec: string): void;
   _isPlaying(xcomponentId: string): boolean;
   _getDuration(xcomponentId: string): number;
-  _setMessageListener(xcomponentId: string, messageCallBack:Function): void;
+  _setMessageListener(xcomponentId: string, messageCallBack: Function): void;
   _getCurrentPosition(xcomponentId: string): number;
   _getAudioSessionId(xcomponentId: string): number;
   _setPropertyFloat(xcomponentId: string, property: string, value: string): void;
@@ -45,4 +45,6 @@ export declare class newIjkPlayerAudio {
   _getAudioCodecInfo(xcomponentId: string): string;
   _getMediaMeta(xcomponentId: string): string;
   _native_setup(xcomponentId: string): void;
+  _setEqualizerEnabled(xcomponentId: string, enabled: boolean): void;
+  _setEqualizerGains(xcomponentId: string, gains: number[]): void;
 }

+ 19 - 0
ijkplayer/src/main/ets/ijkplayer/IjkMediaPlayer.ets

@@ -251,6 +251,25 @@ export class IjkMediaPlayer {
     }
   }
 
+  setEqualizerEnabled(enabled: boolean): void {
+    if (this.ijkplayer_audio_napi) {
+      this.ijkplayer_audio_napi._setEqualizerEnabled(this.id, enabled);
+    } else if (!!this.ijkplayer_napi) {
+      // Video player also has equalizer support
+      this.ijkplayer_napi._setEqualizerEnabled?.(this.id, enabled);
+    }
+  }
+
+  setEqualizerGains(gains: number[]): void {
+    if (this.ijkplayer_audio_napi) {
+      this.ijkplayer_audio_napi._setEqualizerGains(this.id, gains);
+    } else if (!!this.ijkplayer_napi) {
+      // Video player also has equalizer support
+      this.ijkplayer_napi._setEqualizerGains?.(this.id, gains);
+    }
+  }
+
+
   public on(type: AudioInterrupt|AudioDeviceChange,  callback: Callback<InterruptEvent>) : void {
     if(type == 'audioInterrupt') {
       this.interruptCallback= callback;

+ 2 - 0
ijkplayer/src/main/ets/ijkplayer/utils/IjkPlayerNapi.d.ets

@@ -41,4 +41,6 @@ export declare class IjkPlayerNapi {
   _getAudioCodecInfo(xcomponentId: string): string;
   _getMediaMeta(xcomponentId: string): string;
   _native_setup(xcomponentId: string): void;
+  _setEqualizerEnabled(xcomponentId: string, enabled: boolean): void;
+  _setEqualizerGains(xcomponentId: string, gains: number[]): void;
 }