HarmonyOS鸿蒙Next中AVPlayer不支持直播流格式吗

HarmonyOS鸿蒙Next中AVPlayer不支持直播流格式吗 【问题描述】:使用AVPlayer解码时报错 5400106,是不支持直播流格式吗

【问题现象】:Unsupported media format or codec, code: 5400106

cke_9923.png

【版本信息】:不涉及

【复现代码】:不涉及

【尝试解决方案】:不涉及


更多关于HarmonyOS鸿蒙Next中AVPlayer不支持直播流格式吗的实战教程也可以访问 https://www.itying.com/category-93-b0.html

15 回复

尊敬的开发者,您好,
关于您反馈的问题,

AVPlayer 的直播流播放仅支持 HLS 和 HTTP-FLV 两种协议,您使用的裸TS流不在支持范围内,可使用三方库 ijkplayer 播放此类资源,我在本地已验证可以正常播放。
具体实现代码如下:

import { Callback } from '@ohos.base';
import { IjkMediaPlayer, InterruptEvent, InterruptHintType } from '@ohos/ijkplayer';
import { OnPreparedListener } from '@ohos/ijkplayer';
import { OnVideoSizeChangedListener } from '@ohos/ijkplayer';
import { OnCompletionListener } from '@ohos/ijkplayer';
import { OnBufferingUpdateListener } from '@ohos/ijkplayer';
import { OnErrorListener, OnTimedTextListener } from '@ohos/ijkplayer';
import { OnInfoListener } from '@ohos/ijkplayer';
import { OnSeekCompleteListener } from '@ohos/ijkplayer';
import { LogUtils } from '@ohos/ijkplayer';

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

@Entry
@Component
struct PageDemo {
  @State progressValue: number = 0;
  @State currentTime: string = '00:00';
  @State totalTime: string = '00:00';

  private  replayVisible: Visibility = Visibility.None;
  @State slideEnable: boolean = false;
  private mContext: object | undefined = undefined;
  private mFirst: boolean = true;
  private mDestroyPage: boolean = false;
  private playSpeed: string = '1f';
  @State volume: number = 1.0;
  private oldSeconds: number = 0;
  private isSeekTo: boolean = false;
  private isCurrentTime: boolean = false;
  @State videoWidth: string = '100%';
  private initAspectRatio: number = 1;
  @State videoAspectRatio: number = this.initAspectRatio;
  private videoUrl: string = '';
  private last: number = 0;
  private loadingVisible: Visibility = Visibility.None;
  private videoParentAspectRatio: number = this.initAspectRatio;
  private mIjkMediaPlayer = IjkMediaPlayer.getInstance();
  private controlPlayStatus = PlayStatus.INIT;
  private PROGRESS_MAX_VALUE: number = 100;
  private updateProgressTimer: number = 0;
  private curIndex: number = 0;
  private videoUrls: string[] = ['https://xxx.mp4'];

  aboutToAppear() {
    LogUtils.getInstance().LOGI('aboutToAppear');
    this.videoUrl = 'http://o11.163189.xyz/stream/live/typd/';
    let event: Callback<InterruptEvent> = (event) => {
      LogUtils.getInstance().LOGI(`event: ${JSON.stringify(event)}`);
      if (event.hintType === InterruptHintType.INTERRUPT_HINT_PAUSE) {
        this.pause();
      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_RESUME) {
        this.startPlayOrResumePlay();
      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_STOP) {
        this.stop();
      }
    };
    this.mIjkMediaPlayer.on('audioInterrupt', event);
  }

  aboutToDisappear() {
    // 退出
    LogUtils.getInstance().LOGI('aboutToDisappear');
    this.mDestroyPage = true;
    this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
    if (this.controlPlayStatus !== PlayStatus.INIT) {
      this.stop();
    }
    this.mIjkMediaPlayer.off('audioInterrupt');
  }

  onPageShow() {
    // 开始播放
    if (this.mContext && !this.mFirst) {
      this.startPlayOrResumePlay();
    }
  }

  onPageHide() {
    // 暂停播放
    this.pause();
  }

  xcomponentController: XComponentController = new XComponentController();

  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%')
            .width(this.videoWidth)
            .aspectRatio(this.videoAspectRatio)
        }.aspectRatio(this.videoAspectRatio)

        Image($r('app.media.startIcon'))
          .objectFit(ImageFit.Auto)
          .width('120px')
          .height('120px')
          .visibility(this.replayVisible)
          .border({ width: 0 })
          .borderStyle(BorderStyle.Dashed)
          .onClick(() => {
            this.startPlayOrResumePlay();
          })
        Image($r('app.media.startIcon'))
          .objectFit(ImageFit.Auto)
          .width('120px')
          .height('120px')
          .visibility(this.loadingVisible)
          .border({ width: 0 })
          .borderStyle(BorderStyle.Dashed)
      }.width('100%').backgroundColor('#000000').clip(true)

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Text(this.currentTime).width('100px').fontSize('20px').margin('20px');
        Slider({
          value: this.progressValue,
          min: 0,
          max: this.PROGRESS_MAX_VALUE,
          step: 1,
          style: SliderStyle.OutSet
        })
          .width('600px')
          .blockColor(Color.Blue)
          .trackColor(Color.Gray)
          .selectedColor(Color.Blue)
          .showSteps(true)
          .showTips(true)
          .enabled(this.slideEnable)
          .onChange((value: number, mode: SliderChangeMode) => {
            if (mode === 2) {
              this.isSeekTo = true;
              this.mDestroyPage = false;
              this.showLoadIng();
              LogUtils.getInstance().LOGI('slider-->seekValue start:' + value);
              let seekValue = value * (this.mIjkMediaPlayer.getDuration() / 100);
              this.seekTo(seekValue + '');
              this.setProgress();
              LogUtils.getInstance().LOGI('slider-->seekValue end:' + seekValue);
              this.isSeekTo = false;
            }
          });
        Text(this.totalTime).width('100px').fontSize('20px').margin('10px');
      }

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Button('播放')
          .onClick(() => {
            this.startPlayOrResumePlay();
          }).width('400px').height('80px').margin('15px');
        Button('暂停')
          .onClick(() => {
            this.pause();
          }).width('400px').height('80px').margin('15px');
        Button('切换')
          .onClick(() => {
            this.playNext();
          }).width('400px').height('80px').margin('15px');
      }

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Button('1倍速')
          .onClick(() => {
            if (!this.debounce()) {
              return;
            }
            this.playSpeed = '1f';
            this.mIjkMediaPlayer.setSpeed('1f');
          })
          .width('400px')
          .height('80px')
          .margin('15px')

        Button('1.5倍速')
          .onClick(() => {
            if (!this.debounce()) {
              return;
            }
            this.playSpeed = '1.5f';
            this.mIjkMediaPlayer.setSpeed('1.5f');
          })
          .width('400px')
          .height('80px')
          .margin('15px')

        Button('2 倍速')
          .onClick(() => {
            this.playSpeed = '2f';
            this.mIjkMediaPlayer.setSpeed('2f');
          })
          .width('400px')
          .height('80px')
          .margin('15px')
      }

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Text('视频音量').width('120px').fontSize('30px').margin('15px')
        Slider({
          value: this.volume,
          min: 0.0,
          max: 1.0,
          step: 0.1,
          style: SliderStyle.OutSet
        })
          .width('600px')
          .blockColor(Color.Blue)
          .trackColor(Color.Gray)
          .selectedColor(Color.Blue)
          .showSteps(true)
          .showTips(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            if (mode === SliderChangeMode.End) {
              this.volume = value;
              this.mIjkMediaPlayer.setVolume(this.volume.toString(), this.volume.toString());
            }
          })
      }
    }
  }

  // initDelayPlay
  private initDelayPlay(context: object) {
    this.mContext = context;
    let that = this;
    setTimeout(() => {
      that.startPlayOrResumePlay();
      that.mFirst = false;
    }, 300);
  }

  // startPlayOrResumePlay
  private startPlayOrResumePlay() {
    this.mDestroyPage = false;
    LogUtils.getInstance().LOGI('startPlayOrResumePlay start this.controlPlayStatus:' + this.controlPlayStatus);
    if (this.controlPlayStatus === PlayStatus.INIT) {
      this.stopProgressTask();
      this.startProgressTask();
      this.play(this.videoUrl.toString());
    }
    if (this.controlPlayStatus === PlayStatus.PAUSE) {
      this.mIjkMediaPlayer.start();
      this.setProgress();
    }
  }

  // completionNum
  private completionNum(num: number): string | number {
    if (num < 10) {
      return '0' + num;
    } else {
      return num;
    }
  }

  // stringForTime
  private stringForTime(timeMs: number): string {
    let totalSeconds: number | string = (timeMs / 1000);
    let newSeconds: number | string = totalSeconds % 60;
    let minutes: number | string = (totalSeconds / 60) % 60;
    let hours: number | string = totalSeconds / 3600;
    LogUtils.getInstance().LOGI('stringForTime hours:' + hours + ',minutes:' + minutes + ',seconds:' + newSeconds);
    hours = this.completionNum(Math.floor(Math.floor(hours * 100) / 100));
    minutes = this.completionNum(Math.floor(Math.floor(minutes * 100) / 100));
    newSeconds = Math.floor(Math.floor(newSeconds * 100) / 100);
    if (this.isCurrentTime) {
      if (this.oldSeconds < newSeconds || newSeconds === 0 || this.isSeekTo) {
        this.oldSeconds = newSeconds;
      } else {
        newSeconds = this.oldSeconds;
      }
    }
    newSeconds = this.completionNum(newSeconds);
    if (hours > 0) {
      return hours + ':' + minutes + ':' + newSeconds;
    } else {
      return minutes + ':' + newSeconds;
    }
  }

更多关于HarmonyOS鸿蒙Next中AVPlayer不支持直播流格式吗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


// setProgress
private setProgress() {
  let position = this.mIjkMediaPlayer.getCurrentPosition();
  let duration = this.mIjkMediaPlayer.getDuration();
  let pos = 0;
  if (duration > 0) {
    this.slideEnable = true;
    let curPercent = position / duration;
    pos = curPercent * 100;
    if (pos > this.PROGRESS_MAX_VALUE) {
      this.progressValue = this.PROGRESS_MAX_VALUE;
    } else {
      this.progressValue = pos;
    }
  }
  LogUtils.getInstance()
    .LOGI('setProgress position:' + position + ',duration:' + duration + ',progressValue:' + pos);
  this.totalTime = this.stringForTime(duration);
  if (position > duration) {
    position = duration;
  }
  this.isCurrentTime = true;
  this.currentTime = this.stringForTime(position);
  this.isCurrentTime = false;
}

// startProgressTask
private startProgressTask() {
  let that = this;
  this.updateProgressTimer = setInterval(() => {
    LogUtils.getInstance().LOGI('startProgressTask');
    if (!that.mDestroyPage) {
      that.setProgress();
    }
  }, 300);
}

// stopProgressTask
private stopProgressTask() {
  LogUtils.getInstance().LOGI('stopProgressTask');
  clearInterval(this.updateProgressTimer);
}

// showLoadIng
private showLoadIng() {
  this.loadingVisible = Visibility.Visible;
  this.replayVisible = Visibility.None;
}

// hideLoadIng
private hideLoadIng() {
  this.loadingVisible = Visibility.None;
  this.replayVisible = Visibility.None;
}

// showRePlay
private showRePlay() {
  this.loadingVisible = Visibility.None;
  this.replayVisible = Visibility.Visible;
}

private play(url: string) {
  let that = this;
  that.showLoadIng();
  // 设置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.setVolume(this.volume.toString(), this.volume.toString());
  // 设置视频源
  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, 'enable-accurate-seek', '1');
  // 预读数据的缓冲区大小
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'max-buffer-size', '102400');
  // 停止预读的最小帧数
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'min-frames', '100');
  // 启动预加载
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'start-on-prepared', '1');
  // 设置无缓冲,这是播放器的缓冲区,有数据就播放
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'packet-buffering', '0');
  // 跳帧处理,放CPU处理较慢时,进行跳帧处理,保证播放流程,画面和声音同步
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'framedrop', '5');
  // 最大缓冲cache是3s,有时候网络波动,会突然在短时间内收到好几秒的数据
  // 因此需要播放器丢包,才不会累积延时
  // 这个和第三个参数packet-buffering无关。
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'max_cached_duration', '3000');
  // 无限制收流
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'infbuf', '1');
  this.mIjkMediaPlayer.setOptionLong(IjkMediaPlayer.OPT_CATEGORY_PLAYER, 'infbuf', '1');
  // 屏幕常亮
  this.mIjkMediaPlayer.setScreenOnWhilePlaying(true);
  // 设置超时
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, 'timeout', '10000000');
  this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, 'connect_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, 'soundtouch', '1');
  this.mIjkMediaPlayer.setSpeed(this.playSpeed);
  let speed = this.mIjkMediaPlayer.getSpeed();
  LogUtils.getInstance().LOGI('getSpeed--' + speed);
  // 是否开启循环播放
  let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = {
    onVideoSizeChanged: (width: number, height: number, sarNum: number, sarDen: number) => {
      if (height === 0) {
        return;
      }
      const va = width / height;
      const vpa = that.videoParentAspectRatio;
      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);
      that.getVideoSize();
      that.hideLoadIng();
    }
  };
  this.mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
  let mOnPreparedListener: OnPreparedListener = {
    onPrepared: () => {
      LogUtils.getInstance().LOGI('setOnPreparedListener-->go');
    }
  };
  this.mIjkMediaPlayer.setOnPreparedListener(mOnPreparedListener);

  let mOnTimedTextListener: OnTimedTextListener = {
    onTimedText: () => {}
  };
  this.mIjkMediaPlayer.setOnTimedTextListener(mOnTimedTextListener);

  let mOnCompletionListener: OnCompletionListener = {
    onCompletion: () => {
      LogUtils.getInstance().LOGI('OnCompletionListener-->go');
      that.showRePlay();
      that.currentTime = that.stringForTime(this.mIjkMediaPlayer.getDuration());
      that.progressValue = this.PROGRESS_MAX_VALUE;
      that.slideEnable = false;
      that.stop();
    }
  };
  this.mIjkMediaPlayer.setOnCompletionListener(mOnCompletionListener);

  let mOnBufferingUpdateListener: OnBufferingUpdateListener = {
    onBufferingUpdate: (percent: number) => {
      LogUtils.getInstance().LOGI('OnBufferingUpdateListener-->go:' + percent);
      let mediaInfo = this.mIjkMediaPlayer.getMediaInfo();
      LogUtils.getInstance().LOGI('getMediaInfo---' + mediaInfo);
      let videoWidth = this.mIjkMediaPlayer.getVideoWidth();
      LogUtils.getInstance().LOGI('getVideoWidth---' + videoWidth);
      let videoHeight = this.mIjkMediaPlayer.getVideoHeight();
      LogUtils.getInstance().LOGI('getVideoHeight---' + videoHeight);
      let videoSarNum = this.mIjkMediaPlayer.getVideoSarNum();
      LogUtils.getInstance().LOGI('getVideoSarNum---' + videoSarNum);
      let videoSarDen = this.mIjkMediaPlayer.getVideoSarDen();
      LogUtils.getInstance().LOGI('getVideoSarDen---' + videoSarDen);
      let audioSessionId = this.mIjkMediaPlayer.getAudioSessionId();
      LogUtils.getInstance().LOGI('getAudioSessionId---' + audioSessionId);
      let looping = this.mIjkMediaPlayer.isLooping();
      LogUtils.getInstance().LOGI('isLooping---' + looping);
    }
  };
  this.mIjkMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
  let mOnSeekCompleteListener: OnSeekCompleteListener = {
    onSeekComplete: () => {
      LogUtils.getInstance().LOGI('OnSeekCompleteListener-->go');
      that.startPlayOrResumePlay();
    }
  };
  this.mIjkMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
  let mOnInfoListener: OnInfoListener = {
    onInfo: (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) => {
      this.stopProgressTask();
      LogUtils.getInstance().LOGI('OnErrorListener-->go:' + what + '===' + extra);
      that.hideLoadIng();
    }
  };
  this.mIjkMediaPlayer.setOnErrorListener(mOnErrorListener);
  this.mIjkMediaPlayer.setMessageListener();
  this.mIjkMediaPlayer.prepareAsync();
  this.mIjkMediaPlayer.start();
}

// video大小
private getVideoSize() {
  let videoWidth = this.mIjkMediaPlayer.getVideoWidth();
  LogUtils.getInstance().LOGI('getVideoWidth---' + videoWidth);
  let videoHeight = this.mIjkMediaPlayer.getVideoHeight();
  LogUtils.getInstance().LOGI('getVideoHeight---' + videoHeight);
}

// 暂停
private pause() {
  if (this.mIjkMediaPlayer.isPlaying()) {
    this.mIjkMediaPlayer.pause();
    this.setProgress();
    this.mDestroyPage = true;
    this.controlPlayStatus = PlayStatus.PAUSE;
  }
}

// 停止
private stop() {
  this.controlPlayStatus = PlayStatus.INIT;
  this.mIjkMediaPlayer.stop();
  this.mIjkMediaPlayer.release();
  this.stopProgressTask();
}

// 跳转
private seekTo(value: string) {
  this.mIjkMediaPlayer.seekTo(value);
}

// 播放
private playNext() {
  if (!this.debounce()) {
    return;
  }
  if (this.curIndex === this.videoUrls.length - 1) {
    this.curIndex = 0;
  } else {
    this.curIndex++;
  }
  this.controlPlayStatus = PlayStatus.INIT;
  this.stop();
  this.videoUrl = this.videoUrls[this.curIndex];
  this.startPlayOrResumePlay();
}

// 设置时间间隔
debounce() {
  const delay = 600;
  let cur = new Date().getTime();
  if (cur - this.last > delay) {
    this.last = cur;
    return true;
  }
  return false;
}

AVPlayer 不是完全不支持直播流。官方 AVPlayer 文档中,url 支持 http://、https:// 网络播放路径和 HLS 网络播放路径;视频容器也写明支持 mp4、mpeg-ts、mkv。文档还专门提到直播场景下 duration 和 currentTime 默认返回 -1、直播场景不支持 loop。

你遇到的 5400106 在 Media 错误码里是 Unsupport format,所以更像是当前流的容器、编码、封装、鉴权、重定向或 MIME 不被当前播放链路支持。建议先确认:是否声明 ohos.permission.INTERNET;URL 是否可直接访问;流是否 HLS 或受支持容器;音视频编码是否设备支持;如果是 FLV、RTMP 或特殊封装,需要提供实际流信息再判断。

依据:

AVPlayer 官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-media-avplayer

Media 错误码:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/errorcode-media

开发者您好,AVPlayer是无法支持部分直播流格式的

5400106 不支持的规格:

错误信息

Unsupported format.

错误描述

不支持的规格。

可能原因

不支持的文件或者格式。

处理步骤

当前使用的格式规格不支持,用户需要切换为支持的规格。

支持的规格请根据实际使用的模块功能,参考Media Kit简介中对应模块的规格介绍。

解决方案
尊敬的开发者,您好,
关于您反馈的问题,参考以下:

AVPlayer只支持主流的容器格式与音视频编码规格。如果播放的媒体资源的规格格式不在AVPlayer支持的范围内,调用prepare准备播放时会报错5400106。
从错误日志信息中可以看到“unsupport container format type”,说明当前播放的媒体资源的音视频封装编码格式不在AVPlayer支持的范围内,导致播放出错。
可以通过工具(如:ffprobe、mediaInfo等)查看媒体资源的封装格式与音视频轨道的编码格式,并检查是否为AVPlayer支持的格式。若当前设置的媒体资源的封装编码格式为不支持的格式,需要更换媒体资源。AVPlayer支持的音视频格式可参考:支持的格式与协议

背景知识
AVPlayer的主要工作是将音视频媒体资源转码为可供渲染的图像和可听见的音频模拟信号,并通过输出设备进行播放。应用只需要提供流媒体来源,不负责数据解析和解码就可达成播放效果。
5400106-不支持的规格:当前播放的媒体资源的格式规格不支持,用户需要切换为支持的规格。

有“TS流”相关的方案嘛

老师,请问下,这种裸TS流里的音频字幕轨道是按照这种方式显示吗?

// 获取媒体流信息(包含音视频轨道列表)
const mediaInfo: object = mIjkMediaPlayer.getMediaInfo();
console.info(`媒体信息:${JSON.stringify(mediaInfo)}`);

// 切换到指定轨道(trackId 从 getMediaInfo 中获取)
mIjkMediaPlayer.selectTrack("1");    // 选择轨道 ID 为 1

// 取消选中当前轨道
mIjkMediaPlayer.deselectTrack("1");

是支持直播流的,但:

不是“所有直播流”都支持

你这个:

5400106 = Unsupported Format

本质上是:

流媒体的封装格式 / 编码格式 / 协议
不被 AVPlayer 当前能力支持

不是单纯“直播不能播”。

———————— 你日志里最关键的是:

unsupported container format type

说明:

AVPlayer 连流的容器格式都没识别成功

———————— 你当前 URL:

http://o11.163189.xyz/stream/live/typd/

看起来像:

  • 自定义直播源
  • IPTV
  • m3u8中转
  • ts流
  • flv直播
  • 或私有协议

而 HarmonyOS AVPlayer:

目前官方明确支持的是:

官方支持

视频:

mp4
mpeg-ts
webm
mkv

网络流:

http
https
hls(m3u8)

官方文档也只明确提到了:

HLS 网络播放

没有提:

  • flv live
  • rtmp
  • rtsp
  • fmp4 low latency
  • 自定义 IPTV ts push

———————— 很多人会误以为:

Android ExoPlayer 能播
= HarmonyOS AVPlayer 也能播

实际上:

HarmonyOS AVPlayer 对直播流严格很多

尤其:

最常见不支持的:

  • FLV直播流
  • RTMP
  • 某些 TS Push
  • 非标准 m3u8
  • HEVC直播
  • AAC配置异常
  • 缺少 metadata
  • chunked ts
  • 某些 IPTV 源

———————— 尤其你这个报错:

CONTAINER_ERR-null-unsupport interface

非常像:

流根本不是标准 HLS

或者:

返回头 Content-Type 不正确

———————— 建议你先验证几个东西。

1、浏览器打开 URL

看看:

到底返回什么

很多 IPTV 接口:

实际上返回:

  • 302
  • html
  • token鉴权失败
  • json
  • m3u

而不是视频流。

————————

2、确认是不是 m3u8

真正能被 HarmonyOS AVPlayer 稳定支持的:

标准 HLS m3u8

例如:

#EXTM3U
#EXT-X-VERSION:3

这种。

————————

3、确认编码

推荐:

视频:

H264 AVC

音频:

AAC

不要:

  • HEVC
  • AV1
  • Opus
  • EAC3

很多直播流会踩坑。

————————

4、不要直接播 flv

HarmonyOS AVPlayer:

基本不支持 FLV Live

Android 那边很多播放器支持:

是因为:

ExoPlayer/IjkPlayer/FFmpeg
自己扩展了解码

不是系统原生支持。

————————

5、推荐方案

如果你是直播 APP:

建议:

方案A(推荐)

服务端统一转:

HLS(m3u8) + H264 + AAC

这是 HarmonyOS 最稳方案。

————————

方案B

直接上:

FFmpeg + ijkplayer

Native 解码。

很多直播项目最终都会走这个。

因为:

AVPlayer 对直播协议兼容性一般

————————

6、还有一个坑

HarmonyOS AVPlayer:

对:

非标准 m3u8

容忍度远低于 Android/iOS。

很多:

  • VLC能播
  • PotPlayer能播
  • ExoPlayer能播

的流:

HarmonyOS 不一定能播。

———————— 简单总结:

  • AVPlayer 支持直播
  • 但主要支持标准 HLS(m3u8)
  • 5400106 本质是格式/容器不支持
  • 你这个更像非标准直播流
  • FLV/RTMP/IPTV TS 流大概率不支持
  • 推荐统一转 HLS + H264 + AAC
  • 复杂直播建议 FFmpeg/ijkplayer

你好,AVPlayer支持的音视频格式可参考:支持的格式与协议

解决参考AVPlayer播放音视频报错5400106,常见的不支持原因有:

1、封装和编码格式不支持

2、视频分辨率不支持

3、音频采样率不支持

对于5400106错误,不支持的规格问题,需要用户切换为支持规格的媒体资源后使用AVPlayer播放。

cke_181.png

关于这个,参考下《AVPlayer播放音视频报错5400106》
支持的格式参考《Media Kit支持的格式与协议》
可以贴下你直播流格式。或者到《组件库》瞅瞅。

看看是不是视频格式不对,尝试转码适配下。

cke_135.png

提示有两个错误代码,查询文档代码意思如下:

cke_2167.png

5400102 当前状态不支持此操作

错误信息

Operation not allowed.

错误描述

当前操作不允许。

可能原因

当前状态不支持此操作。

处理步骤

确认当前状态是否支持当前操作,把实例切换到正确的状态进行正确的操作。

5400106 不支持的规格

错误信息

Unsupported format.

错误描述

不支持的规格。

可能原因

不支持的文件或者格式。

处理步骤

当前使用的格式规格不支持,用户需要切换为支持的规格。

支持的规格请根据实际使用的模块功能,参考Media Kit简介中对应模块的规格介绍。

开发者您好,是支持的,详见文档:

使用AVPlayer播放流媒体(ArkTS)

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/streaming-media-playback-development-guide

AVPlayer 在 HarmonyOS Next 中支持直播流格式,包括 HLS(m3u8)、RTMP 及 HTTP-FLV。需确保直播源封装格式符合要求(如 HLS 的 TS 或 fMP4 分段),且网络协议栈已正确配置。

错误码 5400106 表示媒体格式或编解码器不被支持。HarmonyOS Next 的 AVPlayer 当前原生支持 HLS(HTTP Live Streaming)直播流,但暂不支持 RTMP、RTSP 等常见直播协议。若您使用了非 HLS 的直播流,就会触发该错误。请检查您的直播地址,确认是否为 .m3u8 形式的 HLS 流,且内部音视频编码建议使用 H.264 和 AAC,这些是被广泛支持的格式。如果源流为 RTMP 等,需要先在服务端转换为 HLS 或使用支持对应协议的三方播放库。简言之,不是完全不支持直播,而是仅支持 HLS 直播,协议和编码不匹配会导致 5400106。

回到顶部