java rtmp_搭建rtmp直播流服务之2:使用java实现ffmpeg命令接口化调用(用java执行ffmpeg命令)...

一、环境搭建

1、安装ffmpeg

下载对应系统的ffmpeg安装包,个人采用windows平台进行开发,所以安装了windows版本(各平台ffmpeg命令都是一样的,无须纠结)

2、ffmpeg的命令

这里不在详述,在这里会用简单的命令即可,后面我会写篇专门介绍ffmpeg的命令的文章

二、使用Java实现ffmpeg的命令调用的接口化可管理

1、java解析ffmpeg命令解析及动态实现

这是rtmp直播流服务器的发布地址:rtmp://192.168.30.21/live/

如果新发布一个视频,可以增加一个应用名,比如

rtmp://192.168.30.21/live/test2

或者

rtmp://192.168.30.21/live/DahuaCamera

注释写的很全,这里就不在做过多叙述

/**

* 通过解析参数生成可执行的命令行字符串;

* name:应用名;input:接收地址;output:推送地址;fmt:视频格式;fps:视频帧率;rs:视频分辨率;disableAudio:是否开启音频

*

* @param paramMap

* @return 命令行字符串

*/

protectedString getComm4Map(Map paramMap)

{

// -i:输入流地址或者文件绝对地址

StringBuilder comm = newStringBuilder("ffmpeg -i ");

// 是否有必输项:输入地址,输出地址,应用名

if(paramMap.containsKey("input") && paramMap.containsKey("output")

&& paramMap.containsKey("name"))

{

comm.append(paramMap.get("input")).append(" ");

// -f :转换格式,默认flv

comm.append(" -f ").append(paramMap.containsKey("fmt") ? paramMap.get("fmt") :"flv").append(" ");

// -r :帧率,默认25

comm.append("-r ").append(paramMap.containsKey("fps") ? paramMap.get("fps") :"30").append(" ");

// -s 分辨率 默认是原分辨率

comm.append("-s ").append(paramMap.containsKey("rs") ? paramMap.get("rs") :"").append(" ");

// -an 禁用音频

comm.append("-an ").append(paramMap.containsKey("disableAudio") && ((Boolean)paramMap.get("disableAudio")) ?"-an":"").append(" ");

// 输出地址

comm.append(paramMap.get("output"));

//发布的应用名

comm.append(paramMap.get("name"));

//一个视频源,可以有多个输出,第二个输出为拷贝源视频输出,不改变视频的各项参数

comm.append(" ").append(" -vcodec copy -f flv -an rtmp://192.168.30.21/live/test2");

System.out.println(comm.toString());

returncomm.toString();

}

else

{

thrownewRuntimeException("输入流地址不能为空!");

}

}

2、执行ffmpmeg命令

2.1、上一步已经可以动态的创建ffmpeg的命令了,这一步我们要让命令执行

finalProcess proc = Runtime.getRuntime().exec(comm);

System.out.println("执行命令----start commond");

OutHandler errorGobbler = newOutHandler(proc.getErrorStream(),"Error");

OutHandler outputGobbler = newOutHandler(proc.getInputStream(),"Info");

errorGobbler.start();

outputGobbler.start();

2.2、在执行ffmpeg命令时必须开启两个输出线程(上面代码中的OutHandler类)

OutHandler类实现了Thread接口,并且重写了注销该线程的方法(用于关闭该线程)

具体实现是这样的:

importjava.io.BufferedReader;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.InputStreamReader;

/**

* 用于输出命令行主进程的消息线程(必须开启,否则命令行主进程无法正常执行) 重要:该类重写了destroy方法,用于安全的关闭该线程

*

* @author eguid

* @see OutHandler

* @since jdk1.7

*/

publicclassOutHandlerextendsThread

{

// 控制线程状态

volatilebooleanstatus =true;

BufferedReader br = null;

String type = null;

publicOutHandler(InputStream is, String type)

{

br = newBufferedReader(newInputStreamReader(is));

this.type = type;

}

/**

* 重写线程销毁方法,安全的关闭线程

*/

@Override

publicvoiddestroy()

{

status = false;

}

/**

* 执行输出线程

*/

@Override

publicvoidrun()

{

String msg = null;

try

{

while(status)

{

if((msg = br.readLine()) !=null)

{

System.out.println(type + "消息:"+ msg);

}

}

}

catch(IOException e)

{

e.printStackTrace();

}

}

}

2.3、实现统一关闭命令行主进程和关联的两个输出线程

现在命令行已经可以执行了,但是却没法关闭它和关联的两个输出线程,怎么办?

我们在上面代码中已经重写了输出线程的注销方法,只要能够得到这两个线程我们就能关闭它们;

命令行主进程也是同样,只需要获得该进程Process即可使用destroy()方法进行关闭。

2.3.1、实现(把主进程Process和两个OutHandler返回给上一级,让上一级统一存放并管理他们):

publicConcurrentMap push(Map paramMap)

throwsIOException

{

// 从map里面取数据,组装成命令

String comm = getComm4Map(paramMap);

ConcurrentMap resultMap = null;

// 执行命令行

finalProcess proc = Runtime.getRuntime().exec(comm);

System.out.println("执行命令----start commond");

OutHandler errorGobbler = newOutHandler(proc.getErrorStream(),"Error");

OutHandler outputGobbler = newOutHandler(proc.getInputStream(),"Info");

errorGobbler.start();

outputGobbler.start();

// 返回参数

resultMap = newConcurrentHashMap();

resultMap.put("info", outputGobbler);

resultMap.put("error", errorGobbler);

resultMap.put("process", proc);

returnresultMap;

}

2.3.2、父级这样实现关闭主进程和两个输出线程(必须先关闭两个输出线程):

publicvoidremovePush(String pushId)

{

if(hd.isHave(pushId))

{

ConcurrentMap map = hd.get(pushId);

//关闭两个线程

((OutHandler)map.get("error")).destroy();

((OutHandler)map.get("info")).destroy();

System.out.println("停止命令-----end commond");

//关闭命令主进程

((Process)map.get("process")).destroy();

hd.delete(pushId);

}

}

2.3.3、简单使用map存放Process和OutHandler

privatestaticConcurrentMap> handlerMap =newConcurrentHashMap>(20);

到这里,我们就可以做到动态创建、运行并关闭ffmpeg命令的功能

简单测试一下能不能正常发布视频流到rtmp直播流服务器

//name:应用名;input:接收地址;output:推送地址;fmt:视频格式;fps:视频帧率;rs:视频分辨率;disableAudio:是否开启音频

PushManager pusher = newPushManagerImpl();

Map map=newHashMap();

map.put("name","test3");

map.put("input","rtsp://admin:admin@192.168.2.236:37779/cam/realmonitor?channel=1&subtype=0");

map.put("output","rtmp://192.168.30.21/live/");

map.put("fmt","flv");

map.put("fps","25");

map.put("rs","640x360");

map.put("disableAudio",true);

//推送后会获得该处理器的id,通过该id可关闭推送流

String id = pusher.push(map);

Thread.sleep(100000);

//关闭推送流

pusher.removePush(id);

通过输出线程输出的消息可以看到直播流发布成功了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值