IOS H5页面中 HLS视频无法正常播放,使用hls.插件

IOS H5页面中 HLS视频无法正常播放,使用hls.插件

HLS.js依靠 HTML5 视频和 MediaSource Extensions 进行播放。

所有 iPhone 浏览器 (iOS) 都没有可用的 MediaSourceExtension,因此Hls.js将不起作用。如果您在 iPhone 上检查 Hls.isSupported() ,您会看到该函数返回 false 。

js 复制代码
  if (Hls.isSupported()) {
    var hls = new Hls();
    hls.loadSource(videoSrc);
    hls.attachMedia(video);
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = videoSrc;
  }

iOS 可以使用普通的 <video><source></video> HTML,因为 Safari 具有原生 HLS 支持。初始化 Hls.js <video> 将阻止本机 HLS 功能根本无法工作。

Hls.isSupported return true

但这个解决方案对我不起作用,因为Hls.isSupported 总是返回 true。我做了以下调整以使其正常工作:

React:

react 复制代码
import Hls from "hls.js";
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";

interface PlayerProps {
  src?: string;
  poster: string;
  onPlay(): void;
  onPlayFailed(): void;
}

export const Player = forwardRef<{}, PlayerProps>(({ src, poster, onPlay, onPlayFailed }, ref) => {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const hlsRef = useRef<Hls | null>(null);

  useEffect(() => {
    if (!src || !videoRef.current) return;
    const supportHLS = Boolean(videoRef.current.canPlayType("application/vnd.apple.mpegurl"));

    if (supportHLS) {
      videoRef.current.src = src;
      videoRef.current.play().then(onPlay).catch(onPlayFailed);
    } else {
      hlsRef.current = new Hls();
      const hls = hlsRef.current;

      hls.on(Hls.Events.MEDIA_ATTACHED, function () {
        videoRef.current!.play().then(onPlay).catch(onPlayFailed);
      });
      hls.loadSource(src);
      hls.attachMedia(videoRef.current);
      
      return () => {
          hls.destroy();
      };
    }
  }, [src]);

  useImperativeHandle(ref, () => videoRef.current!, []);

  return (
    <video className="w-full h-full object-cover" playsInline ref={videoRef} poster={poster}>
      <source src={src} type="application/x-mpegURL"></source>
    </video>
  );
});

Vue:

然后,你可以在 Vue 组件中这样使用它:

vue 复制代码
<template>  
  <video  
    class="w-full h-full object-cover"  
    playsinline  
    :poster="poster"  
    @play="onPlay"  
    @error="onPlayFailed"  
    ref="videoElement"  
  >  
    <source :src="src" type="application/x-mpegURL"></source>  
  </video>  
</template>  
  
<script>  
import Hls from 'hls.js';  
  
export default {  
  props: {  
    src: {  
      type: String,  
      default: ''  
    },  
    poster: {  
      type: String,  
      required: true  
    }  
  },  
  data() {  
    return {  
      hlsInstance: null  
    };  
  },  
  mounted() {  
    this.initializePlayer();  
  },  
  beforeDestroy() {  
    if (this.hlsInstance) {  
      this.hlsInstance.destroy();  
      this.hlsInstance = null;  
    }  
  },  
  methods: {  
    initializePlayer() {  
      if (!this.src || !this.$refs.videoElement) return;  
      const supportsHLS = this.$refs.videoElement.canPlayType('application/vnd.apple.mpegurl');  
  
      if (supportsHLS) {  
        this.$refs.videoElement.src = this.src;  
        this.$refs.videoElement.play().then(this.onPlay).catch(this.onPlayFailed);  
      } else {  
        this.hlsInstance = new Hls();  
        const hls = this.hlsInstance;  
  
        hls.on(Hls.Events.MEDIA_ATTACHED, () => {  
          this.$refs.videoElement.play().then(this.onPlay).catch(this.onPlayFailed);  
        });  
        hls.loadSource(this.src);  
        hls.attachMedia(this.$refs.videoElement);  
      }  
    },  
    onPlay() {  
      this.$emit('onPlay');  
    },  
    onPlayFailed() {  
      this.$emit('onPlayFailed');  
    }  
  },  
  watch: {  
    src() {  
      this.initializePlayer();  
    }  
  }  
};  
</script>  
  
<style scoped>  
/* Add your styles here */  
</style>

在这个 Vue 组件中,我们使用了 Vue 的生命周期钩子 mounted 来初始化播放器,并在 beforeDestroy 钩子中销毁 hls.js 实例以避免内存泄漏。data 对象用来存储 hls.js 的实例。methods 包含了初始化播放器的方法以及处理播放和播放失败的方法。

此外,我们使用 watch 选项来监听 src 属性的变化,如果它变化了,我们重新初始化播放器。

最后,我们通过 $refs 访问到 DOM 元素,这和 React 中的 useRef 类似。我们还通过 $emit 触发自定义事件,以便父组件可以监听这些事件。

注意:在模板中,我们使用 :src="src" 和 :poster="poster" 来绑定属性,这和 React 中的属性绑定类似。

确保在父组件中监听 onPlay 和 onPlayFailed 事件,就像这样:

vue 复制代码
<template>  
  <Player :src="videoSrc" :poster="videoPoster" @onPlay="handlePlay" @onPlayFailed="handlePlayFailed"></Player>  
</template>  
  
<script>  
import Player from './Player.vue';  
  
export default {  
  components: {  
    Player  
  },  
  data() {  
    return {  
      videoSrc: 'your-video-source-url',  
      videoPoster: 'your-poster-image-url'  
    };  
  },  
  methods: {  
    handlePlay() {  
      // 处理播放事件  
    },  
    handlePlayFailed() {  
      // 处理播放失败事件  
    }  
  }  
};  
</script>

画面为初始页面,视频进度会走,有声音

但使用之后,发现项目中的hls视频加载成功,可以播放,但是播放只有声音,画面为初始的画面,调查打断点之后,发现解决方案

js 复制代码
<template>
  <div class="hls-video-player">
    <video
      class="videoElement"
      ref="videoElement"
      autoplay
      muted
      preload="true"
      playsinline="true" webkit-playsinline="true"
      @loadedmetadata="onLoadedMetadata"
      controls
    >
      <source :src="hlsUrl" type="application/x-mpegURL"/>
    </video>
  </div>
</template>

<script>
import Hls from 'hls.js'
export default {
  name: 'HlsVideoPlayer',
  props: {
    hlsUrl: {
      type: String,
      required: true
    }
  },
  data () {
    return {
      hlsInstance: null
    }
  },
  mounted () {
    this.initializePlayer()
  },
  beforeDestroy () {
    if (this.hlsInstance) {
      this.hlsInstance.destroy()
      this.hlsInstance = null
    }
  },
  methods: {
    onLoadedMetadata () {
      console.log('Video metadata loaded')
    },
    initializePlayer () {
      if (!this.hlsUrl || !this.$refs.videoElement) return
      const supportsHLS = this.$refs.videoElement.canPlayType('application/vnd.apple.mpegurl')
      console.log(Hls.isSupported(), supportsHLS, 'hls.isSupported', 'supportsHLS')
      // if (supportsHLS) {
      //   this.$refs.videoElement.src = this.hlsUrl
      //   this.$refs.videoElement.play().then(this.onPlay).catch(this.onPlayFailed)
      // } else {
      //   this.hlsInstance = new Hls()
      //   const hls = this.hlsInstance
      //   hls.on(Hls.Events.MEDIA_ATTACHED, () => {
      //     this.$refs.videoElement.play().then(this.onPlay).catch(this.onPlayFailed)
      //   })
      //   hls.loadSource(this.hlsUrl)
      //   hls.attachMedia(this.$refs.videoElement)
      // }
      if (Hls.isSupported()) {
        // 如果支持 hls.js(MediaSource Extensions)
        this.hlsInstance = new Hls()
        const hls = this.hlsInstance
        hls.on(Hls.Events.MEDIA_ATTACHED, () => {
          this.$refs.videoElement.play().then(this.onPlay).catch(this.onPlayFailed)
        })
        hls.loadSource(this.hlsUrl)
        hls.attachMedia(this.$refs.videoElement)
      } else if (supportsHLS) {
        // 如果支持原生播放
        // video.src = url
        // // 自动播放
        // video.addEventListener('canplay', function () {
        //   video.play()
        // })
        this.$refs.videoElement.src = this.hlsUrl
        this.$refs.videoElement.play().then(this.onPlay).catch(this.onPlayFailed)
      }
    },
    onPlay () {
      console.log('onPlay')
      // this.$emit('onPlay')
    },
    onPlayFailed () {
      console.log('onPlayFailed')
      // this.$emit('onPlayFailed')
    }
  },
  watch: {
    hlsUrl () {
      this.initializePlayer()
    }
  }
}
</script>

<style scoped>
.hls-video-player {
  width: 100%;
  max-width: 600px; /* 或其他你需要的宽度 */
  margin: 0 auto;
  display: flex;
  margin: 0 auto;
  justify-content: center;
  align-items: center;
}
.videoElement{
  width: 100%;
}
</style>
相关推荐
voipmaker7 小时前
GB28181 对接踩坑实录:注册不上、目录不全、流卡顿,一次说清(附 2022 版变更清单)
物联网·音视频·实时音视频·信息与通信·iot·gb28181·国标gb28181
她说可以呀8 小时前
Vue插槽
前端·javascript·vue.js
qq_448011168 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
2601_958352909 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
2601_965448879 小时前
AI视频制作:做系列历史人物视频音色不搭,AI 音色克隆的方法实测
人工智能·音视频
码上观世界9 小时前
多余的一声「嗯」:Fun-CosyVoice3 zero_shot 开头伪影排查实录
音视频
传奇开心果编程9 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
manyingAi9 小时前
AI漫剧生产系统架构解析:从文本拆解到音视频合成
人工智能·音视频
FRing0310 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
兰亭妙微UI设计公司13 小时前
兰亭妙微ui设计公司分享:B端组件命名如何支持团队协作
前端·javascript·vue.js