HarmonyOS鸿蒙Next中ijkplayer播放MP4正常但播放m3u8提示“inputBufferInfoQueue_ is empty”

HarmonyOS鸿蒙Next中ijkplayer播放MP4正常但播放m3u8提示“inputBufferInfoQueue_ is empty”

// 无限制收流
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,"infbuf","1");
// 设置无缓冲,这是播放器的缓冲区,有数据就播放
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,"packet-buffering","0");
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'start-on-prepared', '1')
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'framedrop', '5')
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, 'fetch_first', 'on');
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "connect_timeout", "10000000");
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "listen_timeout", "10000000");
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "addrinfo_timeout", "10000000");
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", "10000000");

// 预读数据的缓冲区大小
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'max-buffer-size', '1024000');

// player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,"fflags","nobuffer");
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", "1000000");
// 设置探测媒体文件格式时读取的字节数
player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '10240');
// 减少缓冲区大小,极小化延迟
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'min-frames', '2');
// 缓冲区最大时长(单位:微秒)
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'max_cached_duration', '1000');


player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", '1');
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", '1');
player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", '1');

使用ijkplayer文档里的配置可以播放,但是mp4会黑屏很久,有优化空间吗


更多关于HarmonyOS鸿蒙Next中ijkplayer播放MP4正常但播放m3u8提示“inputBufferInfoQueue_ is empty”的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

开发者您好,

m3u8 是切片流,probesize(探测大小)太小可能导致播放器在解析索引文件或第一个 TS 切片时,还没读到完整的视频头信息:

mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '102400');

如果某些 m3u8 流依然报错,可以尝试关闭硬解测试:

mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", '0');

测试部分mp4链接,暂无黑屏很久的现象,能否提供相关的测试地址

本地测试代码如下:

import {
  IjkMediaPlayer,
  LogUtils,
  OnVideoSizeChangedListener
} from '@ohos/ijkplayer';
import { PromptAction } from '@kit.ArkUI';

export enum PlayStatus {
  INIT = 0,
  PAUSE = 1,
  PLAY = 2,
};

@Entry
@Component
struct PageDemo {
  private videoUrl: string = '';

  uiContext: UIContext = this.getUIContext();
  promptAction: PromptAction = this.uiContext.getPromptAction();
  @State mContext: object | undefined = undefined;
  private mIjkMediaPlayer = IjkMediaPlayer.getInstance();
  private controlPlayStatus = PlayStatus.INIT;
  private videoWidth: string = '100%';
  private initAspectRatio: number = 1;
  @State videoAspectRatio: number = this.initAspectRatio;
  @State isRecord: boolean = false;
  private recordSaveFilePath: string = '';

  aboutToDisappear() {
    this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
    if (this.controlPlayStatus !== PlayStatus.INIT) {
      this.stop();
    }
  }

  /**
   * 延时播放
   * @param context
   */
  private initDelayPlay(context: object) {
    this.mContext = context;
    setTimeout(() => {
      this.startResumePlay();
    }, 300);
  }

  private startResumePlay() {
    LogUtils.getInstance().LOGI('startResumePlay start this.controlPlayStatus:' + this.controlPlayStatus);
    if (this.controlPlayStatus === PlayStatus.INIT) {
      this.play(this.videoUrl.toString());
    }
    if (this.controlPlayStatus === PlayStatus.PAUSE) {
      this.mIjkMediaPlayer.start();
    }
  }

  private play(url: string) {
    this.initPlayer(url);
    this.mIjkMediaPlayer.prepareAsync();
    this.mIjkMediaPlayer.start();
  }

  private initPlayer(url: string) {
    // 设置XComponent回调的context
    if (!!this.mContext) {
      this.mIjkMediaPlayer.setContext(this.mContext, 'xcomponentId');
    }
    if (this.controlPlayStatus === PlayStatus.INIT) {
      this.mIjkMediaPlayer.reset();
    }
    this.controlPlayStatus = PlayStatus.PLAY;
    // 设置debug模式
    this.mIjkMediaPlayer.setDebug(true);
    // 初始化配置
    this.mIjkMediaPlayer.native_setup();
    // 初始化配置后需要重新设置音频流音量,否则音量为默认值1.0
    // 设置视频源
    this.mIjkMediaPlayer.setDataSource(url);
    // // 设置视频源http请求头
    // let headers = new Map([
    //   ['user_agent', 'Mozilla/5.0 BiliDroid/7.30.0 (xxxx.com)'],
    //   ['referer', 'xxx.xxx.xxxx.com']
    // ]);
    // this.mIjkMediaPlayer.setDataSourceHeader(headers);

    // 无限制收流
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,"infbuf","0");
    // 设置无缓冲,这是播放器的缓冲区,有数据就播放
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,"packet-buffering","0");
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'start-on-prepared', '1')
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'framedrop', '5')
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, 'fetch_first', 'on');
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "connect_timeout", "10000000");
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "listen_timeout", "10000000");
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "addrinfo_timeout", "10000000");
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", "10000000");

    // 预读数据的缓冲区大小
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'max-buffer-size', '1024000');

    // player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,"fflags","nobuffer");
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", "1000000");
    // 设置探测媒体文件格式时读取的字节数
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '102400');
    // 减少缓冲区大小,极小化延迟
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'min-frames', '2');
    // 缓冲区最大时长(单位:微秒)
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'max_cached_duration', '1000');


    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", '1');
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", '1');
    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", '1');

    this.registerListener();
  }

  // 注册ijkplayer事件监听
  private registerListener() {
    let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = {
      onVideoSizeChanged: (width: number, height: number, sarNum: number, sarDen: number) => {
        if (height === 0) {
          return;
        }
        const va = width / height;
        const vpa = this.initAspectRatio;
        if (vpa > va) {
          this.videoWidth = (width / (height * vpa)) * 100 + '%';
        } else {
          this.videoWidth = '100%';
        }
        if (width && height) {
          this.videoAspectRatio = width / height;
        }
        LogUtils.getInstance()
          .LOGI('setOnVideoSizeChangedListener-->go:' + width + ',' + height + ',' + sarNum + ',' + sarDen);
      }
    };
    this.mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);

    this.mIjkMediaPlayer.setMessageListener();
  }

  private stop() {
    this.controlPlayStatus = PlayStatus.INIT;
    this.mIjkMediaPlayer.stop();
    this.mIjkMediaPlayer.release();
  }

  startRecord(context: UIContext, block?: (success: boolean) => void) {
    let isRecord = this.mIjkMediaPlayer.isRecord();
    LogUtils.getInstance().LOGI(`testTag startRecord isRecord: ${isRecord}`);
    if (isRecord) {
      if (block) {
        block(false);
      }
      return;
    }
    this.recordSaveFilePath = context.getHostContext()?.cacheDir + '/record.mp4';
    this.mIjkMediaPlayer.setRecordDefaultFrameRate('30', false);
    let result = this.mIjkMediaPlayer.startRecord(this.recordSaveFilePath);
    LogUtils.getInstance().LOGI(`testTag startRecord recordSaveFilePath : ${this.recordSaveFilePath}  result : ${result}`);
    this.promptAction.openToast({
      message: '开始录制'
    });
    if (block) {
      block(result ? true : false);
    }
  }

  stopRecord(context: UIContext, block?: (success: boolean, filePath: string) => void) {
    let isRecord = this.mIjkMediaPlayer.isRecord();
    LogUtils.getInstance().LOGI(`testTag stopRecord isRecord: ${isRecord}`);
    if (!isRecord) {
      if (block) {
        block(false, '');
      }
      return;
    }

    this.mIjkMediaPlayer.stopRecord().then((result) => {
      if (!result) {
        LogUtils.getInstance().LOGI(`stopRecord fail: ${result}`);
        this.promptAction.openToast({
          message: '录制失败'
        });
        return;
      } else {
        this.promptAction.openToast({
          message: '录制完成'
        });
      }
    });
  }

  screenshot() {
    let saveFilePath = this.getUIContext().getHostContext()?.cacheDir + '/screen.jpg';
    this.mIjkMediaPlayer.screenshot(saveFilePath).then((result) => {
      if (!result) {
        this.promptAction.openToast({
          message: '截屏失败'
        });
      } else {
        this.promptAction.openToast({
          message: '截屏成功'
        });
      }
    });
  }

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Auto, justifyContent: FlexAlign.Start }) {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Text('ijkplayer播放器')
          .fontSize('30px')
          .fontColor(Color.White)
          .margin('10px')
          .fontWeight(FontWeight.Bold);
      }
      .height('100px')
      .width('100%')
      .backgroundColor(Color.Black);

      Divider().vertical(false).strokeWidth('20px').color(Color.White).lineCap(LineCapStyle.Round);

      Stack({ alignContent: Alignment.Center }) {
        Column() {
          XComponent({
            id: 'xcomponentId',
            type: 'surface',
            libraryname: 'ijkplayer_napi'
          })
            .onLoad((event?: object) => {
              if (!!event) {
                this.initDelayPlay(event);
              }
            })
            .onDestroy(() => {
            })
            .width('100%')
            .aspectRatio(this.videoAspectRatio)
        }
        .aspectRatio(this.videoAspectRatio);
      }
      .width('100%')
      .backgroundColor('#000000')
      .clip(true);

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Button(this.isRecord ? '结束录屏' : '开始录屏')
          .onClick(() => {
            if (this.isRecord) {
              // 结束录屏
              this.stopRecord(this.getUIContext());
            } else {
              // 开始录屏
              this.startRecord(this.getUIContext());
            }
            this.isRecord = !this.isRecord;
          })
          .width('400px')
          .height('80px')
          .margin('15px');

        Button('截图')
          .onClick(() => {
            this.screenshot();
          })
          .width('400px')
          .height('80px')
          .margin('15px');
      };
    }
  }
}

更多关于HarmonyOS鸿蒙Next中ijkplayer播放MP4正常但播放m3u8提示“inputBufferInfoQueue_ is empty”的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


+1 帮顶一下

在HarmonyOS鸿蒙Next中,ijkplayer播放m3u8时出现“inputBufferInfoQueue_ is empty”错误,通常与网络流媒体协议处理有关。该错误表明播放器的输入缓冲区队列为空,可能由于m3u8索引文件解析失败、网络请求超时或媒体片段(TS文件)未能正确加载导致。建议检查网络连接稳定性、m3u8文件格式合规性及服务器响应状态。

在HarmonyOS Next中使用ijkplayer播放m3u8时出现“inputBufferInfoQueue_ is empty”错误,通常与媒体解码器的缓冲区管理有关。这个问题在播放HLS流媒体时较为常见,而本地MP4文件播放正常,说明问题可能出在网络流处理和缓冲区配置上。

从你提供的配置来看,你已经设置了多个优化参数,但有几个关键点需要注意:

  1. 缓冲区配置冲突:你同时设置了infbuf(无限缓冲)和packet-buffering=0(无缓冲),这可能导致内部缓冲区状态不一致。对于m3u8流媒体,建议优先使用infbuf,但需要确保网络流能够持续提供数据。

  2. 缓冲区大小限制过严max_cached_duration设置为1000微秒(仅1毫秒),这对于网络流来说可能太短,容易导致缓冲区饥饿。建议适当增加这个值,例如设置为3000000(3秒)。

  3. 解码器配置:你已启用mediacodec,但m3u8流可能包含的编码格式或分片特性与硬解码器兼容性有关。可以尝试临时关闭mediacodec测试是否是解码器问题:

    player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", "0");
    
  4. 针对HLS的特定配置:添加以下HLS专用参数可能有助于解决问题:

    player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", "0");
    player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "fastseek");
    
  5. 网络缓冲区调整:增加网络缓冲区大小:

    player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", "5242880"); // 增加到5MB
    

关于MP4播放黑屏问题,通常与start-on-prepared参数和缓冲区配置有关。可以尝试:

  • start-on-prepared设置为0,通过prepareAsync()回调后再开始播放
  • 适当增加max_cached_duration到2000000(2秒)以上
  • 确保MP4文件的编码格式与设备硬解码兼容

建议先简化配置,逐步添加优化参数,以定位具体是哪个参数组合导致了问题。

回到顶部