通过 ffmpeg 获取视频第一帧(指定时间)图片

1、VideoInfo.java 获取视频信息。读者可认真研究此方法。

复制代码
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 获取视频的信息
 * FFMPEG homepage http://ffmpeg.org/about.html
 */
public class VideoInfo
{
    //视频路径
    private String ffmpegApp;
    //视频时
    private int hours;
    //视频分
    private int minutes;
    //视频秒
    private float seconds;
    //视频width
    private int width;
    //视频height
    private int heigt;
    

    public VideoInfo() {}
    
    public VideoInfo(String ffmpegApp)
    {
        this.ffmpegApp = ffmpegApp;
    }
    
    public String toString()
    {
        return "time: " + hours + ":" + minutes + ":" + seconds + ", width = " + width + ", height= " + heigt;
    }
    
    public void getInfo(String videoFilename) throws IOException,
            InterruptedException
    {
        String tmpFile = videoFilename + ".tmp.png";
        ProcessBuilder processBuilder = new ProcessBuilder(ffmpegApp, "-y",
                "-i", videoFilename, "-vframes", "1", "-ss", "0:0:0", "-an",
                "-vcodec", "png", "-f", "rawvideo", "-s", "100*100", tmpFile);

        Process process = processBuilder.start();

        InputStream stderr = process.getErrorStream();
        InputStreamReader isr = new InputStreamReader(stderr);
        BufferedReader br = new BufferedReader(isr);
        String line;
        //打印 sb,获取更多信息。 如 bitrate、width、heigt
        StringBuffer sb = new StringBuffer();
        while ((line = br.readLine()) != null)
        {
            sb.append(line);
        }

        new File(tmpFile).delete();
        
        System.out.println("video info:\n" + sb);
        Pattern pattern = Pattern.compile("Duration: (.*?),");
        Matcher matcher = pattern.matcher(sb);
        
        if (matcher.find())
        {
            String time = matcher.group(1);
            calcTime(time);
        }

        pattern = Pattern.compile("w:\\d+ h:\\d+");
        matcher = pattern.matcher(sb);
        
        if (matcher.find())
        {
            String wh = matcher.group();
            //w:100 h:100
            String[] strs = wh.split("\\s+");
            if(strs != null && strs.length == 2)
            {
                width = Integer.parseInt(strs[0].split(":")[1]);
                heigt = Integer.parseInt(strs[1].split(":")[1]);
            }
        }
        
        process.waitFor();
        if(br != null)
            br.close();
        if(isr != null)
            isr.close();
        if(stderr != null)
            stderr.close();
    }

    private void calcTime(String timeStr)
    {
        String[] parts = timeStr.split(":");
        hours = Integer.parseInt(parts[0]);
        minutes = Integer.parseInt(parts[1]);
        seconds = Float.parseFloat(parts[2]);
    }

    public String getFfmpegApp()
    {
        return ffmpegApp;
    }

    public void setFfmpegApp(String ffmpegApp)
    {
        this.ffmpegApp = ffmpegApp;
    }

    public int getHours()
    {
        return hours;
    }

    public void setHours(int hours)
    {
        this.hours = hours;
    }

    public int getMinutes()
    {
        return minutes;
    }

    public void setMinutes(int minutes)
    {
        this.minutes = minutes;
    }

    public float getSeconds()
    {
        return seconds;
    }

    public void setSeconds(float seconds)
    {
        this.seconds = seconds;
    }

    public int getWidth()
    {
        return width;
    }

    public void setWidth(int width)
    {
        this.width = width;
    }

    public int getHeigt()
    {
        return heigt;
    }

    public void setHeigt(int heigt)
    {
        this.heigt = heigt;
    }

    public static void main(String[] args)
    {
        VideoInfo videoInfo = new VideoInfo("D:\\ffmpeg\\bin\\ffmpeg.exe");
        try
        {
            videoInfo.getInfo("f:/sun/Diana_Vickers_-_The_Boy_Who_Murdered_Love.mkv");
            System.out.println(videoInfo);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
复制代码

从此类,可以看出,我把 ffmpeg,解压在了 D:\\ffmpeg\\bin\\ffmpeg.exe 路径下,传入一个视频地址,就可以得出视频信息。

2、VideoThumbTaker.java 获取视频指定播放时间的图片

复制代码
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

/**
 * @author Bashan
 * FFMPEG homepage http://ffmpeg.org/about.html
 * By Google Get first and last thumb of a video using Java and FFMpeg
 * From http://www.codereye.com/2010/05/get-first-and-last-thumb-of-video-using.html
 */

public class VideoThumbTaker
{
    protected String ffmpegApp;

    public VideoThumbTaker(String ffmpegApp)
    {
        this.ffmpegApp = ffmpegApp;
    }

    @SuppressWarnings("unused")
    /****
     * 获取指定时间内的图片
     * @param videoFilename:视频路径
     * @param thumbFilename:图片保存路径
     * @param width:图片长
     * @param height:图片宽
     * @param hour:指定时
     * @param min:指定分
     * @param sec:指定秒
     * @throws IOException
     * @throws InterruptedException
     */
    public void getThumb(String videoFilename, String thumbFilename, int width,
            int height, int hour, int min, float sec) throws IOException,
            InterruptedException
    {
        ProcessBuilder processBuilder = new ProcessBuilder(ffmpegApp, "-y",
                "-i", videoFilename, "-vframes", "1", "-ss", hour + ":" + min
                        + ":" + sec, "-f", "mjpeg", "-s", width + "*" + height,
                "-an", thumbFilename);

        Process process = processBuilder.start();

        InputStream stderr = process.getErrorStream();
        InputStreamReader isr = new InputStreamReader(stderr);
        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null)
            ;
        process.waitFor();
        
        if(br != null)
            br.close();
        if(isr != null)
            isr.close();
        if(stderr != null)
            stderr.close();
    }

    public static void main(String[] args)
    {
        VideoThumbTaker videoThumbTaker = new VideoThumbTaker("D:\\ffmpeg\\bin\\ffmpeg.exe");
        try
        {
            videoThumbTaker.getThumb("f:/sun/Diana_Vickers_-_The_Boy_Who_Murdered_Love.mkv", "C:\\thumbTest.png",    800, 600, 0, 0, 9);
            System.out.println("over");
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
复制代码

 

3、VideoFirstThumbTaker.java 获取第一帧图片

复制代码
import java.io.IOException;

/***
 * 
 * 得到第一秒(也是第一帧)图片
 */
public class VideoFirstThumbTaker extends VideoThumbTaker
{
    public VideoFirstThumbTaker(String ffmpegApp)
    {
        super(ffmpegApp);
    }

    public void getThumb(String videoFilename, String thumbFilename, int width,
            int height) throws IOException, InterruptedException
    {
        super.getThumb(videoFilename, thumbFilename, width, height, 0, 0, 1);
    }
}
复制代码

4、VideoLastThumbTaker.java 获取最后一帧图片

复制代码
/**
 * 得到最后一秒(也是最后一帧)图片
 */

public class VideoLastThumbTaker extends VideoThumbTaker
{
    public VideoLastThumbTaker(String ffmpegApp)
    {
        super(ffmpegApp);
    }

    public void getThumb(String videoFilename, String thumbFilename, int width,
            int height) throws IOException, InterruptedException
    {
        VideoInfo videoInfo = new VideoInfo(ffmpegApp);
        videoInfo.getInfo(videoFilename);
        super.getThumb(videoFilename, thumbFilename, width, height,
                videoInfo.getHours(), videoInfo.getMinutes(),
                videoInfo.getSeconds() - 0.2f);
    }
}
复制代码
<think>我们正在使用Vue.js项目,需要利用FFmpeg提取WMV视频第一帧图像。FFmpeg是一个功能强大的多媒体处理工具,可以在命令行中使用。在Vue.js中,我们可以通过JavaScript调用FFmpeg。但是,由于FFmpeg通常是在命令行中运行,而在浏览器中不能直接调用本地命令行工具,因此我们需要在浏览器环境中运行FFmpeg。幸运的是,有一个名为ffmpeg.wasm的项目,它通过WebAssembly将FFmpeg移植到浏览器中运行。因此,解决方案是使用ffmpeg.wasm库在浏览器中处理视频。步骤:1.在Vue项目中安装ffmpeg.wasm相关的包。2.在Vue组件中引入ffmpeg并加载。3.使用ffmpeg.wasm处理上传的WMV视频文件,提取第一帧。4.将提取的图像显示或保存。具体步骤:第一步:安装ffmpeg.wasm我们可以使用@ffmpeg/ffmpeg和@ffmpeg/core这两个包。在项目目录下运行:```npminstall@ffmpeg/ffmpeg@ffmpeg/core```第二步:在Vue组件中引入并使用我们创建一个Vue组件,包含一个文件上传输入,当用户选择文件后,调用ffmpeg.wasm处理视频并提取第一帧。示例代码:```vue<template><div><inputtype="file"@change="onFileChange"accept=".wmv"/><img:src="firstFrameUrl"v-if="firstFrameUrl"alt="FirstFrame"/></div></template><script>import{createFFmpeg,fetchFile}from'@ffmpeg/ffmpeg';exportdefault{data(){return{ffmpeg:null,firstFrameUrl:null,};},asyncmounted(){//初始化FFmpegthis.ffmpeg=createFFmpeg({log:true});awaitthis.ffmpeg.load();},methods:{asynconFileChange(e){constfile=e.target.files[0];if(!file){return;}//将文件写入FFmpeg的文件系统this.ffmpeg.FS('writeFile','input.wmv',awaitfetchFile(file));//运行FFmpeg命令:提取第一帧(使用-vframes1)awaitthis.ffmpeg.run('-i','input.wmv','-vframes','1','output.jpg');//从文件系统中读取输出的图片constdata=this.ffmpeg.FS('readFile','output.jpg');//创建URL用于显示图片this.firstFrameUrl=URL.createObjectURL(newBlob([data.buffer],{type:'image/jpeg'}));//清理文件系统(可选)this.ffmpeg.FS('unlink','input.wmv');this.ffmpeg.FS('unlink','output.jpg');},},};</script>```说明:1.我们使用`createFFmpeg`创建一个FFmpeg实例,并在`mounted`钩子中加载它。2.当用户选择文件后,我们将文件写入FFmpeg的文件系统(使用`fetchFile`将文件转换成Uint8Array)。3.执行FFmpeg命令:`-iinput.wmv`指定输入文件,`-vframes1`表示只取一帧,输出文件为`output.jpg`。4.读取输出的图片文件,并转换成BlobURL,然后赋值给`img`标签的`src`。注意:ffmpeg.wasm处理大文件可能会比较慢,因为需要将整个文件加载到内存中,并且WebAssembly的处理速度可能不如本地快。另外,WMV格式可能需要额外的编解码器,但是ffmpeg.wasm默认编译的版本包含了一些常用编解码器,对于WMV可能需要确认是否支持。如果不支持,可能需要自己编译ffmpeg.wasm并包含WMV解码器。以上代码是一个基本示例,实际应用中可能需要添加错误处理和加载状态提示。引用说明:我们参考了ffmpeg.wasm的基本用法[^1],以及FFmpeg提取视频帧的基本命令[^2]。[^1]:ffmpeg.wasm官方文档:https://github.com/ffmpegwasm/ffmpeg.wasm[^2]:提取视频帧的命令参考了用户提供的引用[2]中的思路,即使用`-vframes`参数指定帧数。§§相关问题§§1.在Vue中使用ffmpeg.wasm处理大视频文件时如何优化性能?2.除了第一帧,如何提取WMV视频指定时间点的帧?3.ffmpeg.wasm支持哪些视频格式的解析?</think>在Vue.js中使用FFmpeg提取WMV视频第一帧图像,可通过`ffmpeg.wasm`库在浏览器端实现。以下是完整解决方案: ### 解决方案 #### 1. 安装依赖 ```bash npm install @ffmpeg/ffmpeg @ffmpeg/core ``` #### 2. Vue组件实现 ```vue <template> <div> <input type="file" accept=".wmv" @change="handleFileUpload" /> <img v-if="frameUrl" :src="frameUrl" alt="第一帧预览" style="max-width: 100%" /> </div> </template> <script> import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'; export default { data() { return { ffmpeg: null, frameUrl: null }; }, async mounted() { // 初始化FFmpeg this.ffmpeg = createFFmpeg({ log: true }); await this.ffmpeg.load(); }, methods: { async handleFileUpload(e) { const file = e.target.files?.[0]; if (!file) return; try { // 写入内存文件系统 this.ffmpeg.FS('writeFile', 'input.wmv', await fetchFile(file)); // 执行帧提取(关键命令) await this.ffmpeg.run( '-i', 'input.wmv', // 输入文件 '-vframes', '1', // 只取1帧 '-q:v', '2', // 输出质量(1-31,值越小质量越高) 'output.jpg' // 输出文件名 ); // 读取结果并生成URL const data = this.ffmpeg.FS('readFile', 'output.jpg'); this.frameUrl = URL.createObjectURL( new Blob([data.buffer], { type: 'image/jpeg' }) ); } catch (error) { console.error('处理失败:', error); } finally { // 清理临时文件 ['input.wmv', 'output.jpg'].forEach(f => { if (this.ffmpeg.FS('readdir', '/').includes(f)) { this.ffmpeg.FS('unlink', f); } }); } } }, beforeUnmount() { // 释放Blob URL if (this.frameUrl) URL.revokeObjectURL(this.frameUrl); } }; </script> ``` ### 关键参数说明 1. **`-vframes 1`**:精确提取第一帧[^2] 2. **`-q:v 2`**:控制JPEG输出质量(2为高质量)[^2] 3. **WebAssembly处理**:通过`ffmpeg.wasm`在浏览器安全沙箱中运行 ### 注意事项 1. **格式兼容性**:WMV需要FFmpeg内置的`asf`解复用器支持[^1] 2. **性能优化**: - 大文件需分片处理 - 添加进度提示(`ffmpeg.setProgress`) 3. **备选方案**:如遇复杂WMV文件,可先用`ffmpeg`转码: ```bash ffmpeg -i input.wmv -c:v libx264 output.mp4 # 参考引用[1] ``` ### 工作流程 ```mermaid graph TD A[用户选择WMV文件] --> B[写入FFmpeg内存文件系统] B --> C[执行帧提取命令] C --> D[读取生成的JPEG] D --> E[转换为Blob URL] E --> F[在img标签显示] ``` [^1]: 视频格式转换需使用正确的编解码器参数[^1] [^2]: 关键帧提取参数控制输出质量和帧数[^2]
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值