博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
FFmpeg进行视频帧提取&音频重采样-Process.waitFor()引发的阻塞超时
阅读量:5343 次
发布时间:2019-06-15

本文共 3827 字,大约阅读时间需要 12 分钟。

  由于产品需要对视频做一系列的解析操作,利用FFmpeg命令来完成视频的音频提取、第一帧提取作为封面图片、音频重采样、字幕压缩等功能;

  前一篇文章已经记录了 ,就不再重复说明,今天简单介绍下如何实现第一帧提取、重采样,然后再分享下Process.waitFor()引发的阻塞问题。

  一、首先,分享两个命令:

  1. 视频第一帧提取:ffmpeg -i [videofile]  -vframes 1 -q:v 2 -f image2  [imagefile]
  2. 音频重采样:ffmpeg -i [audiofile]  -ar 16000  [audiofile]

  说明:

  • -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、32,000Hz、44,100Hz;

  具体的代码依然使用Process类实现,可以参照之前的文章。

  二、接下来,重点讲一下在使用process.waitFor()发生的阻塞问题

  先介绍一下,这个功能由于11月6日需要演示,所以花了11月3日和4日两天开发完成的,主要流程是添加视频-音频提取-第一帧提取-语音识别-字幕处理-中英翻译,开发完成测试后功能ok,于是开开心心过周末。

  11月5日,周日下午产品经理打电话告诉我,4点开始的视频都没法正确处理了,于是我让他把视频发给我(当时他说这个视频是通过别的视频压缩工具压缩过的)。

  我测试了一下,通过查看log,发现在提取第一帧的时候,卡住了。但是我再次测试以前测试成功的那些视频,都是很顺利地处理完成,于是猜测,视频在压缩的过程中损坏,因为一开始他发视频给我后,我并没有播放,直接测试了,后来我双击播放了一下,竟然全是黑屏,只有声音???

  那么,到现在可以确定,问题确实出现在视频的本身,无法正确提取第一帧。

  可是无法提取第一帧,不应该是提取图片为空吗?为什么卡在那边不动了呢?暂时只能建议产品经理周一演示的时候,不要使用这些压缩损坏的视频,等周一去了之后再看看问题出现在哪。

  11月6日周一上午,到了公司,回想昨天的问题,首先看一下内存使用率(这是公司内部的监控系统,是不是很棒!哈哈哈哈!)

  

  内存图可以展示两个情况:

  1. 11月5日下午4点开始,内存使用率逐步上升,说明那段时间测试了视频,线程一直处于堵塞状态;

  2. 查看了一下dump文件(jmap -dump:live,format=b,file=heap.dmp PID),发现有很多线程在extractVideoFirstFrame方法时阻塞;

    

  3. 11月6日10点半左右,重启了一下应用,内存立马就下来了,可以断定,这就是因为内存使用率上升的原因,如果一直这样下去,肯定会导致OOM。 

  三、最后,如何解决process.waitFor()发生的阻塞问题呢?

   一般需要调用系统命令时,使用Runtime.getRuntime().exec(command)返回一个process对象,再调用process.waitFor()来等待命令执行结束,获取执行结果。

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

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

  

  但是,我把执行提取视频第一帧的命令单独拿出来执行,虽然图片未提取成功,但是命令仍然执行结束了,按道理不应该一直堵塞啊???

  

  然后又重新看了一遍JDK的API,看到如下一段话:

  

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

  解决方法:

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

  2. 记得释放文件流资源,以免导致“Too many open files”的错误;

  代码如下:

/**     * 处理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();    }

 

 

 

转载于:https://www.cnblogs.com/handsomeye/p/7792035.html

你可能感兴趣的文章
客户端连接linux虚拟机集群报错
查看>>
linux下部署一个JavaEE项目的简单步骤
查看>>
hash储存机制
查看>>
[Android学习系列16]Android把php输出的json加载到listview
查看>>
20145205 《信息安全系统设计基础》第14周学习总结
查看>>
6)添加一个窗口的图标
查看>>
POJ - 1422 Air Raid 二分图最大匹配
查看>>
Road Map
查看>>
正则替换中的一个Bug
查看>>
HI3531uboot开机画面 分类: arm-linux-Ubunt...
查看>>
制作U盘启动CDLinux 分类: 生活百科 ...
查看>>
strcpy函数里的小九九
查看>>
搭建ssm过程中遇到的问题集
查看>>
OpenLayers绘制图形
查看>>
tp5集合h5 wap和公众号支付
查看>>
Flutter学习笔记(一)
查看>>
iOS10 国行iPhone联网权限问题处理
查看>>
洛谷 P1991 无线通讯网
查看>>
[HIHO1184]连通性二·边的双连通分量(双连通分量)
查看>>
Codeforces Round #178 (Div. 2) B. Shaass and Bookshelf 【动态规划】0-1背包
查看>>