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

无需保存视频,如何从字节数组提取时长与缩略图?

直接从视频字节数组提取时长与缩略图(无需保存文件)

完全可以借助FFmpeg实现,不需要先将字节数组保存为本地文件再读取。在Go中有两种常用方案:

方案一:使用FFmpeg的Go绑定库(如ffmpeg-go)

ffmpeg-go封装了FFmpeg的API,支持直接从io.Reader读取数据,刚好匹配你手里的fileBytes字节数组。

实现示例

package utils

import (
    "bytes"
    "fmt"
    "image"
    "image/jpeg"

    "github.com/u2takey/ffmpeg-go"
)

func ExtractDurationAndFrameFromVideo(fileBytes []byte) (float64, image.Image, error) {
    // 将字节数组转为io.Reader
    videoReader := bytes.NewReader(fileBytes)

    // 提取视频时长
    durationCtx, err := ffmpeg.ProbeReader(videoReader)
    if err != nil {
        return 0, nil, fmt.Errorf("probe video failed: %w", err)
    }
    duration, err := durationCtx.GetDuration()
    if err != nil {
        return 0, nil, fmt.Errorf("get duration failed: %w", err)
    }

    // 提取缩略图(取视频第1秒的帧)
    thumbnailBuf := &bytes.Buffer{}
    // 重置reader指针,因为ProbeReader已经读取过一次
    videoReader.Seek(0, 0)
    err = ffmpeg.Input("pipe:0", ffmpeg.KwArgs{"f": "mp4"}) // 根据实际视频格式调整f参数
        .Filter("select", ffmpeg.Args{"gte(t,1)"})
        .Output("pipe:1", ffmpeg.KwArgs{"vframes": 1, "f": "image2", "vcodec": "mjpeg"})
        .WithInput(videoReader)
        .WithOutput(thumbnailBuf)
        .Run()
    if err != nil {
        return 0, nil, fmt.Errorf("extract thumbnail failed: %w", err)
    }

    // 将JPEG字节转为image.Image
    thumbnailImg, err := jpeg.Decode(thumbnailBuf)
    if err != nil {
        return 0, nil, fmt.Errorf("decode thumbnail failed: %w", err)
    }

    return duration, thumbnailImg, nil
}

方案二:直接调用FFmpeg命令(通过管道传递数据)

如果你不想引入第三方库,可以直接用Go的os/exec包调用系统中的FFmpeg命令,通过标准输入(stdin)传入视频字节数组,从标准输出(stdout)获取结果。

实现示例

package utils

import (
    "bytes"
    "fmt"
    "image"
    "image/jpeg"
    "os/exec"
    "strconv"
    "strings"
)

func ExtractDurationAndFrameFromVideo(fileBytes []byte) (float64, image.Image, error) {
    // 提取视频时长
    durationCmd := exec.Command("ffmpeg", "-i", "pipe:0", "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0")
    durationCmd.Stdin = bytes.NewReader(fileBytes)
    durationOutput, err := durationCmd.Output()
    if err != nil {
        return 0, nil, fmt.Errorf("ffmpeg get duration failed: %w", err)
    }
    durationStr := strings.TrimSpace(string(durationOutput))
    duration, err := strconv.ParseFloat(durationStr, 64)
    if err != nil {
        return 0, nil, fmt.Errorf("parse duration failed: %w", err)
    }

    // 提取缩略图
    thumbnailCmd := exec.Command("ffmpeg", "-i", "pipe:0", "-ss", "1", "-vframes", "1", "-f", "image2pipe", "-vcodec", "mjpeg", "-")
    thumbnailCmd.Stdin = bytes.NewReader(fileBytes)
    thumbnailOutput, err := thumbnailCmd.Output()
    if err != nil {
        return 0, nil, fmt.Errorf("ffmpeg extract thumbnail failed: %w", err)
    }

    thumbnailImg, err := jpeg.Decode(bytes.NewReader(thumbnailOutput))
    if err != nil {
        return 0, nil, fmt.Errorf("decode thumbnail failed: %w", err)
    }

    return duration, thumbnailImg, nil
}

注意事项

  • 确保系统中已安装FFmpeg(方案二依赖系统FFmpeg,方案一也需要FFmpeg的动态库支持)
  • 根据实际视频格式调整FFmpeg的参数(比如-f指定的格式)
  • 处理大视频时,可优化为边接收gRPC流边向FFmpeg管道写入,无需一次性将所有字节存到fileBytes中,降低内存占用

内容的提问来源于stack exchange,提问作者Humam Husam Eldeen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 00:55:28