|
|
@@ -0,0 +1,174 @@
|
|
|
+<template>
|
|
|
+ <div class="video-player-container">
|
|
|
+ <video
|
|
|
+ ref="videoRef"
|
|
|
+ class="video-js vjs-big-play-centered"
|
|
|
+ playsinline
|
|
|
+ webkit-playsinline
|
|
|
+ ></video>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts">
|
|
|
+import { defineComponent, onMounted, onBeforeUnmount, ref, watch } from 'vue'
|
|
|
+import videojs from 'video.js'
|
|
|
+import 'video.js/dist/video-js.css'
|
|
|
+// 使用声明文件后这样引入
|
|
|
+import videojsFlash from 'videojs-flash'
|
|
|
+videojs.registerPlugin('flash', videojsFlash)
|
|
|
+
|
|
|
+interface VideoPlayerProps {
|
|
|
+ url: string
|
|
|
+ options?: videojs.PlayerOptions
|
|
|
+}
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'VideoPlayer',
|
|
|
+ props: {
|
|
|
+ url: {
|
|
|
+ type: String,
|
|
|
+ required: true
|
|
|
+ },
|
|
|
+ options: {
|
|
|
+ type: Object as () => videojs.PlayerOptions,
|
|
|
+ default: () => ({})
|
|
|
+ }
|
|
|
+ },
|
|
|
+ setup(props: VideoPlayerProps) {
|
|
|
+ const videoRef = ref<HTMLVideoElement | null>(null)
|
|
|
+ const player = ref<videojs.Player | null>(null)
|
|
|
+
|
|
|
+ // 初始化播放器
|
|
|
+ const initPlayer = () => {
|
|
|
+ if (!videoRef.value) return
|
|
|
+
|
|
|
+ // 合并默认选项和传入的选项
|
|
|
+ const mergedOptions: videojs.PlayerOptions = {
|
|
|
+ controls: true,
|
|
|
+ fluid: true,
|
|
|
+ preload: 'auto',
|
|
|
+ techOrder: ['html5', 'flash'], // 优先使用HTML5,回退到Flash
|
|
|
+ ...props.options
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 video.js 播放器实例
|
|
|
+ player.value = videojs(videoRef.value, mergedOptions, () => {
|
|
|
+ console.log('Player is ready')
|
|
|
+ })
|
|
|
+
|
|
|
+ // 根据 URL 后缀确定视频类型
|
|
|
+ setupSource()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置视频源
|
|
|
+ const setupSource = () => {
|
|
|
+ if (!player.value) return
|
|
|
+
|
|
|
+ const url = props.url.toLowerCase()
|
|
|
+
|
|
|
+ if (isRTMPStream(url)) {
|
|
|
+ // RTMP 流媒体
|
|
|
+ setupRTMPPlayer()
|
|
|
+ } else {
|
|
|
+ // 普通视频文件 (MP4, AVI 等)
|
|
|
+ player.value.src({
|
|
|
+ src: props.url,
|
|
|
+ type: getVideoType(props.url)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否是RTMP流
|
|
|
+ const isRTMPStream = (url: string): boolean => {
|
|
|
+ return url.startsWith('rtmp://') ||
|
|
|
+ url.startsWith('rtmps://') ||
|
|
|
+ url.includes('rtmp.stream') ||
|
|
|
+ url.includes('rtmp/live')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置RTMP播放器
|
|
|
+ const setupRTMPPlayer = () => {
|
|
|
+ if (!player.value) return
|
|
|
+
|
|
|
+ // 使用Flash技术播放RTMP
|
|
|
+ player.value.src({
|
|
|
+ src: props.url,
|
|
|
+ type: 'rtmp/mp4',
|
|
|
+ withCredentials: false
|
|
|
+ })
|
|
|
+
|
|
|
+ // 备选方案:如果Flash不可用,可以尝试HLS转换
|
|
|
+ player.value.ready(() => {
|
|
|
+ player.value!.on('error', () => {
|
|
|
+ console.warn('RTMP playback failed, trying HLS fallback')
|
|
|
+ tryHLSFallback()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试HLS回退方案
|
|
|
+ const tryHLSFallback = () => {
|
|
|
+ if (!player.value) return
|
|
|
+
|
|
|
+ // 这里假设你的RTMP流有对应的HLS流
|
|
|
+ const hlsUrl = props.url
|
|
|
+ .replace('rtmp://', 'http://')
|
|
|
+ .replace('rtmps://', 'https://')
|
|
|
+ .replace('/live/', '/hls/') + '.m3u8'
|
|
|
+
|
|
|
+ player.value.src({
|
|
|
+ src: hlsUrl,
|
|
|
+ type: 'application/x-mpegURL'
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据 URL 获取视频类型
|
|
|
+ const getVideoType = (url: string): string => {
|
|
|
+ const lowerUrl = url.toLowerCase()
|
|
|
+ if (lowerUrl.endsWith('.mp4')) {
|
|
|
+ return 'video/mp4'
|
|
|
+ } else if (lowerUrl.endsWith('.avi')) {
|
|
|
+ return 'video/x-msvideo'
|
|
|
+ }
|
|
|
+ // 默认类型,让浏览器自己判断
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听 URL 变化
|
|
|
+ watch(
|
|
|
+ () => props.url,
|
|
|
+ () => {
|
|
|
+ setupSource()
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ initPlayer()
|
|
|
+ })
|
|
|
+
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ // 清理 video.js 播放器
|
|
|
+ if (player.value) {
|
|
|
+ player.value.dispose()
|
|
|
+ player.value = null
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ return {
|
|
|
+ videoRef
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.video-player-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.video-js {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+</style>
|