Explorar o código

优化ios 摄像头播放

yawuga hai 1 mes
pai
achega
2c6ea0e553
Modificáronse 1 ficheiros con 238 adicións e 62 borrados
  1. 238 62
      pages/trace/detail.vue

+ 238 - 62
pages/trace/detail.vue

@@ -193,7 +193,26 @@
 
 											<!-- 使用跨平台视频播放组件 -->
 											<!-- #ifdef H5 -->
-											<view v-if="isPlaying" class="h5-video-wrapper">
+											<!-- iOS H5:使用 video 直接播放 HLS(绕过 Jessibuca + wss_flv) -->
+											<video
+												v-if="isPlaying && isH5IOS"
+												id="h5IosVideo"
+												class="video-player"
+												:src="getIOSH5StreamUrl"
+												:autoplay="true"
+												:controls="true"
+												:show-center-play-btn="true"
+												:enable-progress-gesture="false"
+												:object-fit="'contain'"
+												@error="onVideoError"
+												@play="onVideoPlay"
+												@pause="onVideoPause"
+												@ended="onVideoEnded"
+												@timeupdate="onTimeUpdate"
+												@fullscreenchange="onFullscreenChange"
+											></video>
+											<!-- 非 iOS H5:使用 Jessibuca + wss_flv(保持安卓/PC 原有逻辑) -->
+											<view v-else-if="isPlaying && !isH5IOS" class="h5-video-wrapper">
 												<Jessibuca ref="jessibucaRef" :videoUrl="getH5StreamUrl" :hasAudio="true" @error="onVideoError" />
 											</view>
 											<!-- #endif -->
@@ -262,7 +281,7 @@
 
 													<view class="control-row bottom-controls">
 														<!-- 只在播放时显示时间码 -->
-														<view v-if="isPlaying" class="video-time">{{ currentTime }}</view>
+														<view v-if="isPlaying && !isH5IOS" class="video-time">{{ currentTime }}</view>
 													</view>
 												</view>
 
@@ -600,6 +619,7 @@ const isFullscreen = ref(false)
 const isPlaying = ref(false)
 const livePlayerContext = ref(null) // 小程序视频上下文
 const appLivePlayerContext = ref(null) // App端视频上下文
+const h5IosVideoContext = ref(null) // H5 iOS video 上下文
 const jessibucaRef = ref(null)
 
 const isMuted = ref(false)
@@ -608,6 +628,44 @@ const isVoiceActive = ref(false)
 const isGridView = ref(false)
 const isZoomMode = ref(false)
 const currentTime = ref('14:30:25')
+
+// ===== 平台识别:iPhone / iOS 设备判断 =====
+const isIOSDevice = computed(() => {
+	// #ifdef H5
+	const ua = navigator.userAgent.toLowerCase()
+	return /iphone|ipad|ipod/.test(ua)
+	// #endif
+	// #ifdef APP-PLUS
+	const platform = uni.getSystemInfoSync().platform
+	return platform === 'ios'
+	// #endif
+	return false
+})
+
+const isIPhoneH5 = computed(() => {
+	// #ifdef H5
+	const ua = navigator.userAgent.toLowerCase()
+	return /iphone/.test(ua) && !/ipad|ipod/.test(ua)
+	// #endif
+	return false
+})
+
+const isIPadH5 = computed(() => {
+	// #ifdef H5
+	const ua = navigator.userAgent.toLowerCase()
+	return /ipad/.test(ua)
+	// #endif
+	return false
+})
+
+// H5 iOS 设备(包括 iPhone 和 iPad)
+const isH5IOS = computed(() => {
+	// #ifdef H5
+	const ua = navigator.userAgent.toLowerCase()
+	return /iphone|ipad|ipod/.test(ua)
+	// #endif
+	return false
+})
 // 响应式数据
 const deviceInfo = reactive({
 	deviceId: '34020000001110000001',
@@ -619,7 +677,11 @@ const deviceInfo = reactive({
 	deviceTypeId: null,
 	streamUrl: '',
 	channelId: null, // 当前通道ID
-	originalStreamUrl: '',
+	// ===== 流地址:分格式保存,不再互相覆盖 =====
+	originalStreamUrl: '',    // 兼容旧逻辑
+	wsFlvStreamUrl: '',       // wss_flv 流地址(安卓 H5 用)
+	hlsStreamUrl: '',         // hls 流地址(iOS H5 / 小程序 / App iOS 用)
+	fmp4StreamUrl: '',        // fmp4 流地址(App Android 用)
 })
 const MOCK_TRACE_DETAILS = {
 	normal: {
@@ -902,16 +964,34 @@ reportPending: {
 		document.head.appendChild(script)
 		// #endif
 	}
-// 获取H5环境使用的流地址
+// 获取H5环境使用的流地址(非 iOS H5 用 wss_flv)
 	const getH5StreamUrl = computed(() => {
 		// 确保使用安全的 WSS 协议
-		let url = deviceInfo.originalStreamUrl
+		let url = deviceInfo.wsFlvStreamUrl
 		if (url && url.startsWith('ws://')) {
 			console.warn('检测到不安全的 ws:// 协议,自动转换为 wss://')
 			url = url.replace('ws://', 'wss://')
 		}
 		return url
 	})
+
+	// ===== iOS H5 专用流地址(直接用 HLS,不走 Jessibuca) =====
+	const getIOSH5StreamUrl = computed(() => {
+		// iOS H5 优先使用 HLS 地址
+		const hlsUrl = deviceInfo.hlsStreamUrl
+		console.log('========== [诊断] iOS H5 流地址 ==========')
+		console.log('hlsStreamUrl:', hlsUrl)
+		return hlsUrl || ''
+	})
+
+	// 当前 H5 实际使用的流地址(根据设备类型选择)
+	const getCurrentH5StreamUrl = computed(() => {
+		if (isH5IOS.value) {
+			return getIOSH5StreamUrl.value
+		} else {
+			return getH5StreamUrl.value
+		}
+	})
 function resolveStateKey(opts) {
 	const raw = (opts?.state || opts?.batchState || opts?.scene || '').toString().trim()
 	if (!raw) return 'normal'
@@ -948,10 +1028,15 @@ onLoad((opts) => {
 	
 	// #ifdef H5
 	loadJessibucaScript()
+	setTimeout(() => {
+		h5IosVideoContext.value = uni.createVideoContext('h5IosVideo')
+		console.log('H5 iOS 视频上下文已创建')
+	}, 500)
 	// #endif
 })
 // 根据设备id获取通道列表
 	const queryChannels = () => {
+		console.log('========== [诊断] queryChannels 被调用 ==========')
 		getChannels(deviceInfo.deviceId)
 			.then(response => {
 				console.log('获取通道列表:', response)
@@ -960,6 +1045,15 @@ onLoad((opts) => {
 					const channels = res.data.list
 					deviceInfo.channelId = channels[0].deviceId
 					deviceInfo.status = channels[0].status
+					console.log('通道信息:', { channelId: deviceInfo.channelId, status: deviceInfo.status })
+
+					// ===== 诊断日志:调用 playStart 前 =====
+					console.log('========== [诊断] 准备调用 playStart ==========')
+					console.log('deviceId:', deviceInfo.deviceId)
+					console.log('channelId:', deviceInfo.channelId)
+					console.log('isH5IOS:', isH5IOS.value)
+					console.log('isIPhoneH5:', isIPhoneH5.value)
+
 					playStart(deviceInfo.deviceId, deviceInfo.channelId).then(res => {
 						if (res.data.code !== 0) {
 							console.error('播放开始失败:', res.message)
@@ -969,32 +1063,65 @@ onLoad((opts) => {
 							})
 							return
 						}
-						console.log('播放开始:', res)
-						
+						console.log('playStart 返回数据:', res.data.data)
+						console.log('playStart 可用流地址:', Object.keys(res.data.data || {}))
+
+						// ===== 诊断日志:playStart 成功,保存各格式流地址 =====
+						console.log('========== [诊断] 保存各格式流地址 ==========')
+
 						// #ifdef H5
-						let streamUrl = res.data.data.wss_flv
-						
-						if (streamUrl) {
-							const urlObj = new URL(streamUrl)
-							// 替换 hostname
+						// H5 环境:分别保存 wss_flv 和 hls
+						const rawWsFlv = res.data.data.wss_flv
+						const rawHls = res.data.data.hls
+
+						// 非 iOS H5:走 wss_flv + Jessibuca,保留 URL 替换逻辑
+						if (!isH5IOS.value && rawWsFlv) {
+							const urlObj = new URL(rawWsFlv)
 							urlObj.hostname = 'nxy.gbdfarm.com'
-							// 替换端口
 							urlObj.port = '9000'
-							
-							deviceInfo.originalStreamUrl = urlObj.toString()
-							console.log("queryChannels - deviceInfo.originalStreamUrl", deviceInfo.originalStreamUrl)
-						} else {
-							console.warn('未获取到 wss_flv 流地址')
+							deviceInfo.wsFlvStreamUrl = urlObj.toString()
+							console.log('H5 非iOS - wsFlvStreamUrl:', deviceInfo.wsFlvStreamUrl)
+						} else if (!isH5IOS.value) {
+							console.warn('H5 非iOS - 未获取到 wss_flv')
+						}
+
+						// iOS H5:直接用 HLS,不做 URL 替换
+						if (isH5IOS.value && rawHls) {
+							deviceInfo.hlsStreamUrl = rawHls  // 直接使用原始 HLS 地址,不替换域名
+							console.log('H5 iOS - hlsStreamUrl (不替换域名):', deviceInfo.hlsStreamUrl)
+						} else if (isH5IOS.value) {
+							console.warn('H5 iOS - 未获取到 hls,使用 wss_flv 兜底')
+							// iOS 没有 HLS 时,尝试用 wss_flv(可能也会黑屏,但至少有尝试)
+							deviceInfo.hlsStreamUrl = rawWsFlv || ''
 						}
+
+						// 兼容旧逻辑
+						deviceInfo.originalStreamUrl = isH5IOS.value
+							? deviceInfo.hlsStreamUrl
+							: deviceInfo.wsFlvStreamUrl
+						console.log('H5 - originalStreamUrl (兼容):', deviceInfo.originalStreamUrl)
 						// #endif
-						
+
 						// #ifdef MP-WEIXIN
-						deviceInfo.originalStreamUrl = res.data.data.hls || deviceInfo.originalStreamUrl
+						deviceInfo.hlsStreamUrl = res.data.data.hls || deviceInfo.hlsStreamUrl
+						deviceInfo.originalStreamUrl = deviceInfo.hlsStreamUrl
+						console.log("queryChannels - 小程序 hlsStreamUrl:", deviceInfo.hlsStreamUrl)
 						// #endif
-						
+
 						// #ifdef APP-PLUS || APP-HARMONY
-						deviceInfo.originalStreamUrl = res.data.data.fmp4 || deviceInfo.originalStreamUrl
+						deviceInfo.fmp4StreamUrl = res.data.data.fmp4 || deviceInfo.fmp4StreamUrl
+						deviceInfo.hlsStreamUrl = res.data.data.hls || deviceInfo.hlsStreamUrl
+						deviceInfo.originalStreamUrl = deviceInfo.fmp4StreamUrl
+						console.log("queryChannels - App fmp4StreamUrl:", deviceInfo.fmp4StreamUrl)
+						console.log("queryChannels - App hlsStreamUrl:", deviceInfo.hlsStreamUrl)
 						// #endif
+
+						// ===== 最终诊断日志 =====
+						console.log('========== [诊断] 流地址保存完成 ==========')
+						console.log('wsFlvStreamUrl:', deviceInfo.wsFlvStreamUrl)
+						console.log('hlsStreamUrl:', deviceInfo.hlsStreamUrl)
+						console.log('fmp4StreamUrl:', deviceInfo.fmp4StreamUrl)
+						console.log('originalStreamUrl (兼容):', deviceInfo.originalStreamUrl)
 					}).catch(err => {
 						console.error('播放开始失败:', err)
 					})
@@ -1028,13 +1155,16 @@ const loadData = async (batchId) => {
 }
 // 视频播放错误处理
 	const onVideoError = (e) => {
+		console.error('========== [诊断] 视频播放错误 ==========')
 		console.error('视频播放错误:', e)
-		
+
 		let errorMsg = '视频加载失败'
-		
+
 		// 微信小程序video组件错误码
 		if (e && e.detail) {
 			const errCode = e.detail.errCode
+			console.error('微信错误码:', errCode)
+			console.error('微信错误信息:', e.detail.errMsg)
 			switch(errCode) {
 				case 10001:
 					errorMsg = '网络错误,请检查网络连接'
@@ -1051,9 +1181,16 @@ const loadData = async (batchId) => {
 				default:
 					errorMsg = `播放失败(错误码:${errCode})`
 			}
-			console.error('视频错误详情:', e.detail)
 		}
-		
+
+		// ===== 诊断日志:当前环境信息 =====
+		// #ifdef H5
+		console.log('========== [诊断] H5 错误上下文 ==========')
+		console.log('isH5IOS:', isH5IOS.value)
+		console.log('getH5StreamUrl (非iOS):', getH5StreamUrl.value)
+		console.log('getIOSH5StreamUrl (iOS):', getIOSH5StreamUrl.value)
+		// #endif
+
 		uni.showToast({
 			title: errorMsg,
 			icon: 'none',
@@ -1063,12 +1200,12 @@ const loadData = async (batchId) => {
 		isPlaying.value = false
 
 		// #ifdef H5
+		// 只对非 iOS H5 做 Jessibuca 重试
 		setTimeout(() => {
-			if (isPlaying.value && jessibucaRef.value) {
+			if (isPlaying.value && !isH5IOS.value && jessibucaRef.value) {
 				console.log('尝试重新加载视频流')
-				if (deviceInfo.originalStreamUrl !== config.streamServer.wsFlvServer) {
-					deviceInfo.streamUrl = config.streamServer.wsFlvServer
-				}
+			} else if (isH5IOS.value) {
+				console.log('【诊断】iOS H5 播放失败,hlsStreamUrl:', deviceInfo.hlsStreamUrl)
 			}
 		}, 3000)
 		// #endif
@@ -1086,10 +1223,31 @@ const loadData = async (batchId) => {
 	}
 // 播放/暂停切换
 	const togglePlayState = () => {
+		console.log('========== [诊断] togglePlayState 被调用 ==========')
+		console.log('当前 isPlaying:', isPlaying.value)
+		console.log('isH5IOS:', isH5IOS.value)
+
 		if (!isPlaying.value) {
+			// 开始播放
 			isPlaying.value = true
-			
+
+			// ===== 诊断日志:播放链路选择 =====
+			// #ifdef H5
+			if (isH5IOS.value) {
+				console.log('========== [诊断] H5 iOS 播放链路 ==========')
+				console.log('使用 iOS H5 专用流地址')
+				console.log('getIOSH5StreamUrl:', getIOSH5StreamUrl.value)
+			} else {
+				console.log('========== [诊断] H5 非iOS 播放链路 ==========')
+				console.log('使用 Jessibuca + wss_flv')
+				console.log('getH5StreamUrl:', getH5StreamUrl.value)
+			}
+			// #endif
+
 			// #ifdef MP-WEIXIN
+			console.log('========== [诊断] 微信小程序播放链路 ==========')
+			console.log('使用 video + hls')
+			console.log('getAppStreamUrl:', getAppStreamUrl.value)
 			setTimeout(() => {
 				if (livePlayerContext.value) {
 					livePlayerContext.value.play()
@@ -1097,8 +1255,11 @@ const loadData = async (batchId) => {
 				}
 			}, 300)
 			// #endif
-		
+
 			// #ifdef APP-PLUS || APP-HARMONY
+			console.log('========== [诊断] App 播放链路 ==========')
+			console.log('使用 video + fmp4/hls')
+			console.log('getAppStreamUrl:', getAppStreamUrl.value)
 			setTimeout(() => {
 				if (appLivePlayerContext.value) {
 					appLivePlayerContext.value.play()
@@ -1108,36 +1269,31 @@ const loadData = async (batchId) => {
 			// #endif
 
 			// #ifdef H5
-			setTimeout(() => {
-				playStart(deviceInfo.deviceId, deviceInfo.channelId).then(res => {
-					if (res.data.code !== 0) {
-						console.error('播放开始失败:', res.message)
-						uni.showToast({
-							title: '播放失败: ' + res.message,
-							icon: 'none'
-						})
-						return
-					}
-					console.log('播放开始:', res)
-					
-					// 使用 wss_flv 并替换域名和端口
-					let streamUrl = res.data.data.wss_flv
-					if (streamUrl) {
-						const urlObj = new URL(streamUrl)
-						urlObj.hostname = 'nxy.gbdfarm.com'
-						urlObj.port = '9000'
-						deviceInfo.originalStreamUrl = urlObj.toString()
-						console.log("togglePlayState - deviceInfo.originalStreamUrl", deviceInfo.originalStreamUrl)
+			// 【最小修复】不再重复调用 playStart,直接使用 queryChannels 中已保存的流地址
+			console.log('【修复】togglePlayState 不再调用 playStart,使用已保存的流地址')
+			console.log('H5 实际播放 URL:', getCurrentH5StreamUrl.value)
+
+			// iOS H5 需要手动调用 video.play()
+			if (isH5IOS.value) {
+				setTimeout(() => {
+					if (h5IosVideoContext.value) {
+						h5IosVideoContext.value.play()
+						console.log('H5 iOS video.play() 已调用')
 					}
-				}).catch(err => {
-					console.error('播放开始失败:', err)
-				})
-				uni.vibrateShort()
-			}, 300)
+				}, 300)
+			}
+
+			uni.vibrateShort()
 			// #endif
 		} else {
 			// #ifdef H5
-			if (jessibucaRef.value) {
+			// iOS H5 的 video 暂停
+			if (isH5IOS.value && h5IosVideoContext.value) {
+				h5IosVideoContext.value.pause()
+				console.log('H5 iOS video.pause() 已调用')
+			}
+			// 非 iOS H5 的 Jessibuca 暂停
+			if (!isH5IOS.value && jessibucaRef.value) {
 				jessibucaRef.value.pause()
 			}
 			// #endif
@@ -1466,10 +1622,30 @@ const loadData = async (batchId) => {
 	}
 // 获取App(安卓/鸿蒙)环境使用的流地址
 	const getAppStreamUrl = computed(() => {
-		// 优先使用 fmp4 格式(微信小程序和App都支持)
-		if (deviceInfo.originalStreamUrl) {
-			console.log("当前视频流地址:", deviceInfo.originalStreamUrl)
-			return deviceInfo.originalStreamUrl
+		// ===== 诊断日志 =====
+		console.log('========== [诊断] getAppStreamUrl ==========')
+		console.log('fmp4StreamUrl:', deviceInfo.fmp4StreamUrl)
+		console.log('hlsStreamUrl:', deviceInfo.hlsStreamUrl)
+
+		// App iOS 优先使用 HLS
+		// #ifdef APP-PLUS
+		const platform = uni.getSystemInfoSync().platform
+		console.log('App 平台:', platform)
+		if (platform === 'ios') {
+			if (deviceInfo.hlsStreamUrl) {
+				console.log('App iOS 优先使用 hlsStreamUrl')
+				return deviceInfo.hlsStreamUrl
+			} else if (deviceInfo.fmp4StreamUrl) {
+				console.log('App iOS 无 hls,使用 fmp4 兜底')
+				return deviceInfo.fmp4StreamUrl
+			}
+		}
+		// #endif
+
+		// App Android / 鸿蒙:优先使用 fmp4
+		if (deviceInfo.fmp4StreamUrl) {
+			console.log('App 使用 fmp4StreamUrl')
+			return deviceInfo.fmp4StreamUrl
 		}
 
 		// 如果没有流地址,返回空字符串