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>