微信小程序视频无法播放

微信小程序视频无法播放问题解析与解决方案

问题背景

在使用微信小程序的 <video> 组件播放第三方 OSS(对象存储服务)地址的视频时,开发者工具模拟器中可以正常播放,但真机上无法播放。

请求差异分析

  • 模拟器请求
    • 请求头包含 Rangebytes=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
2
3
4
5
6
7
8
9
10
11
wx.downloadFile({
url: 'https://your-domain.com/video.mp4',
success: res => {
if (res.statusCode === 200) {
this.setData({ videoSrc: res.tempFilePath });
}
},
fail: err => {
console.error('下载视频失败:', err);
}
});

在 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)
}