欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaEE:ffmpeg使用

程序员文章站 2022-07-10 18:32:47
说明:ffmpeg用于视频编码、视频解码。一、ffmpeg安装/使用(windows系统):1.ffmpeg安装:(1)下载ffmpeg-20200807-fab00b0-win64-static.zip,解压到C:\ffmpeg:https://ffmpeg.zeranoe.com/builds/(2)配置ffmpeg环境变量,在系统变量"Path"末尾加入路径:...;C:\ffmpeg\bin;2.ffmpeg一些命令使用(打开CMD):(1)查看版本号:...

说明:

ffmpeg用于视频编码、视频解码。

一、ffmpeg安装/使用(windows系统):

1.ffmpeg安装:

(1)下载ffmpeg-20200807-fab00b0-win64-static.zip,解压到C:\ffmpeg:

https://ffmpeg.zeranoe.com/builds/

(2)配置ffmpeg环境变量,在系统变量"Path"末尾加入路径:

...;C:\ffmpeg\bin;

2.ffmpeg一些命令使用(打开CMD):

(1)查看版本号:

ffmpeg.exe -version

(2)转化视频文件格式(这里为avi转mp4,也可转为mp3、gif等):

<1>说明:

命令说明:
-c:v  >-c:v设置编码格式为libx264(h264的一种)
-s    >设置显示分辨率为1280x720
-pix_fmt yuv420p    >设置每个像素点采样方式(分三种:yuv4:4:4、yuv4:2:2、yuv4:2:0),用于还原每个像素点的yuv值(亮度与色彩)。
-b:a  >设置音频码率为63k
-b:v  >设置视频码率为753k
-r    >设置帧率为18

Kbps码率计算公式:
10MB视频:10 * 1024 * 1024 * 8 /10 / 1024 = 8192Kbps

分辨率要求:
高清>=960x400、超清>=1280x544、1080p>=1920x800
时长要求:
高清/超清/1080p>=15秒
平均码率要求:
H.265/HEVC(mp4格式):高清>0.56Mbps、超清>0.9Mbps、1080p>2.1Mbps
H.264/AVC/AVCHD/X264(mp4、mkv格式)、RV40/RealVideo 9(rmvb格式)、WMV3/WVC1/WMVA/VC-1/Windows Meadia Video 9(wmv格式):高清>0.86Mbps、超清>1.5Mbps、1080p>3.5Mbps
MPEG-4 Visual/Xvid/Divx(avi、mp4格式):高清>1.6Mbps、超清>3Mbps、1080p>5Mbps
MPEG-2(mpeg/mpg/vob格式)、MPEG-1(mpeg/mpg格式):高清>4Mbps、超清>7.5Mbps、1080p>8Mbps

<2>命令(cd到C:\video目录下,old.avi在此目录):

ffmpeg.exe -i old.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 old1.mp4

(3)将mp4分段,生成一个m3u8索引文件+n个ts文件:

说明:

-hls_time      >设置每片长度为10秒
-hls_list_size >设置保存分段数量,0保存所有
-hls_segment_filename  >设置分段文件名,%06d动态生成6位数字,如new_123456.ts

命令(cd到C:\video目录下,old1.mp4在此目录,生成的m3u8和ts列表放在C:\hls目录下):

ffmpeg -i old1.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename ../hls/new_%06d.ts ../hls/new.m3u8

生成的文件如下:

new.m3u8
new_000000.ts
new_000001.ts
new_000002.ts
...

new.m3u8内容如下:

#EXTM3U                       //开始标志
#EXT-X-VERSION:3              //版本号
#EXT-X-TARGETDURATION:14
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:13.888889,
new_000000.ts                 //每个片段的名称
#EXTINF:13.888889,
new_000001.ts
...
#EXT-X-ENDLIST                //结束标志

二、使用java调用ffmpeg转换视频编码:

1.在工程application.yml中配置ffmpeg.exe路径和源视频与生成的视频目录:

#hls:
hls: #自定义属性,配置ffmpeg与视频加载与输出的地址
  ffmpeg‐path: C:/ffmpeg/bin/ffmpeg.exe
  avi-dir: C:/video/avi/
  mp4-dir: C:/video/mp4/
  hls-dir: C:/video/hls/

2.调用ffmpeg进行视频转换的java类:

(1)处理基类,VideoConvertBase.java:

//视频处理基类
public abstract class VideoConvertBase {
    protected String ffmpegPath; //ffmpeg.exe路径
    public VideoConvertBase(String ffmpegPath) {
        this.ffmpegPath = ffmpegPath;
    }
    //删除已存在的文件
    protected void delete(String dirPath, String fileName) {
        if (dirPath == null || fileName == null) {
            return;
        }
        File file = new File(dirPath, fileName);
        if (file.exists() && file.isFile()) {
            file.delete();
        }
    }
    //校验视频长度是否一样
    protected Boolean check(String oldVideo, String newVideo) {
        if (oldVideo == null || newVideo == null) {
            return false;
        }
        String oldLen = getLenth(oldVideo);
        if (oldLen == null) {
            return false;
        }
        if (oldLen.lastIndexOf(".") != -1 && oldLen.length() > 1) {
            oldLen = oldLen.substring(0, oldLen.lastIndexOf("."));  //视频分秒
        }
        String newLen = getLenth(newVideo);
        if (newLen == null) {
            return false;
        }
        if (newLen.lastIndexOf(".") != -1 && newLen.length() > 1) {
            newLen = newLen.substring(0, newLen.lastIndexOf("."));  //视频分秒
        }
        if (!oldLen.equals(newLen)) {
            return false;
        }
        return true;
    }
    //获取视频长度
    public String getLenth(String path) {
        ArrayList<String> cmd = new ArrayList<>(); //等同于cmd中输入命令:ffmpeg -i old.avi
        cmd.add(ffmpegPath);
        cmd.add("-i");
        cmd.add(path);
        try {
            Process process = new ProcessBuilder().command(cmd).redirectErrorStream(true).start(); //执行命令
            String info = readInfo(process);
            int start = info.trim().indexOf("Duration: ");
            int end = info.trim().indexOf(", start:");
            if (start == -1 || end == -1) {
                return null;
            }
            String time = info.substring(start + 10, end);
            if (time != null && !time.equals("")) {
                return time.trim();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    //读取信息
    public String readInfo(Process process) {
        InputStream in = null;
        try {
            StringBuffer sb = new StringBuffer();
            in = process.getInputStream();
            InputStream errorIn = process.getErrorStream();
            boolean done = false;
            int maxSleep = 10 * 60;
            int sleep = 0;
            while (!done) {
                if (sleep > maxSleep) {
                    return "读取错误";
                }
                try {
                    while (in.available() > 0) {
                        sb.append(new Character((char) in.read()));
                    }
                    while (errorIn.available() > 0) {
                        sb.append(new Character((char) in.read()));
                    }
                    process.exitValue();
                    done = true;
                } catch (IllegalThreadStateException e) {
                    e.printStackTrace();
                    Thread.currentThread().sleep(1000);
                    sleep++;
                }
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "读取错误";
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

(2)avi转mp4的类:

public class AviToMp4Biz extends VideoConvertBase {
    private static AviToMp4Biz I;
    private AviToMp4Biz(String ffmpegPath) {
        super(ffmpegPath);
    }
    public static AviToMp4Biz I(String ffmpegPath) {
        if (I == null) {
            I = new AviToMp4Biz(ffmpegPath);
        }
        return I;
    }
    private String createMp4Name(String mp4Name) {
        return mp4Name.substring(0, mp4Name.lastIndexOf(".")) + ".mp4";
    }
    //avi转mp4
    public String aviToMp4(String aviDir, String aviName, String mp4Dir) {
        String mp4Name = createMp4Name(aviName);
        String aviPath = aviDir + aviName;
        String mp4Path = mp4Dir + mp4Name;
        delete(mp4Dir, mp4Name);  //删已存在的
        File file = new File(mp4Path);
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //等同在cmd中输入:ffmpeg.exe -i C:/video/avi/old.avi -c:v libx264 -y -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 C:/video/mp4/old.mp4
        ArrayList<String> cmd = new ArrayList<>();
        cmd.add(ffmpegPath); //配置ffmpeg.exe
        cmd.add("-i");
        cmd.add(aviPath);  //avi文件路径
        cmd.add("-c:v");
        cmd.add("libx264");
        cmd.add("-y");
        cmd.add("-s");
        cmd.add("1280x720");
        cmd.add("-pix_fmt");
        cmd.add("yuv420p");
        cmd.add("-b:a");
        cmd.add("63k");
        cmd.add("-b:v");
        cmd.add("753k");
        cmd.add("-r");
        cmd.add("18");
        cmd.add(mp4Path);  //mp4文件路径
        try {
            new ProcessBuilder().command(cmd).redirectErrorStream(true).start(); //执行avi转mp4命令
            if (check(aviPath, mp4Path)) {
                return "avi转mp4成功";
            } else {
                return "avi转mp4失败,视频文件校验不通过";
            }
        } catch (IOException e) {
            e.printStackTrace();
            return "avi转mp4异常: " + e.getMessage();
        }
    }
}

(3)mp4转hls的类:

public class MP4ToHlsBiz extends VideoConvertBase {
    private static MP4ToHlsBiz I;
    private MP4ToHlsBiz(String ffmpegPath) {
        super(ffmpegPath);
    }
    public static MP4ToHlsBiz I(String ffmpegPath) {
        if (I == null) {
            I = new MP4ToHlsBiz(ffmpegPath);
        }
        return I;
    }
    private String createM3u8Name(String mp4Name) {
        return mp4Name.substring(0, mp4Name.lastIndexOf(".")) + ".m3u8";
    }
    //mp4转hls
    public String mp4ToHls(String mp4Dir, String mp4Name, String hlsDir) {
        String m3u8Name = createM3u8Name(mp4Name);
        String mp4Path = mp4Dir + mp4Name;
        String hlsPath = hlsDir + m3u8Name;
        delete(hlsDir, m3u8Name);  //删已存在的
        //等同在cmd中输入:ffmpeg -i C:/video/mp4/old.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename C:/video/hls/old_%06d.ts C:/video/hls/old.m3u8
        List<String> cmd = new ArrayList<String>();
        cmd.add(ffmpegPath);
        cmd.add("-i");
        cmd.add(mp4Path);
        cmd.add("-hls_time");
        cmd.add("10");
        cmd.add("-hls_list_size");
        cmd.add("0");
        cmd.add("-hls_segment_filename");
        cmd.add(hlsDir + m3u8Name.substring(0, m3u8Name.lastIndexOf(".")) + "_%06.ts"); //等同:../hls/new_%06d.ts
        cmd.add(hlsPath);
        try {
            new ProcessBuilder().command(cmd).redirectErrorStream(true).start(); //执行mp4转hls命令
            if (!check(mp4Path, hlsPath)) {
                return "mp4转hls失败,视频文件校验不通过";
            }
            ArrayList<String> list = getTsList(hlsPath);
            if (list == null || list.isEmpty()) {
                return "mp4转hls失败,ts文件为空";
            }
            return "mp4转hls成功";
        } catch (IOException e) {
            e.printStackTrace();
            return "mp4转hls异常: " + e.getMessage();
        }
    }
    private ArrayList<String> getTsList(String hlsPath) {
        ArrayList<String> list = new ArrayList<>();
        BufferedReader read = null;
        try {
            read = new BufferedReader(new FileReader(hlsPath));
            String line;
            String lastLine = "";
            while ((line = read.readLine()) != null) {
                if (line.endsWith(".ts")) {
                    list.add(line);
                }
                lastLine = line;
            }
            if (lastLine.contains("#EXT-X-ENDLIST")) {
                return list;
            }
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (read != null) {
                try {
                    read.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3.视频转换业务类:

@Service
public class VideoBiz {//视频转换业务类
    @Value("${hls.ffmpeg‐path}")  //加载application.yml中配置的ffmpeg路径
    String ffmpegPath;
    @Value("${hls.avi-dir}")   //加载视频路径
    String aviDir;
    @Value("${hls.mp4-dir}")
    String mp4Dir;
    @Value("${hls.hls-dir}")
    String hlsDir;
    public String aviToMp4(String aviName) {//avi转mp4
        return AviToMp4Biz.I(ffmpegPath).aviToMp4(aviDir, aviName, mp4Dir);
    }
    public String mp4ToHls(String mp4Name) {//mp4转hls
        return MP4ToHlsBiz.I(ffmpegPath).mp4ToHls(mp4Dir, mp4Name, hlsDir);
    }
}

 

 

 

 

本文地址:https://blog.csdn.net/a526001650a/article/details/107893093

相关标签: JavaEE