zoukankan      html  css  js  c++  java
  • 关于ffmpeg解决主流浏览器无法播放wmv、avi等格式视频问题(内附linux环境相关安装包nasm、yasm、x264等)

    最近接到一个老项目,由于老项目之前适配的是ie浏览器。该老项目中有很多wmv和avi格式的视频。最近需要更换视频其他浏览器访问,需要对除ie浏览器的其他浏览器进行适配。ie浏览器播放视频没有任何问题,但是在主流浏览器中,无法识别<embed>标签,只支持<video>、<audio>标签,然而这些标签支持的视频格式为主流的mp4格式的视频。导致兼容性问题,无法播放,以及主流浏览器无法播放非mp4格式的视频。尝试了很多,查阅了很多资料,前端无法解决该问题,最后尝试使用后端来解决该问题。通过java调用ffmpeg来对已经存储在系统中的视频进行自主的格式转换为mp4,前端通过判断是否为ie浏览器,而执行对应的js脚本。如果发现为非ie浏览器,并且视频为非mp4格式的视频,则主动调用后台接口,对该视频进行格式转换,同时更新数据库存储视频的目标位置,之后再访问该视频时,就查询到的是mp4格式的视频,就无需再做格式转换操作了。

    文末附相关安装包、ffmpeg相关命令介绍


    ffmpeg

    ffmpeg是一个支持多个格式视频转换(包括asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv格式)的软件。在windows和linux环境下使用的方式不同。

    首先需要下载ffmpeg软件,解压之后将ffmpeg.exe放入到项目目录中。通过java去调用该程序,执行命令对目标视频进行格式转换。


    相关java代码

    ConvertVideo 是接收需要转换的视频的类。需要注意的是,其中window环境下获取的ffmpeg目录与linux环境下获取ffmpeg目录是不同的。

    public class ConvertVideo {
        public static Boolean convertVedio(String inputPath) throws FFmpegException {
            if (inputPath.substring(inputPath.lastIndexOf(".")).equals(".mp4") || inputPath.substring(inputPath.lastIndexOf(".")).equals(".MP4")){
                return true;
            }
            String ffmpegPath = getFfmpegPath();
            String outputPath = getOutputPath(inputPath);
            if (new File(outputPath).exists()){
                return true;
            }
            return FFmpegUtil.ffmpeg(ffmpegPath, inputPath, outputPath);
        }
    
        /**
         * 获取ffmpeg执行文件的路径
         *
         * @return
         */
        private static String getFfmpegPath() {
            // windows环境
            return new Object(){
                public String getPath(){
                    return this.getClass().getResource("/").getPath().replaceAll("WEB-INF/classes/", "")+"ffmpeg/";
                }
            }.getPath();
    
            // linux环境
            //return "/opt/ffmpeg/ffmpeg-release.4.1/";
        }
    
        /**
         * 获取输出文件名
         *
         * @param inputPath
         * @return
         */
        private static String getOutputPath(String inputPath) {
            return inputPath.substring(0, inputPath.lastIndexOf(".")) + ".mp4";
        }
    
        public static String getNewFileUrl(String inputPath) {
            return inputPath.substring(0, inputPath.lastIndexOf(".")) + ".mp4";
        }
    }
    

    FFmpegException 这是定义ffmpeg异常类

    public class FFmpegException extends Exception {
        private static final long serialVersionUID = 1L;
    
        public FFmpegException() {
            super();
        }
    
        public FFmpegException(String message) {
            super(message);
        }
    
        public FFmpegException(Throwable cause) {
            super(cause);
        }
    
        public FFmpegException(String message, Throwable cause) {
            super(message, cause);
        }
    }
    

    FFmpegUtil 主要对视频进行格式转换的类

    public class FFmpegUtil {
        /**
         * 将视频转换为mp4
         *
         * @param ffmpegPath ffmpegPath bin路径
         * @param inputPath 源文件路径
         * @param outputPath 输出文件路径
         * @return
         */
        public static Boolean ffmpeg(String ffmpegPath, String inputPath, String outputPath) throws FFmpegException{
    
            if (!checkfile(inputPath)) {
                throw new FFmpegException("文件格式不合法");
            }
    
            int type = checkContentType(inputPath);
            List<String> command = getFfmpegCommand(type, ffmpegPath, inputPath, outputPath);
            if (null != command && command.size() > 0) {
                return process(command);
            }
            return false;
        }
    
        /**
         * 检查视频的格式
         *
         * @param inputPath 源文件
         * @return
         */
        private static int checkContentType(String inputPath) {
            String type = inputPath.substring(inputPath.lastIndexOf(".") + 1, inputPath.length()).toLowerCase();
            // ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
            if (type.equals("avi")) {
                return 0;
            } else if (type.equals("mpg")) {
                return 0;
            } else if (type.equals("wmv")) {
                return 0;
            } else if (type.equals("3gp")) {
                return 0;
            } else if (type.equals("mov")) {
                return 0;
            } else if (type.equals("mp4")) {
                return 0;
            } else if (type.equals("asf")) {
                return 0;
            } else if (type.equals("asx")) {
                return 0;
            } else if (type.equals("flv")) {
                return 0;
            }
            // 对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等)
            else if (type.equals("wmv9")) {
                return 1;
            } else if (type.equals("rm")) {
                return 1;
            } else if (type.equals("rmvb")) {
                return 1;
            }
            return 9;
        }
    
        /**
         * 检查文件的合法性
         *
         * @param path 文件路径
         * @return
         */
        private static boolean checkfile(String path) {
            File file = new File(path);
            return file.isFile();
        }
    
        /**
         * ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
         *
         * @param command ffmpeg的命令
         * @throws FFmpegException
         */
        private static boolean process(List<String> command) throws FFmpegException{
    
            try {
    
                if (null == command || command.size() == 0) {
                    return false;
                }
                Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();
    
                new PrintStream(videoProcess.getErrorStream()).start();
    
                new PrintStream(videoProcess.getInputStream()).start();
    
                int exitcode = videoProcess.waitFor();
    
                return exitcode != 1;
            } catch (Exception e) {
                throw new FFmpegException("file upload failed",e);
            }
    
        }
    
        /**
         * 根据文件类型设置ffmpeg命令
         *
         * @param type 该视频对应的类型
         * @param ffmpegPath ffmpeg执行文件的路径
         * @param oldfilepath 需要被转换的视频路径
         * @param outputPath 转换后输出的路径
         * @return
         * @throws FFmpegException
         */
        private static List<String> getFfmpegCommand(int type, String ffmpegPath, String oldfilepath, String outputPath) throws FFmpegException {
            List<String> command = new ArrayList<String>();
            if (type == 0) {
                command.add(ffmpegPath+"ffmpeg");
                command.add("-y");
                command.add("-i");
                command.add(oldfilepath);
                command.add("-c:v");
                command.add("libx264");
                command.add("-mbd");
                command.add("0");
                command.add("-c:a");
                command.add("aac");
                command.add("-strict");
                command.add("-2");
                command.add("-pix_fmt");
                command.add("yuv420p");
                command.add("-movflags");
                command.add("faststart");
                command.add(outputPath);
            } else{
                throw new FFmpegException("不支持当前上传的文件格式");
            }
            return command;
        }
    }
    
    // 开启新的线程对视频进行格式转换
    class PrintStream extends Thread {
        private static final Logger log = LoggerFactory.getLogger(PrintStream.class);
        java.io.InputStream inputStream = null;
    
        public PrintStream(java.io.InputStream is) {
            inputStream = is;
        }
    
        @Override
        public void run() {
            try {
                while (this != null) {
                    int ch = inputStream.read();
                    if (ch == -1) {
                        break;
                    } else {
                        System.out.print((char) ch);
                    }
    
                }
            } catch (Exception e) {
                log.error("convert media error!", e);
            }
        }
    }
    

    linux环境下使用ffmpeg

    安装GCC

    yum install gcc
    查看安装结果gcc --version

    安装yasm

    1.下载yasm
    wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
    2.解压yasm
    执行:tar -zxvf yasm-1.3.0.tar.gz
    3.进入解压目录
    执行: cd yasm-1.3.0
    4.编译和安装
    执行1:./configure
    执行2:make
    执行3:make install
    5.查看安装结果
    执行yasm --version

    安装nasm

    1.下载nasm
    wget http://www.nasm.us/pub/nasm/releasebuilds/2.13/nasm-2.13.tar.gz
    2.解压nasm
    执行 tar xzvf nasm-2.13.tar.gz
    3.进入目录
    执行:cd nasm-2.13
    4.编译和安装
    执行1:./configure
    执行2:make
    执行3:make install
    5.查看安装结果
    执行nasm --version

    安装x264

    1,下载x264 或解压x264
    git clone https://code.videolan.org/videolan/x264.git
    2.进入x264目录
    cd x264
    3.编译和安装
    执行1:./configure --enable-shared
    执行2:make
    执行3:make install
    4.查看安装结果
    执行x264 --version

    安装ffmpeg

    1.下载ffmpeg4.1
    wget https://git.ffmpeg.org/gitweb/ffmpeg.git/snapshot/refs/heads/release/4.1.tar.gz

    2.解压ffmpeg4.1
    执行: tar xzfv 4.1.tar.gz

    3.进入解压目录

    解压之后目录名为ffmpeg-release.4.1-d44da66,请将目录名修改为ffmpeg-release.4.1

    执行: cd ffmpeg-release.4.1/

    4.编译和安装
    执行1:./configure --enable-gpl --enable-libx264

    如果出现类似error如下:

    WARNING: using libfdk without pkg-config
    WARNING: using libx264 without pkg-config
    ERROR: x265 not found using pkg-config
    If you think configure made a mistake, make sure you are using the latest
    version from Git.  If the latest version fails, report the problem to the
    ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.freenode.net.
    Include the log file "ffbuild/config.log" produced by configure as this will help
    solve the problem.
    

    原因是需要设置PKG_CONFIG_PATH,通过pkg-config去指定路径自动寻找需要链接的依赖库,解决方法如下,执行命令:

    export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
    echo $PKG_CONFIG_PATH
    查看结果为 /usr/local/lib/pkgconfig
    

    解释:PKG_CONFIG_PATH (此路径为.pc文件所在路径,里面有x264.cp)

    然后重新编译就ok了!

    执行2:make
    执行3:make install(大概30分钟左右)

    5.查看安装结果 ffmpeg -version

    如果出现如下:

    6.编辑id.so.conf文件
    执行vim /etc/ld.so.conf
    在include ld.so.conf.d/*.conf后换行添加
    /usr/local/lib (如果有这句话,直接保存退出,没有就添加这句话)
    :x 保存退出
    在ffmpeg目录下,执行 ldconfig
    然后再执行 ffmpeg -version


    测试

    2.wmv转换成2.mp4

    本人安装ffmpeg时,是将ffmpeg安装在/home/herocheung/ffmpeg/目录下的,因此有了一下测试命令:

    /home/herocheung/ffmpeg/ffmpeg-release.4.1/ffmpeg -i ./2.wmv -c:v libx264 -mbd 0 -c:a aac -strict -2 -pix_fmt yuv420p -movflags faststart ./2.mp4
    
    
    -- 命令解释:
    (指定ffmpeg安装目录下的ffmpeg) -i (需要转换的视频位置) -c:v libx264 -mbd 0 -c:a aac -strict -2 -pix_fmt yuv420p -movflags faststart (转换后视频存放位置)
    

    安装完成之后,把java中相关的代码切换到linux环境下能够正确识别的路径即可实现java调用ffmpeg程序,实现视频格式转。


    截取视频中的图片

    从视频中截取第60s处的图片 ffmpeg -i test.mp4 -y -f image2 -ss 60 -vframes 1 test1.jpg


    附件

    【相关安装包(windows ffmpeg、linux ffmpeg相关包)】


    链接:https://pan.baidu.com/s/1GFt-i4l4r_IlgscAVqcmkQ
    提取码:ef26

    【ffmpeg相关命令】http://linux.51yip.com/search/ffmpeg


    相关博客:
    【linux安装ffmpeg】https://blog.csdn.net/chaos1/article/details/110440426?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-4.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-4.nonecase

  • 相关阅读:
    Python 简明教程 --- 15,Python 函数
    Python 简明教程 --- 14,Python 数据结构进阶
    Zookeeper分布式过程协同技术
    Zookeeper分布式过程协同技术
    Zookeeper分布式过程协同技术
    实战:IDEA运行速度调优
    Nacos学习笔记
    设计模式的七大原则详解
    【IDEA】创建maven项目,webapp没有被标识,无法识别
    新手安装配置git简洁教程
  • 原文地址:https://www.cnblogs.com/turbo30/p/14133292.html
Copyright © 2011-2022 走看看