微信小程序视频无法播放
微信小程序视频无法播放问题解析与解决方案
问题背景
在使用微信小程序的 <video>
组件播放第三方 OSS(对象存储服务)地址的视频时,开发者工具模拟器中可以正常播放,但真机上无法播放。
请求差异分析
- 模拟器请求:
- 请求头包含
Range
:bytes=0-
或bytes=655360-
- 模拟器能够正确处理分段加载视频。
- 请求头包含
- 真机请求:
- 请求头缺少
Range
或为No range
。 - 服务端无法正确处理缺失
Range
的请求,导致视频无法播放。
- 请求头缺少
真机需要 Range 请求的原因
微信小程序真机的 <video>
组件依赖 Range
请求以支持以下功能:
- 渐进式加载:分段加载视频以优化缓冲和内存管理。
- 拖拽进度条:支持用户快进、快退操作。
- 性能优化:真机资源有限,无法一次性加载整个视频。
- 用户体验:支持即时播放,无需等待整个视频下载完成。
解决方案
以下是优化微信小程序 <video>
组件播放视频的步骤:
1. 确保正确的 Content-Type
服务端响应头需返回正确的视频 MIME 类型,例如:
1 | Content-Type: video/mp4 |
- 检查服务端是否正确设置了
Content-Type
,避免使用不标准的 MIME 类型(如application/octet-stream
)。
2. 检查并优化 Response Headers
- 移除不必要的响应头(如无关的
Cache-Control
或自定义头)。 - 如果服务端不支持
Range
请求,需在后端添加对Range
请求的支持。 - 示例:为真机请求添加默认
Range
请求头(bytes=0-65535
)以获取视频元数据。
3. 验证域名和协议
- 确保视频资源使用 HTTPS 协议。
- 在小程序后台(“开发 -> 开发设置 -> 服务器域名”)中配置合法的视频资源域名。
4. 确保视频编码格式兼容
- 微信小程序对视频格式要求严格,推荐使用 H.264 编码的 MP4 格式。
- 使用 FFmpeg 转换视频格式:
1
ffmpeg -i input.mov -vcodec h264 -acodec aac output.mp4
- 验证视频是否符合小程序的编码要求,避免使用不兼容的编码格式(如 H.265)。
5. 使用 wx.downloadFile 预下载视频
通过 wx.downloadFile
API 下载视频到临时路径后播放,避免直接使用 OSS 地址可能带来的问题:
1 | wx.downloadFile({ |
在 WXML 中使用:
1 | <video src="{{videoSrc}}" controls /> |
6. 优化服务端代码(中间件)
以下是优化后的服务端代码,用于处理 Range
请求并确保真机兼容性:
import { REMOTE_API_CONFIG } from "@/config/orginone";
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
const { path } = await params;
const targetUrl = `${REMOTE_API_CONFIG.baseUrl}/${path.join("/")}`;
// 获取 Range 请求头
const range = req.headers.get("range");
console.log("Range request:", range || "No range");
// 构建请求头
const requestHeaders: HeadersInit = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1",
"Accept": "*/*",
"Accept-Encoding": "identity", // 禁用压缩以确保兼容性
};
// 为真机添加默认 Range 请求
if (!range) {
requestHeaders["Range"] = "bytes=0-65535";
console.log("Added default range for mobile: bytes=0-65535");
} else {
requestHeaders["Range"] = range;
}
// 发起请求
const resp = await fetch(targetUrl, {
headers: requestHeaders,
});
// 检查响应状态
if (!resp.ok) {
console.error(`Failed to fetch video: ${resp.status}`);
return new NextResponse("Failed to fetch video", { status: resp.status });
}
// 设置响应头
const headers = new Headers(resp.headers);
headers.set("Content-Type", "video/mp4"); // 确保正确的 MIME 类型
return new NextResponse(resp.body)
}