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 中的属性绑定类似。

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

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>
相关推荐
落日沉溺于海25 分钟前
React From表单使用Formik和yup进行校验
开发语言·前端·javascript
知识分享小能手26 分钟前
React学习教程,从入门到精通, React 新创建组件语法知识点及案例代码(11)
前端·javascript·学习·react.js·架构·前端框架·react
an__ya__32 分钟前
Vue数据响应式reactive
前端·javascript·vue.js
华仔啊1 小时前
面试都被问懵了?CSS 的 flex:1 和 flex:auto 真不是一回事!90%的人都搞错了
前端·javascript
前端康师傅1 小时前
JavaScript 函数详解
前端·javascript
葡萄城技术团队1 小时前
从基础到实战:一文吃透 JS Tuples 与 Records 的所有核心用法
javascript
@小红花2 小时前
从0到1学习Vue框架Day03
前端·javascript·vue.js·学习·ecmascript
2501_915918412 小时前
uni-app 项目 iOS 上架效率优化 从工具选择到流程改进的实战经验
android·ios·小程序·uni-app·cocoa·iphone·webview
前端与小赵2 小时前
vue3中 ref() 和 reactive() 的区别
前端·javascript·vue.js
魔云连洲2 小时前
Vue的响应式底层原理:Proxy vs defineProperty
前端·javascript·vue.js