uniapp之播放hls流系列之问题整理

128 阅读5分钟

前提,公司要求在app端播放HLS协议的视频流,流有h.264h.265(HEVC)两种,并且要实现秒开播放。第一想法就是使用uniapp实现的原生video标签,官网介绍了它在APP平台是完全支持该流的播放,我确实也这么用了(省事就完了)。先说明一点,官方video播放真的很流畅,很香!!

image.png

但是问题来了,首次加载超长延迟(平均5s或以上),远远达不到秒开的要求,就被打回了 /(ㄒoㄒ)/~~ ,降低了要求控制在3s内打开。其实在这之前,PC端是有通过hls.js做过播放的,确实是秒开的,所以优化的第一个想法就是在uniapp端使用hls.js,先说怎么做吧,再说在uniapp端使用它存在的问题.

因为hls.js是需要浏览器环境的,所以我在uniapp中使用了加载本地webview的方式(当然也可以使用renderjs,这个的话就不存在webview层级太高的问题),废话不说实现如下:

// 本地html文件中
<div class="controls">
<input type="text" id="videoUrl" placeholder="输入HLS视频URL" style="flex: 1;">
<button onclick="loadVideo()">加载视频</button>
<button onclick="playVideo()">播放</button>
<button onclick="destroyVideo()">销毁</button>
</div>
<video class="video-player" id="videoPlayer" muted></video>
<div class="status">
<strong>状态:</strong> <span id="statusText">等待加载视频...</span>
</div>

//npm i hls  将hls.js放到hybrid下的静态文件夹下(不知道如何加载本地html可以去官网看),uni.web-view.js 必须要放的,跟webview通信关键文件
<script type="text/javascript" src="./js/uni.web-view.js"></script> 
<script type="text/javascript" src="./js/hls.js"></script>

<script type="text/javascript">
// 全局变量
let videoPlayer = null;
let hls = null;
let algorithmList = [];
let alarmList = [];
let fallbackImageUrl = './assets/default.png';

// 获取DOM元素
const videoElement = document.getElementById('videoPlayer');
const videoUrlInput = document.getElementById('videoUrl');

// 更新状态显示
function updateStatus(status) {
          statusTextElement.textContent = status;
}
// 初始化HLS播放器
function initHlsPlayer() {
		if (!Hls.isSupported()) {
			updateStatus('HLS.js不支持当前浏览器');
			return false;
		}

                hls = new Hls({
					debug: true,
					enableWorker: true,
					liveSyncDurationCount: 8,
					liveMaxLatencyDurationCount: 10
				});

				// 监听HLS事件
				hls.on(Hls.Events.MANIFEST_LOADED, function(event, data) {
					updateStatus('MANIFEST_LOADED');
				});

				hls.on(Hls.Events.FRAG_CHANGED, function(event, data) {
					updateStatus('FRAG_CHANGED');
				});

				hls.on(Hls.Events.ERROR, function(event, data) {
					console.log('ERROR:', data);
					updateStatus('ERROR');
					showErrorImage();
				});

				hls.on(Hls.Events.FRAG_LOADED, function(event, data) {
					updateStatus('FRAG_LOADED');
				});

				hls.on(Hls.Events.FRAG_PARSED, function(event, data) {
					updateStatus('FRAG_PARSED');
				});

				hls.on(Hls.Events.FRAG_LOAD_EMERGENCY_ABORTED, function(event, data) {
					updateStatus('FRAG_LOAD_EMERGENCY_ABORTED');
				});

				hls.on(Hls.Events.MEDIA_ATTACHED, function() {
					updateStatus('MEDIA_ATTACHED');
				});

				hls.attachMedia(videoElement);
				return true;
			}

			// 加载视频
			function loadVideo() {
				const videoUrl = videoUrlInput.value.trim();
				if (!videoUrl) {
					alert('请输入视频URL');
					return;
				}

				if (hls) {
					destroyVideo();
				}

				if (initHlsPlayer()) {
					hls.loadSource(videoUrl);
					updateStatus('正在加载视频...');
				}
			}

			// 播放视频
			function playVideo() {
				if (videoElement && hls) {
					videoElement.play().catch(error => {
						console.error('播放失败:', error);
						updateStatus('播放失败: ' + error.message);
					});
				} else {
					alert('请先加载视频');
				}
			}

			// 销毁播放器
			function destroyVideo() {
				if (hls) {
					hls.destroy();
					hls = null;
				}
			}

			// 页面可见性变化处理
			document.addEventListener('visibilitychange', function() {
				if (document.visibilityState === 'hidden') {
					if (hls) {
						destroyVideo();
					}
				} else if (document.visibilityState === 'visible') {
					// 可以在这里添加重新加载的逻辑
				}
			});

			// 页面加载完成后初始化
			window.addEventListener('load', function() {
				updateStatus('就绪');
			});
</script>
//vue文件中 使用
<web-view src="/hybrid/html/hls-video.html" @message="handleMessage" id='webview' :style="{width:'400px',height:'300rpx'}"></web-view>

在h5端确实秒开了(本来就应该是秒开哈哈),但是在真机运行报错了!!!(对于小白来说人麻了)

img_v3_02ss_fadf3173-0146-478a-9a2c-7dc57a77360g.jpg

Failed to execute 'addSourceBuffer'on 'MediaSource': The type provided ('video/mp4;codeecs=hvc1.1.6.L153.B0') is unsupported why?why?why?

大大的疑问:我手机不支持h.265解码能力?我手机这么low?不对,安卓12支持啊 详情请看 image.png webview的问题?不确定去看看,Android System Webviewimage.png 再看看, 安卓5.0以上 且 Chromium 143 版本支持 image.png 看看我手机的Android System Webview版本,原来如此!!!!!手机太low 🥲(版本太低的话也可以更新,但是是非必要,因为不可能去让用户主动更新) 0ed77b33-097b-439b-8273-8c4e051ca43f.jpeg 但其实搜索相关文章会发现 大约在 Android 5-6x WebView:Chromium 105 之后就开始支持 h.265了,发现这一问题后找了其他同事的手机(142的版本)尝试 ,确实Android System Webview版本高的是可以播放的,不会报错,而且秒开无卡顿。这期间其实还试了使用 腾讯X5浏览器内核 打自定义基座包尝试也是报错,后来发现X5内核的免费版可能基于较旧的Chromium版本(如89),同样不支持H265,所以很遗憾hls的方案就没有继续了

第二种尝试了EasyPlayer.js,在h5运行后直接pass了,高延迟+断流不稳定,真机运行后也是直接报错(其相关js在APP端出现的文件引用问题),时间有限果断放弃了(后面有时间会再尝试解决) img_v3_02sv_34cc81e8-0d5d-4964-84a6-69148a863a4g.jpg

第三种,尝试了muiplayer.js,这个使用了renderjs的方式尝试的,h5端秒开,可以播放h265,但是APP端报错(忘记截图了),大概的报错就是不让使用hls,因为使用的时候需要用到hls.js,找了半天原因,才发现APP端需要购买他们的插件才能用 😂 (¥59,但是我没买,因为还想找找其他方案,而且不确定买了之后能不能播放h265,应该需要问客服) image.png

image.png

其实我还想尝试一下使用 jessibuca.js, 感觉它更强大一点,也支持软解码硬解码,不过想播放h.265也需要付费 , 但奈何时间不够,就三天左右的时间,想了想还是自己有时间尝试尝试吧~ image.png 到后面真的有点没招,决定去插件市场看看,找了两个感觉还可以的,结果发现还是播不了H265, 太绝望了!! 最后找了我们的负责流转发服务的同事讨论了下,结果发现他也可以提供FLV的流,决定直接拿原生video尝试一下,毕竟flv的延迟就是要比hls的低,抱着这个希望试了一下,结果是2s左右就可以播放出画面! 真的失去了所有的力气和手段,兜兜转转还是回到了原点....

不过对于这方面的小白来说还是收获了很多的,专业的名词及含义、设备兼容问题的原因及各种原理等都有了一定的理解和掌握,不知道各位大佬还有没有其他的解决方案,希望可以指导指导!