最近产品提了一个在线音视频直播的需求,技术这边主要是java开发,这让我想起了3年前使用的一款red5的开源流媒体服务器,下面会对开发中使用内容进行说明.
red5介绍
Red5是使用java写的开源的Flash流媒体服务器,它支持以下内容:
- 将视频/音频文件转化为播放流(支持FLV和MP3)
- 录制客户端播放流(仅支持 FLV)
- 共享对象
- 现场直播流发布
- 远程调用(AMF)
red5配置
首先我们了解下red5的主要配置
- beanRefContext.xml 定义了父context
- defaultContext.xml 定义全局服务
- red5-common.xml 所有子context间共享的类在本文件中进行声明,它包含的信息涉及到对象的序列化/反序列化,要使用的网络协议的编解码器以及可用的视频编解码器,同时对象缓存可以在本文件中进行配置或spring-wired,目前常用Ehcache
- red5-core.xml 所有可用的网络服务都在这里进行指定,默认是RTMP和RTMPT
- red5.properties 主要是配置易改变的值,使用Placeholder方式进行注入
- red5-web.xml red5应用程序可以在此文件中进行配置
- ehcache.xml 定义ehcache配置
- logback.xml 定义logback配置
- quartz.properties 定义quartz配置
red5主要类
ApplicationAdapter 此类常被用来作为新应用的基类,也是应用入口.它与FCS/FMS的application功能类似.
假设你连接到rtmp://server/app/room1/room2
首先,连接建立,用户”连接”所有域跨越至room2,即会进行如下调用.
- app(->appConnect)
- room1(->roomConnect)
- room2(->roomConnect)
当连接建立以后,客户端对象被检索,如果是第一次连接,将连接对应room域,会进行如下调用.
- app(->appJoin)
- room1(->roomJoin)
- room2(->roomJoin)
若同一个客户端再次连接到同一个域,就只会调用connect方法.如果你恰好连接到同一些域,可能只会调用到join方法,如rtmp://server/app/room1/room3会触发如下调用,
- appConnect(app)
- joinConnect(room1)
- joinConnect(room3)
- roomJoin(room3)
Red5 此类为red5工具类,提供了获取IConnection,IClient,IScope,IContext对象的方法.
IConnection 定义了一个连接对象,它实际是网络连接对象的接口,每个连接都会关联对应的客户端对象与scope对象.
IClient 定义了一个客户端对象,同时一个客户端会在同一主机上有多个不同scope的连接.
IScope 定义red5中作用域对象.该对象维护了一个由一组客户端连接组成的上下文状态.通过作用域对象我们就可以很轻松的实现一个分级访问,区域对象的共享的功能.对于一个作用域对象它可以有父作用域对象,也可以有子作用域对象.如果一个客户端连接到了一个作用域对象,同时也连接到了它的父作用域对象.通过作用域对象就可以访问资源,共享对象,音视频流等.
red5远程调用
传统的socket服务客户端与服务器需要将对象进行序列化,双方都需要做很多底层工作,但red5提供了一种更为简便的方式可以让方法调用像rmi一样,如果如果在ApplicationAdapter中定义了方法sayHello,那么你可以在客户端编写如下代码进行远程方法调用
1 2 3 4 5 |
nc = new NetConnection(); nc.connect("rtmp://localhost/myapp"); nc.call("sayHello", nc, "Hello world!"); |
如果你的方法并不是在ApplicationAdapter类中定义,但仍然想通过此方法进行调用如何实现呢?其实red5已经提供了相应的支持.
你可以使用spring的@Service或者@Component注解,也可以直接在xml中定义bean,同时给定beanName为”app.service”,注意beanName必须以.service结尾,否则会找不到此方法,同理客户端代码需要修改成如下方式.
1 2 3 4 5 |
nc = new NetConnection(); nc.connect("rtmp://localhost/myapp"); nc.call("app.sayHello", nc, "Hello world!"); |
上面提到客户端调用服务器端方法,下面将说明服务器端如何调用客户端方法,代码如下
1 2 3 4 5 6 |
if (conn instanceof IServiceCapableConnection) { IServiceCapableConnection sc = (IServiceCapableConnection) conn; sc.invoke("client_method", new Object[] { "One", 1}); } |
共享对象
共享对象主要用于多客户端的实时数据同步和方法统一调用,任何一个客户端发起的操作改变对象的属性都会同步至其它客户端上,类似于聊天室里的聊天内容,你画我猜的小黑板都可以使用共享对象实现.
如何创建共享对象?共享对象创建很简单,只需要在ApplicationAdapter中调用createSharedObject方法即可,否则你需要实现ISharedObjectService接口自己定义其共享对象作用域实现对应的方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ISharedObject so = null; // 创建共享对象 不需要持久化 if (createSharedObject(appScope, sharedName, false)) { so = getSharedObject(appScope, sharedName); } // 增加共享对象的监听器 so.addSharedObjectListener(new ISharedObjectListener() { // 实现接口方法 ... }); |
这样无论是客户端还是服务端修改此共享对象的属性,其它的客户端都能收到该对象的属性改变消息.
I’m extremely impressed together with your writing skills as neatly as with the layout for your weblog. Is that this a paid subject or did you modify it yourself? Either way stay up the nice quality writing, it’s uncommon to peer a great weblog like this one today..
It’s very simple to find out any matter on net as compared to books, as I found this paragraph at this site.