java封装FFmpeg命令,支持原生ffmpeg全部命令,实现FFmpeg多进程处理与多线程输出控制(开启、关闭、查询),rtsp/rtmp推流、拉流
前言:
之前已经对FFmpeg命令进行了封装http://blog.csdn.net/eguid_1/article/details/51787646,但是当时没有考虑到扩展性,所以总体设计不是太好,需要改动的地方也比较多,也不支持原生ffmpeg命令,所以本次版本推翻了前面的版本重新设计接口和实现,全面支持各个流程注入自己的实现,并且在原有命令组装基础上增加一个接口用来支持全部原生FFmpeg命令。
概述:
提供一个管理器用于方便管理FFmpeg命令的执行、停止和执行信息持久化。
可以方便的使用ffmpeg来进行推流,拉流,转流等任务
实现的功能:
①开启一个进程+一个输出线程来执行原生ffmpeg命令②开启一个进程+一个输出线程来执行组装命令③查询执行任务信息④查询全部正在执行的任务⑤停止进程和输出线程⑥停止全部正在执行的任务
源码包下载:http://download.csdn.net/detail/eguid_1/9668143
github项目地址:https://github.com/eguid/FFmpegCommandHandler4java
1、接口设计
1.1、发布任务接口
通过原生ffmpeg命令发布处理任务
通过map组装成ffmpeg命令来处理任务
1.2、终止任务接口
结束任务
结束全部任务
1.3、任务查询接口
查询
查询全部
1.4、接口实现注入
执行处理器注入
命令组装器注入
持久化实现注入
2、内部实现
2.1、任务处理器
开启一个进程用于执行ffmpeg命令
开启一个子线程用于输出ffmpeg执行过程
停止进程
停止输出线程(需要在进程关闭前停止输出线程)
按照正确顺序停止进程和线程2.2、输出线程处理器
用于输出ffmpeg执行过程
package cc.eguid.FFmpegCommandManager.service; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import cc.eguid.FFmpegCommandManager.entity.TaskEntity;
/**
* 任务处理实现
* @author eguid
* @since jdk1.7
* @version 2016年10月29日
*/
public class TaskHandlerImpl implements TaskHandler {
private Runtime runtime = null; @Override
public TaskEntity process(String id, String command) {
Process process = null;
OutHandler outHandler = null;
TaskEntity tasker = null;
try {
if (runtime == null) {
runtime = Runtime.getRuntime();
}
process = runtime.exec(command);// 执行本地命令获取任务主进程
outHandler = new OutHandler(process.getErrorStream(), id);
outHandler.start();
tasker = new TaskEntity(id, process, outHandler);
} catch (IOException e) {
stop(outHandler);
stop(process);
// 出现异常说明开启失败,返回null
return null;
}
return tasker;
} @Override
public boolean stop(Process process) {
if (process != null && process.isAlive()) {
process.destroy();
return true;
}
return false;
} @Override
public boolean stop(Thread outHandler) {
if (outHandler != null && outHandler.isAlive()) {
outHandler.stop();
outHandler.destroy();
return true;
}
return false;
} @Override
public boolean stop(Process process, Thread thread) {
boolean ret;
ret=stop(thread);
ret=stop(process);
return ret;
}
} /**
*
* @author eguid
*
*/
class OutHandler extends Thread {
/**
* 控制状态
*/
private volatile boolean desstatus = true; /**
* 读取输出流
*/
private BufferedReader br = null; /**
* 输出类型
*/
private String type = null; public OutHandler(InputStream is, String type) {
br = new BufferedReader(new InputStreamReader(is));
this.type = type;
} /**
* 重写线程销毁方法,安全的关闭线程
*/
@Override
public void destroy() {
setDesStatus(false);
} public void setDesStatus(boolean desStatus) {
this.desstatus = desStatus;
} /**
* 执行输出线程
*/
@Override
public void run() {
String msg = null;
int index = 0;
int errorIndex = 0;
int status = 10;
try {
System.out.println(type + "开始推流!");
while (desstatus && (msg = br.readLine()) != null) {
if (msg.indexOf("[rtsp") != -1) {
System.out.println("接收" + status + "个数据包" + msg);
System.out.println(type + ",网络异常丢包,丢包次数:" + errorIndex++ + ",消息体:" + msg);
status = 10;
index = 0;
} if (index % status == 0) {
System.out.println("接收" + status + "个数据包" + msg);
status *= 2;
}
index++;
}
} catch (IOException e) {
System.out.println("发生内部异常错误,自动关闭[" + this.getId() + "]线程");
destroy();
} finally {
if (this.isAlive()) {
destroy();
}
}
}
}
2.3、持久化服务
增加任务信息
删除任务信息
删除全部任务信息
查询任务信息
查询全部任务信息
任务是否存在
package cc.eguid.FFmpegCommandManager.dao; import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import cc.eguid.FFmpegCommandManager.entity.TaskEntity; /**
* 任务信息持久层实现
*
* @author eguid
* @since jdk1.7
* @version 2016年10月29日
*/
public class TaskDaoImpl implements TaskDao {
// 存放任务信息
private ConcurrentMap<String, TaskEntity> map = null; public TaskDaoImpl(int size) {
map = new ConcurrentHashMap<>(size);
} @Override
public TaskEntity get(String id) {
return map.get(id);
} @Override
public Collection<TaskEntity> getAll() {
return map.values();
} @Override
public int add(TaskEntity taskEntity) {
String id = taskEntity.getId();
if (id != null && !map.containsKey(id)) {
if (map.put(taskEntity.getId(), taskEntity) != null) {
return 1;
}
}
return 0;
} @Override
public int remove(String id) {
if(map.remove(id) != null){
return 1;
};
return 0;
} @Override
public int removeAll() {
int size = map.size();
try {
map.clear();
} catch (Exception e) {
return 0;
}
return size;
} @Override
public boolean isHave(String id) {
return map.containsKey(id);
} }
2.3命令组装器
用于将参数组装成对应的ffmpeg命令
package cc.eguid.FFmpegCommandManager.util; import java.util.Map; public class CommandAssemblyUtil {
/**
*
* @param map
* -要组装的map
* @param id
* -返回参数:id
* @param id
* -返回参数:组装好的命令
* @return
*/
public static String assembly(Map<String, String> paramMap) {
try {
if (paramMap.containsKey("ffmpegPath")) {
String ffmpegPath = (String) paramMap.get("ffmpegPath");
// -i:输入流地址或者文件绝对地址
StringBuilder comm = new StringBuilder(ffmpegPath + " -i ");
// 是否有必输项:输入地址,输出地址,应用名,twoPart:0-推一个元码流;1-推一个自定义推流;2-推两个流(一个是自定义,一个是元码)
if (paramMap.containsKey("input") && paramMap.containsKey("output") && paramMap.containsKey("appName")
&& paramMap.containsKey("twoPart")) {
String input = (String) paramMap.get("input");
String output = (String) paramMap.get("output");
String appName = (String) paramMap.get("appName");
String twoPart = (String) paramMap.get("twoPart");
String codec = (String) paramMap.get("codec");
// 默认h264解码
codec = (codec == null ? "h264" : (String) paramMap.get("codec"));
// 输入地址
comm.append(input);
// 当twoPart为0时,只推一个元码流
if ("0".equals(twoPart)) {
comm.append(" -vcodec " + codec + " -f flv -an " + output + appName);
} else {
// -f :转换格式,默认flv
if (paramMap.containsKey("fmt")) {
String fmt = (String) paramMap.get("fmt");
comm.append(" -f " + fmt);
}
// -r :帧率,默认25;-g :帧间隔
if (paramMap.containsKey("fps")) {
String fps = (String) paramMap.get("fps");
comm.append(" -r " + fps);
comm.append(" -g " + fps);
}
// -s 分辨率 默认是原分辨率
if (paramMap.containsKey("rs")) {
String rs = (String) paramMap.get("rs");
comm.append(" -s " + rs);
}
// 输出地址+发布的应用名
comm.append(" -an " + output + appName);
// 当twoPart为2时推两个流,一个自定义流,一个元码流
if ("2".equals(twoPart)) {
// 一个视频源,可以有多个输出,第二个输出为拷贝源视频输出,不改变视频的各项参数并且命名为应用名+HD
comm.append(" -vcodec copy -f flv -an ").append(output + appName + "HD");
}
}
return comm.toString();
}
}
} catch (Exception e) {
return null;
}
return null;
}
}
2.4、配置文件读取器
读取配置文件中的ffmpeg路径配置
读取默认位置的ffmpeg执行文件
package cc.eguid.FFmpegCommandManager.util; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties; /**
* 读取配置文件并加载FFmpeg
*
* @author eguid
* @since jdk1.7
* @version 2016年10月29日
*/
public class LoadConfig {
private volatile static boolean isHave = false;
private volatile static String ffmpegPath = null; public LoadConfig() {
super();
if (readConfig()) {
System.out.println("读取FFmpeg执行路径成功!");
isHave = true;
} else if (initConfInfo()) {
// 配置文件读取失败,自动从项目路径加载ffmpeg
isHave = true;
} else {
isHave = false;
} } protected boolean readConfig() {
String path = null;
File confFile = new File(getClass().getResource("/").getPath() + "loadFFmpeg.properties");
System.out.println("读取FFMPEG配置文件:" + confFile.getPath());
if (confFile != null && confFile.exists() && confFile.isFile() && confFile.canRead()) {
Properties prop = new Properties();
try {
prop.load(new FileInputStream(confFile));
path = prop.getProperty("path");
if (path != null) {
System.out.println("读取配置文件中的ffmpeg路径:" + path);
ffmpegPath = path;
return true;
}
} catch (IOException e) {
System.err.println("读取配置文件失败!");
return false;
}
}
System.err.println("读取配置文件失败!");
return false;
} /**
* 从配置文件中初始化参数
*/
protected boolean initConfInfo() {
String path = getClass().getResource("../").getPath() + "ffmpeg/ffmpeg.exe";
System.out.print("预加载默认项目路径FFMPEG配置:" + path);
File ffmpeg = new File(path);
ffmpegPath = ffmpeg.getPath();
if (isHave = ffmpeg.isFile()) {
return true;
}
System.out.println("加载ffmpeg失败!");
return false;
} /**
* 判断ffmpeg环境配置
*
* @return true:配置成功;false:配置失败
*/
public boolean isHave() {
return isHave;
} /**
* 获取ffmpeg环境调用地址 添加方法功能描述
*
* @return
*/
public String getPath() {
return ffmpegPath;
} public static void main(String[] args) {
LoadConfig conf = new LoadConfig();
}
}
java封装FFmpeg命令,支持原生ffmpeg全部命令,实现FFmpeg多进程处理与多线程输出控制(开启、关闭、查询),rtsp/rtmp推流、拉流的更多相关文章
- 命令行利用ffmpeg实现rtmp推流《转》
ffmpeg在以前介绍过,是一个相当强大的工具,我们这次利用它实现rtmp推流(最终推流地址统一为rtmp://127.0.0.1:1935/live/123). 1.首先下载ffmpeg和ffpla ...
- FFmpeg命令:几种常见场景下的FFmpeg命令(摄像头采集推流,桌面屏幕录制推流、转流,拉流等等)
前提: 首先你得有FFmpeg(ffmpeg官网快捷通道:http://ffmpeg.org/) 再者,推流你得有个流媒体服务,个人测试用小水管:rtmp://eguid.cc:1935/rtmp/t ...
- 实战FFmpeg--编译iOS平台使用的FFmpeg库(支持arm64的FFmpeg2.6.2)
编译环境:Mac OS X 10.10.2 ,Xcode 6.3 iOS SDK 8.3 FFmpeg库的下载地址是 http://www.ffmpeg.org/releases/ . ...
- ffmpeg处理RTMP流媒体的命令和发送流媒体的命令(UDP,RTP,RTMP)
将文件当做直播送至live ffmpeg -re -i localFile.mp4 -c copy -f flv rtmp://server/live/streamName re限制输出速率, ...
- javacpp-FFmpeg系列补充:FFmpeg拉流截图实现在线演示demo(视频截图并返回base64图像,支持jpg/png/gif/bmp等多种格式)
javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...
- 编译安装FFmpeg 要支持xvid、x264、mp3、ogg、amr、faac
编译安装FFmpeg 要支持xvid.x264.mp3.ogg.amr.faac libfaac faac格式的编解码包libmp3lame mp3格式编解码包libopencore-am ...
- 基于阿里云直播实现视频推流(ffmpeg)/拉流(Django2.0)以及在线视频直播播放(支持http/https)功能
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_146 由于5g网络的光速推广,视频业务又被推上了风口浪尖,在2019年初我们还在谈论照片,短视频等关键字,而进入2020年,我们津 ...
- ffmpeg+EasyDSS流媒体服务器实现稳定的rtmp推流直播
本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/74783269 需求 在做EasyDSS开发时,总 ...
- 运维程序】简单的命令控制器(支持定时命令执行、重复定时任务命令和进程管理,开发这个小程序主要是为了方便管理服务进程)【个人github项目】
一.前言: command-controller 一个运维程序,简单的命令控制器(支持定时命令执行和重复定时命令,开发这个程序主要是为了方便管理服务进程) 本来是要用python做的,但是之前做ffm ...
随机推荐
- 关于用jQuery的animate方法实现的动画在IE中失效的原因以及解决方法
这几天在学jQuery,本身还只是一个新手,写了一个简单的动画--圆形头像的缩放.本身是用Firefox进行调试的,一切进行的很顺利,缩放可以按照预期执行,结果拿到IE上去之后,发现缩放动画失效了.后 ...
- .NET遇上Docker - Docker集成Cron定时运行.NETCore(ConsoleApp)程序.md
配置项目的Docker支持 对于VS中Docker的配置,依旧重复一些废话. 给项目添加Docker支持,VS2015可以直接使用Docker for VS插件,VS2017在安装时选择容器支持.VS ...
- 给自己的QQ群开启腾讯官方的群聊机器人
看到腾讯有个机器人还不错 能聊天 能唱歌 方法:1.点击链接填写https://wj.qq.com/s/946969/64ac 群聊机器人内测邀请.按照提示填就行了 2.然后就是开启机器人了 http ...
- 学习java的感受
学习java有一段时间了,发现现在只是才了解java,还在门槛外边. 有时候真感觉这是不是一种外星语言,各种方法各种类各种借口,总是傻傻分不清. 学过的知识过一个星期就开始模糊,有时半夜睡不着的时候 ...
- VS2015如何新建C++或者C语言版的lib文件
当我们不想公开我们的代码的时候,可以把我们的代码封装成静态数据连接库,即lib文件.下面介绍下如何生成lib文件. 以VS2015为例,一种是C++版的lib文件,一种是C语言版的lib文件. 一.按 ...
- TreeSet对非自然顺序元素的排序
/* 1. 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储. 2. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该 ...
- 分针网—IT教育:调皮的JavaScript
JavaScript是一门有趣的语言,不仅有趣而且调皮,不同的内核的浏览器在解析的时候表现会有些差异,今天主要是抛砖引玉,和大家一起讨论一些在实际开发中比较常见但同时可能并没有过于在意的JavaScr ...
- Spring Session实现分布式session的简单示例
前面有用 tomcat-redis-session-manager来实现分布式session管理,但是它有一定的局限性,主要是跟tomcat绑定太紧了,这里改成用Spring Session来管理分布 ...
- OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现
最近趁着不忙,在构思一个搭建一个开源的完整项目,至于原因以及整个项目框架后边文章我再说明.既然要起一个完整的项目,那么数据仓储访问就必不可少,这篇文章我主要介绍这个新项目(OSS.Core)中我对仓储 ...
- 第三章 霍夫变换(Hough Transform)
主要内容: 霍夫变换的作用 霍夫变换检测直线的原理 霍夫变换检测圆的原理 OpenCV中的霍夫变换 1.霍夫变换检测直线原理 霍夫变换,英文名称Hough Transform,作用是用来检测图像中的直 ...