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

TypeScript客户端预获取音视频时长:仅MP4有效求兼容方案

嘿,这个问题我之前做项目时也踩过坑!用<video>标签直接取duration确实只能处理浏览器原生支持的编码格式(比如MP4的H.264编码),碰到MOV、FLV或者一些小众音频格式就歇菜了。下面给你分享几个靠谱的解决方案,覆盖不同场景需求:

方法一:针对音频文件的AudioContext方案

如果你的需求里包含大量音频文件(MP3、WAV、OGG等),可以用浏览器的AudioContext来解析,它对音频格式的支持比<audio>标签更全面,而且能拿到精确的时长:

async function getAudioDuration(file: File): Promise<number> {
  return new Promise((resolve, reject) => {
    // 兼容老版浏览器的前缀
    const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
    const fileReader = new FileReader();

    fileReader.onload = (event) => {
      audioContext.decodeAudioData(event.target?.result as ArrayBuffer)
        .then(audioBuffer => {
          resolve(audioBuffer.duration);
          audioContext.close(); // 及时关闭上下文,释放资源
        })
        .catch(err => {
          reject(new Error(`解析音频失败:${err.message}`));
          audioContext.close();
        });
    };

    fileReader.onerror = () => reject(new Error('读取音频文件时发生错误'));
    fileReader.readAsArrayBuffer(file);
  });
}
方法二:兼容音视频元素的事件监听方案

你之前的代码问题大概率是没等媒体元数据加载完成就取了duration!浏览器需要先加载媒体的元数据才能拿到时长,而且针对音频文件应该用<audio>标签,而不是硬套<video>。这个方法能覆盖浏览器原生支持的所有音视频格式:

async function getMediaDuration(file: File): Promise<number> {
  return new Promise((resolve, reject) => {
    let mediaElement: HTMLVideoElement | HTMLAudioElement;

    // 根据文件MIME类型选择对应元素
    if (file.type.startsWith('video/')) {
      mediaElement = document.createElement('video');
    } else if (file.type.startsWith('audio/')) {
      mediaElement = document.createElement('audio');
    } else {
      reject(new Error('仅支持音视频文件'));
      return;
    }

    mediaElement.preload = 'metadata'; // 只加载元数据,不下载整个文件
    const objectURL = URL.createObjectURL(file);
    mediaElement.src = objectURL;

    // 监听元数据加载完成事件
    mediaElement.onloadedmetadata = () => {
      const duration = mediaElement.duration;
      URL.revokeObjectURL(objectURL); // 释放URL对象,避免内存泄漏
      resolve(duration);
    };

    // 处理格式不支持的情况
    mediaElement.onerror = () => {
      URL.revokeObjectURL(objectURL);
      reject(new Error(`浏览器不支持解析${file.type}格式的媒体文件`));
    };
  });
}
方法三:FFmpeg.wasm 全格式解析(终极方案)

如果需要支持浏览器原生不兼容的格式(比如MKV、FLV、MOV等),那就得上FFmpeg.wasm了——它在客户端用WebAssembly运行FFmpeg,几乎能解析所有主流音视频格式,缺点是包体积稍大,适合对格式支持要求高的场景:

首先安装依赖:

npm install @ffmpeg/ffmpeg @ffmpeg/core

然后编写TypeScript代码:

import { FFmpeg } from '@ffmpeg/ffmpeg';

async function getFFmpegMediaDuration(file: File): Promise<number> {
  const ffmpeg = new FFmpeg();

  // 加载FFmpeg核心,建议把core文件下载到本地部署,提升加载速度
  await ffmpeg.load({
    coreURL: 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/ffmpeg-core.js',
  });

  // 将用户上传的文件写入FFmpeg的虚拟文件系统
  await ffmpeg.writeFile(file.name, new Uint8Array(await file.arrayBuffer()));

  // 执行ffprobe命令提取时长信息
  const execOutput = await ffmpeg.exec([
    '-v', 'error',
    '-show_entries', 'format=duration',
    '-of', 'default=noprint_wrappers=1:nokey=1',
    file.name
  ]);

  // 解析输出的时长字符串,转成数字
  const durationStr = execOutput.join('').trim();
  const duration = parseFloat(durationStr);

  if (isNaN(duration)) {
    throw new Error('无法解析该媒体文件的时长');
  }

  // 清理资源
  await ffmpeg.deleteFile(file.name);
  ffmpeg.terminate();

  return duration;
}

场景选择建议

  • 只需处理浏览器原生支持的音视频格式:用方法二,简单高效,无额外依赖;
  • 重点处理音频文件:结合方法一+方法二,覆盖更多音频编码;
  • 需要全格式支持:用方法三,虽然有包体积成本,但能解决绝大多数格式兼容问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:22:45