You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js15+FFmpeg:MKV转MP4流返回200但无法播放问题排查

问题分析与修复方案

核心问题诊断

你的代码存在几个关键问题导致视频无法正常播放:

  • Response仅返回第一块数据:ffmpeg.stdout.on("data")触发时直接resolve了Response,只返回FFmpeg输出的首个chunk,浏览器无法获取完整流数据
  • 缺少流式响应处理:Next.js API Route需要返回ReadableStream实现持续视频流传输,而非单次返回chunk
  • Video组件属性错误:control拼写应为controls,错误属性会导致播放控件失效,甚至影响播放逻辑
  • FFmpeg参数不足:当前分片配置缺少适配Web流式播放的关键参数

修复后的代码

/app/api/stream/route.ts

import { spawn } from "child_process";

export async function GET(request) {
  const mkvStream = getMKVStream();
  const ffmpeg = spawn("ffmpeg", [
    "-i", "pipe:0",
    "-f", "mp4",
    "-movflags", "frag_keyframe+empty_moov+default_base_moof",
    "-c:v", "copy",
    "-c:a", "aac",
    "-b:a", "128k",
    "pipe:1"
  ]);

  mkvStream.pipe(ffmpeg.stdin);

  // 创建可读流实现持续传输
  const stream = new ReadableStream({
    start(controller) {
      ffmpeg.stdout.on("data", (chunk) => {
        controller.enqueue(chunk);
      });

      ffmpeg.stdout.on("end", () => {
        controller.close();
      });

      ffmpeg.stderr.on("data", (data) => {
        console.error(`FFmpeg日志: ${data.toString()}`);
      });

      ffmpeg.on("error", (error) => {
        console.error(`FFmpeg进程错误: ${error}`);
        controller.error(error);
      });

      ffmpeg.on("close", (code) => {
        if (code !== 0) {
          console.error(`FFmpeg退出码: ${code}`);
          controller.error(new Error(`FFmpeg exited with code ${code}`));
        }
      });
    },
    cancel() {
      // 客户端中断请求时终止FFmpeg进程,避免资源浪费
      ffmpeg.kill();
    }
  });

  return new Response(stream, {
    headers: {
      "Content-Type": "video/mp4",
      "Cache-Control": "no-cache",
      "Transfer-Encoding": "chunked",
      "Accept-Ranges": "bytes"
    }
  });
}

/app/Video.ts

"use client";
import { useRef, useEffect } from "react";

export default function Video() {
  const vidRef = useRef(null);

  useEffect(() => {
    if (vidRef.current) {
      vidRef.current.src = "/api/stream";
    }
  }, []);

  return (
    <video
      ref={vidRef}
      controls
      autoPlay
      playsInline
      style={{ width: "100%", height: "auto" }}
    />
  );
}

关键修复点说明

  • 流式响应实现:通过ReadableStream将FFmpeg输出持续推送给浏览器,解决单次返回chunk的问题
  • FFmpeg参数优化:添加default_base_moof让每个分片独立,提升Web播放兼容性;指定音频比特率避免编码异常
  • Video组件修正:修复属性拼写错误,添加playsInline适配移动端播放逻辑
  • 资源回收处理:客户端中断请求时自动终止FFmpeg进程,避免无效资源占用

内容的提问来源于stack exchange,提问作者user28760545

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:22:34