| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- <template>
- <view class="video-player-container">
- <!-- H5环境使用Jessibuca -->
- <view v-if="isH5" class="h5-player">
- <view ref="jessibucaContainer" class="jessibuca-container"></view>
- <view v-if="loadError" class="error-message">
- <text>视频加载失败,请刷新重试</text>
- </view>
- </view>
-
- <!-- 小程序环境使用live-player -->
- <view v-else class="mp-player">
- <live-player
- id="videoPlayer"
- :src="videoUrl"
- mode="live"
- :autoplay="autoplay"
- :muted="isMuted"
- object-fit="contain"
- @statechange="onStateChange"
- @error="onError"
- @fullscreenchange="onFullscreenChange"
- class="live-player"
- ></live-player>
- </view>
- </view>
- </template>
- <script setup>
- import { ref, watch, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from 'vue'
- // Props
- const props = defineProps({
- videoUrl: {
- type: String,
- required: true
- },
- hasAudio: {
- type: Boolean,
- default: true
- },
- autoplay: {
- type: Boolean,
- default: true
- }
- })
- // Emits
- const emit = defineEmits(['play', 'pause', 'error', 'fullscreenchange'])
- // Template refs
- const jessibucaContainer = ref(null)
- // Reactive data
- const isH5 = ref(false)
- const isMuted = ref(false)
- const isPlaying = ref(false)
- const isFullscreen = ref(false)
- const jessibucaPlayer = ref(null)
- const livePlayerContext = ref(null)
- const loadError = ref(false)
- // Get current instance for accessing global properties
- const instance = getCurrentInstance()
- // 初始化Jessibuca播放器(H5环境)
- const initJessibucaPlayer = () => {
- if (!isH5.value || !instance?.appContext.config.globalProperties.$jessibuca) {
- console.error('H5环境或Jessibuca插件未初始化')
- loadError.value = true
- return
- }
-
- console.log('正在初始化Jessibuca播放器,URL:', props.videoUrl)
-
- // 使用Jessibuca插件
- instance.appContext.config.globalProperties.$jessibuca.createPlayer({
- container: jessibucaContainer.value,
- url: props.videoUrl,
- hasAudio: props.hasAudio,
- autoplay: props.autoplay,
- decoder: './static/js/jessibuca/decoder.js',
- wasmUrl: './static/js/jessibuca/decoder.wasm'
- }).then(player => {
- if (!player) {
- console.error('Jessibuca播放器创建失败')
- loadError.value = true
- return
- }
-
- jessibucaPlayer.value = player
- console.log('Jessibuca播放器创建成功')
-
- // 监听事件
- player.on('play', () => {
- isPlaying.value = true
- loadError.value = false
- emit('play')
- })
-
- player.on('pause', () => {
- isPlaying.value = false
- emit('pause')
- })
-
- player.on('error', (err) => {
- console.error('Jessibuca播放错误:', err)
- loadError.value = true
- emit('error', err)
- })
- }).catch(err => {
- console.error('Jessibuca播放器初始化失败:', err)
- loadError.value = true
- })
- }
- // 初始化小程序live-player
- const initLivePlayer = () => {
- if (isH5.value) return
-
- // #ifdef MP-WEIXIN
- livePlayerContext.value = uni.createLivePlayerContext('videoPlayer', instance)
- if (props.autoplay) {
- play()
- }
- // #endif
- }
- // 播放
- const play = () => {
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.play()
- } else if (livePlayerContext.value) {
- livePlayerContext.value.play({
- success: () => {
- isPlaying.value = true
- emit('play')
- },
- fail: (err) => {
- console.error('播放失败:', err)
- emit('error', err)
- }
- })
- }
- }
- // 暂停
- const pause = () => {
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.pause()
- } else if (livePlayerContext.value) {
- livePlayerContext.value.pause({
- success: () => {
- isPlaying.value = false
- emit('pause')
- }
- })
- }
- }
- // 截图
- const screenshot = () => {
- if (isH5.value && jessibucaPlayer.value) {
- return jessibucaPlayer.value.screenshot()
- } else if (livePlayerContext.value) {
- return new Promise((resolve, reject) => {
- livePlayerContext.value.snapshot({
- success: (res) => {
- resolve(res.tempImagePath)
- },
- fail: (err) => {
- reject(err)
- }
- })
- })
- }
- }
- // 静音
- const mute = () => {
- isMuted.value = true
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.mute()
- }
- }
- // 取消静音
- const cancelMute = () => {
- isMuted.value = false
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.cancelMute()
- }
- }
- // 全屏
- const fullscreenSwich = () => {
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.fullscreen()
- } else if (livePlayerContext.value) {
- if (isFullscreen.value) {
- livePlayerContext.value.exitFullScreen()
- } else {
- livePlayerContext.value.requestFullScreen({
- direction: 90
- })
- }
- }
- }
- // 重置大小
- const resize = () => {
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.resize()
- }
- }
- // 检查是否全屏
- const isFullscreenCheck = () => {
- return isFullscreen.value
- }
- // live-player状态变化
- const onStateChange = (e) => {
- console.log('播放器状态变化:', e.detail)
- const state = e.detail.code
- if (state === 2003) { // 播放中
- isPlaying.value = true
- emit('play')
- } else if (state === 2004) { // 暂停
- isPlaying.value = false
- emit('pause')
- }
- }
- // live-player错误
- const onError = (e) => {
- console.error('播放器错误:', e.detail)
- emit('error', e.detail)
- }
- // 全屏状态变化
- const onFullscreenChange = (e) => {
- isFullscreen.value = e.detail.fullScreen
- emit('fullscreenchange', isFullscreen.value)
- }
- // 销毁播放器
- const destroyPlayer = () => {
- if (isH5.value && jessibucaPlayer.value) {
- jessibucaPlayer.value.destroy()
- jessibucaPlayer.value = null
- }
- }
- // Watch videoUrl changes
- watch(() => props.videoUrl, () => {
- if (isH5.value && jessibucaPlayer.value) {
- destroyPlayer()
- nextTick(() => {
- initJessibucaPlayer()
- })
- }
- })
- // Lifecycle hooks
- onMounted(() => {
- // 判断当前环境
- // #ifdef H5
- isH5.value = true
- nextTick(() => {
- initJessibucaPlayer()
- })
- // #endif
-
- // #ifdef MP-WEIXIN
- isH5.value = false
- initLivePlayer()
- // #endif
- })
- onBeforeUnmount(() => {
- destroyPlayer()
- })
- // Expose methods for parent component access
- defineExpose({
- play,
- pause,
- screenshot,
- mute,
- cancelMute,
- fullscreenSwich,
- resize,
- isFullscreen: isFullscreenCheck
- })
- </script>
- <style>
- .video-player-container {
- width: 100%;
- height: 100%;
- background-color: #000;
- position: relative;
- }
- .h5-player, .mp-player, .jessibuca-container, .live-player {
- width: 100%;
- height: 100%;
- }
- .error-message {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- color: #fff;
- background-color: rgba(0, 0, 0, 0.5);
- padding: 10px 20px;
- border-radius: 4px;
- }
- </style>
|