IOS H5页面中 HLS视频无法正常播放,使用hls.插件
HLS.js依靠 HTML5 视频和 MediaSource Extensions 进行播放。
所有 iPhone 浏览器 (iOS) 都没有可用的 MediaSourceExtension,因此Hls.js将不起作用。如果您在 iPhone 上检查 Hls.isSupported()
,您会看到该函数返回 false
。
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:
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 组件中这样使用它:
<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
事件,就像这样:
<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视频加载成功,可以播放,但是播放只有声音,画面为初始的画面,调查打断点之后,发现解决方案
<template><div class="hls-video-player"><videoclass="videoElement"ref="videoElement"autoplaymutedpreload="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) returnconst 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.hlsInstancehls.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.hlsUrlthis.$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>