此文已由作者叶海啸授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

FFmpeg是一个开源免费跨平台的视频和音频流方案,可以快速对音视频流进行多方面的处理,本文主要介绍FFmpeg常用的命令与参数讲解,如何在JAVA中使用FFmpeg以及遇到的一些问题。

背景

项目需求中涉及到有关于视频、音频的一系列处理,包含视频中音频提取、视频首帧提取、音频重采样、字幕压缩的功能,一直在研究ffmpeg,仅仅几个功能,却深受ffmpeg的折磨。

今天谈谈ffmpeg在java中的简单使用,首先下载ffmpeg包,官方地址:http://ffmpeg.org/download.html, 这里建议下载Linux Static Builds版本的,轻小而且解压后可以直接使用,本人使用的版本是ffmpeg-git-20170922-64bit-static.tar.xz。

解压之后,文件夹中有一个可执行文件ffmpeg,在linux上可以直接运行./ffmpeg -version,可以查看ffmpeg的版本信息,以及configuration配置信息。

基本命令

  • 视频中音频提取:ffmpeg -i [videofile] -vn -acodec copy [targetaudiofile]

  • 视频首帧提取:ffmpeg -i [videofile] -vframes 1 -q:v 2 -f image2  [imagefile]

  • 音频重采样:ffmpeg -i [audiofile] -ar [samplingrate]  [targetaudiofile]

  • 字幕压缩:ffmpeg -i [videofile] -vf subtitles=[subtitle.srt] [targetvideofile]

命令说明

  • audiofile、videofile是音视频源文件,可以是本地文件,也可以是网络文件URL;

  • 提取音频流时,-vn 忽略视频流 -acodec 设定声音编解码器,未设定时则使用与输入流相同的编解码器,如果需要提取视频流,则参数变为-an -vcodec;

  • -vframes 表示提取的第几帧,获取第一桢则后面的值为1,如果后面的值大于1,那么最后的[imagefile]不能指定一个文件,不然会报错,如下

  • 指定了输出的文件名为“1.jpeg”,报错:不能从1.jpeg文件中获取第二帧的文件名,因为-vframes只要大于1,则会提取出每一帧的图片,建议使用如%03d.jpeg来作为文件名,那么它解析的结果便是001.jpeg,002.jpeg,...依次编号往后;

  • -q:v 2 q代表质量quality, v代表视频流,2是控制质量的参数;-f指定输出的格式是image2;

  • 除了使用-vframes来获取视频帧,还有使用-ss参数来获取,-ss后的时间参数是自行设定,并且在视频的有效时间内(格式为00:00:00),使用-ss时,如果没有使用%03d.jpeg来作为文件名,则获取的是-ss参数指定的那个时间的帧;

  • -ar表示使用新的采样率,常用的有8,000Hz、16,000Hz、22,050Hz、32,000Hz、44,100Hz;

  • subtitle.srt是字幕文件(中文字幕即把英文变为中文,其它格式一致),这边就使用最简单的srt标准格式,srt文件写入的字符编码需要是UTF-8,否则压缩的时候会报无法读取srt文件;

  • 若想压缩中文字幕,需要系统中有中文字体,使用fc-list查询系统支持的字体,fc-list :lang=zh查询支持的中文字体

JAVA使用遇到的问题

一般需要调用系统命令时,大部分人第一反应肯定是使用Runtime.getRuntime().exec(command)返回一个process对象,再调用process.waitFor()来等待命令执行结束,获取执行结果。

产品刚上线,运行很稳定,但是没过多久,产品同学说从某个时间点开始添加的视频都不出来了!因为这个视频必须要经过一系列的处理,才会展示出来,所以中途某个环节出错了。

首先查看了日志,没有发现任何的报错,但是幸好开发的时候加了debug日志,每一句命令exec前后都会打一句log,于是看下是否“开始执行”和“执行成功”两句log都打印了,结果发现在截取首帧的时候,只打印了“开始执行”,一直没有结束,那么猜测进程堵塞了。

但是,我把产品同学的视频拿过来,直接执行提取视频第一帧的命令,提示图片未提取成功,后来发现该视频是产品同学通过某个压缩工具压缩过的,点开视频可以看见黑屏,看不到任何东西,肯定是压缩时把视频压缩出错了,但是截取首帧命令既然执行结束了,按道理不应该一直堵塞啊?   

于是通过dump下了内存镜像文件,命令jmap -dump:live,format=b,file=heap.dmp PID,通过jvisualvm工具查看,发现有很多如下的堆栈:     

因此可以判断,确实是在截取首帧的时候,进程阻塞了,但是为什么会阻塞???

解决方案

查看waitFor()源码可以发现,其实调用的是Object类中的,wait()方法,并且未指定等待时间,那么如果一直不返回,则会一直阻塞。

并且查看了JDK的帮助文档,如下   

因此,可以得出结论:如果外部程序不断在向标准输出流(对于jvm来说就是输入流)和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitFor()这里。

解决方法:在waitFor()之前,利用单独两个线程,分别处理process的getInputStream()和getErrorSteam(),防止缓冲区被撑满,导致阻塞;

 /**
 * 处理process输出流和错误流,防止进程阻塞
 * 在process.waitFor();前调用
 * @param process
 */
private static void dealStream(Process process) {
    if (process == null) {
        return;
    }
    // 处理InputStream的线程
    new Thread() {
        @Override
        public void run() {
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = null;
            try {
                while ((line = in.readLine()) != null) {
                    logger.info("output: " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
    // 处理ErrorStream的线程
    new Thread() {
        @Override
        public void run() {
            BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line = null;
            try {
                while ((line = err.readLine()) != null) {
                    logger.info("err: " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    err.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}

免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 Kubernetes 在网易云中的落地优化实践
【推荐】 寓教于乐——玩转角色互换游戏

FFmpeg在JAVA中的使用以及Process.waitFor()引发的阻塞问题的更多相关文章

  1. FFmpeg进行视频帧提取&音频重采样-Process.waitFor()引发的阻塞超时

    由于产品需要对视频做一系列的解析操作,利用FFmpeg命令来完成视频的音频提取.第一帧提取作为封面图片.音频重采样.字幕压缩等功能: 前一篇文章已经记录了FFmpeg在JAVA中的使用-音频提取&am ...

  2. FFmpeg在JAVA中的使用-音频提取&字幕压缩

    由于项目需求中涉及到视频中音频提取,以及字幕压缩的功能,一直在研究ffmpeg,仅仅两个功能,却深受ffmpeg的折磨. 今天谈谈ffmpeg在java中的简单使用,首先下载FFmpeg包,官方地址: ...

  3. [转]Java中使用Runtime和Process类运行外部程序

    帖子1: 使用Runtime.getRuntime().exec()方法可以在java程序里运行外部程序.  1. exec(String command)  2. exec(String comma ...

  4. java中Runtime类和Process类的简单介绍

    在java.lang包当中定义了一个Runtime类,在java中对于Runtime类的定义如下: Java code public class Runtime extends Object 每个 J ...

  5. java调用第三方命令,process.waitfor()挂起(你不知道的坑)

    我们常在java中运行第三方程序,如sh.python,java提供一个Runtime.exec()方法,生成一个Process对象.今天在使用这个方法的时候,发现接口半天没有返回数据.查了一下,原来 ...

  6. BUGFIX 09 - 记一次Java中String的split正则表达式匹配 - 引发`OutOfMemoryError: Java heap space`的oom异常 排查及解决 -Java根据指定分隔符分割字符串,忽略在引号里面的分隔符

    问题简述 说白了,Java根据指定分隔符分割字符串,忽略在引号(单引号和双引号)里面的分隔符; oom压测的时候,正则匹配"(?=(?:[^\"]*\"[^\" ...

  7. 再谈一次关于Java中的 AIO(异步IO) 与 NIO(非阻塞IO)

    今天用ab进行压力测试时,无意发现的: Requests per second:    xxx [#/sec] (mean) ab -n 5000 -c 1000 http://www:8080/up ...

  8. Java中RunTime类介绍

    Runtime 类代表着Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,我们可以通过Runtime.getRuntime() 方法来获取当前程序的Runtime ...

  9. java中执行cmd命令

    一.java执行cmd命令的三种方式:http://www.jb51.net/article/80829.htm 参考:https://www.cnblogs.com/zhufu9426/p/7928 ...

随机推荐

  1. 29:ISBN号码

    29:ISBN号码 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括9位数字.1位识别码和3位 ...

  2. 【读书笔记】iOS-访问网络

    iOS平台是按照一直有网络连接的思路来设计的,开发者利用这一特点创造了很多优秀的第三方应用.大多数的iOS应用都需要联网,甚至有些应用严重依赖网络,没有网络就无法正常工作. "在访问网络失败 ...

  3. loadrunner 场景设计-集合点设置

    场景设计-集合点设置 by:授客 QQ:1033553122 1  作用 通过让多用户在同一时间点上进行并发操作来测试系统的并发处理的能力 2  实现 通过集合点函数来实现. 注意:集合点经常和事务结 ...

  4. Flutter 布局(十)- ListBody、ListView、CustomMultiChildLayout详解

    本文主要介绍Flutter布局中的ListBody.ListView.CustomMultiChildLayout控件,详细介绍了其布局行为以及使用场景,并对源码进行了分析. 1. ListBody ...

  5. (网页)12种不宜使用的Javascript语法(转)

    转自阮一峰: 最近写的一些小东西,总是出各种各样的问题,用了angular.js反应居然比我的jQuery还慢,客户吐槽了,我又把一个小操作,改成了jQuery.浏览一下大神的的博客.转载一点东西: ...

  6. screen mac linux下一种让程序后台运行的方法

    1: screen 场景的意思.字面意思就是软件运行在不同场景 (1)创建会话 使用命令“screen -S RunWork”来创建一个screen会话,命令执行之后,就会得到一个新的shell窗口, ...

  7. 总结Hibernate4.1+版本与Hibernate3.3+版本区别

    利用休假时间好好学习了当今流行的ORMapping框架-Hibernate,看完了马士兵老师经典的Hibernate视频教程,也算是小小入门了吧. 马老师在讲课中使用的Hibernate版本是3.3. ...

  8. What To Do When MySQL Runs Out of Memory: Troubleshooting Guide

    In this article, I will show you how to use the new version of MySQL (5.7+) and how to troubleshoot ...

  9. 解决Windows10或者其他版本Windows Update报错的问题

    最近更新系统,发现报错0x80248014,系统版本为redstone2(创意者更新). 总结发现,只要是windows各个版本自动更新报错的,如0x80开头的一系列错误,都可以通过如下步骤解决: 手 ...

  10. JRE、JDK概述

    JRE(java Runtime Environment java运行环境) 包括java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等, 如果想要运行一个开 ...