基本環境
Eclipse
Version: Neon.3 Release (4.6.3)
Build id: 20170314-1500
RED5 Server
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)
RED5-Eclipse-Plugin
開始搭建
項目創建
red5 Runtime 配置
測試RED5 server
red5-web.properties
red5-web.xml
基本通訊
- NetConnection.Connect.Success
關於如何關閉red5 server
完善red5項目
package com.state;
/**
* 臨時容器
* @author Allen 2017年3月31日
*
*/
import java.util.HashMap;
import com.state.room.RoomVo;
public class Ram {
public static HashMap<String, RoomVo> roomHm = new HashMap<>();
}
package com.state.user;
public class UserVo implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = -6628674875994109212L;
private String red5Id;
private String red5Name;
private Long red5CreateTime;
public String getRed5Id() {
return red5Id;
}
public void setRed5Id(String red5Id) {
this.red5Id = red5Id;
}
public String getRed5Name() {
return red5Name;
}
public void setRed5Name(String red5Name) {
this.red5Name = red5Name;
}
public Long getRed5CreateTime() {
return red5CreateTime;
}
public void setRed5CreateTime(Long red5CreateTime) {
this.red5CreateTime = red5CreateTime;
}
}
package com.state.user;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import com.state.Ram;
/**
* 用戶操作
*
* @author Allen 2017年3月31日
*
*/
public class UserState {
/**
* 插入用戶到房間中
* @param red5Id
* @param red5Name
* @param red5CreateTime
* @param roomKey
* @return
*/
public boolean insert(String red5Id, String red5Name, Long red5CreateTime, String roomKey) {
try {
if (Ram.roomHm.containsKey(roomKey)) {
UserVo uvo = new UserVo();
uvo.setRed5Id(red5Id);
uvo.setRed5Name(red5Name);
uvo.setRed5CreateTime(red5CreateTime);
Ram.roomHm.get(roomKey).getUserList().add(uvo);
return true;
}
selectAll(roomKey);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 獲取房間中全部用戶
* @param roomKey
* @return
*/
public List<UserVo> selectAll(String roomKey) {
try {
if (Ram.roomHm.containsKey(roomKey)) {
Iterator<UserVo> it = Ram.roomHm.get(roomKey).getUserList().iterator();
System.out.println("================================================");
while (it.hasNext()) {
UserVo temp = it.next();
System.out.println(temp.getRed5Id() + "," + temp.getRed5Name() + ","
+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date(temp.getRed5CreateTime())));
}
System.out.println("================================================");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 刪除房間中某用戶
* @param redId
* @param roomKey
* @return
*/
public boolean delete(String redId, String roomKey) {
try {
if (Ram.roomHm.containsKey(roomKey)) {
Iterator<UserVo> it = Ram.roomHm.get(roomKey).getUserList().iterator();
while (it.hasNext()) {
if (it.next().getRed5Id().equals(redId))
{
it.remove();
break;
}
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 統計房間中用戶數
* @param roomKey
* @return
*/
public int count(String roomKey) {
return Ram.roomHm.containsKey(roomKey) ? Ram.roomHm.get(roomKey).getUserList().size() : 0;
}
}
package com.state.room;
import java.util.ArrayList;
import java.util.List;
import com.state.user.UserVo;
/**
* 房間VO
*
* @author Allen 2017年3月31日
*
*/
public class RoomVo {
private String roomKey;// 房間key
private String roomName;// 房間名
private List<UserVo> userList=new ArrayList<>();// 房間內用戶列表
public List<UserVo> getUserList() {
return userList;
}
public void setUserList(List<UserVo> userList) {
this.userList = userList;
}
public String getRoomKey() {
return roomKey;
}
public void setRoomKey(String roomKey) {
this.roomKey = roomKey;
}
public String getRoomName() {
return roomName;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
}
package com.state.room;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import com.state.Ram;
/**
* 房間操作
*
* @author Allen 2017年3月31日
*
*/
public class RoomState {
/**
* 創建一個房間信息
*
* @param roomKey
* @param roomName
* @return
*/
public boolean insert(String roomKey, String roomName) {
try {
if (!Ram.roomHm.containsKey(roomKey)) {
RoomVo rVo = new RoomVo();
rVo.setRoomName(roomName);
Ram.roomHm.put(roomKey, rVo);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 返回所有房間信息
*
* @return
*/
public List<RoomVo> selectAll() {
try {
List<RoomVo> resultList = new ArrayList<>();
Iterator<Entry<String, RoomVo>> it = Ram.roomHm.entrySet().iterator();
while (it.hasNext()) {
Entry<String, RoomVo> entry = it.next();
resultList.add(entry.getValue());
}
return resultList;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 刪除一個房間信息
*
* @param red5Id
* @return
*/
public boolean delete(String roomKey) {
try {
Ram.roomHm.remove(roomKey);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 統計房間總數
*
* @return
*/
public int count() {
return Ram.roomHm.size();
}
}
package com.service;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.ISubscriberStream;
import com.state.room.RoomState;
import com.state.user.UserState;
/**
*
* @author Allen 2017年4月7日
*
*/
public class Application extends MultiThreadedApplicationAdapter {
@Override
public boolean connect(IConnection conn) {
System.out.println("connect");
return super.connect(conn);
}
@Override
public void disconnect(IConnection arg0, IScope arg1) {
System.out.println("disconnect");
new UserState().delete(arg0.getSessionId(), arg0.getAttribute(arg0.getSessionId()).toString());
super.disconnect(arg0, arg1);
}
/**
* 開始發佈直播
*/
@Override
public void streamPublishStart(IBroadcastStream stream) {
System.out.println("[streamPublishStart]********** ");
System.out.println("發佈Key: " + stream.getPublishedName());
RoomState room = new RoomState();
room.insert(stream.getPublishedName(), "房間" + stream.getPublishedName());
System.out.println(
"發佈時間:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date(stream.getCreationTime())));
System.out.println("****************************** ");
}
/**
* 流結束
*/
@Override
public void streamBroadcastClose(IBroadcastStream arg0) {
RoomState room = new RoomState();
room.delete(arg0.getPublishedName());
super.streamBroadcastClose(arg0);
}
/**
* 用戶斷開播放
*/
@Override
public void streamSubscriberClose(ISubscriberStream arg0) {
new UserState().delete(arg0.getConnection().getSessionId(), arg0.getBroadcastStreamPublishName());
super.streamSubscriberClose(arg0);
}
/**
* 鏈接rtmp服務器
*/
@Override
public boolean appConnect(IConnection arg0, Object[] arg1) {
// TODO Auto-generated method stub
System.out.println("[appConnect]********** ");
System.out.println("請求域:" + arg0.getScope().getContextPath());
System.out.println("id:" + arg0.getClient().getId());
System.out.println("name:" + arg0.getClient().getId());
System.out.println("**************** ");
return super.appConnect(arg0, arg1);
}
/**
* 加入了rtmp服務器
*/
@Override
public boolean join(IClient arg0, IScope arg1) {
// TODO Auto-generated method stub
return super.join(arg0, arg1);
}
/**
* 開始播放流
*/
@Override
public void streamSubscriberStart(ISubscriberStream stream) {
System.out.println("[streamSubscriberStart]********** ");
System.out.println("播放域:" + stream.getScope().getContextPath());
System.out.println("播放Key:" + stream.getBroadcastStreamPublishName());
System.out.println("********************************* ");
String sessionId = stream.getConnection().getSessionId();
stream.getConnection().setAttribute(null, null);
new UserState().insert(sessionId, sessionId, stream.getCreationTime(), stream.getBroadcastStreamPublishName());
super.streamSubscriberStart(stream);
}
/**
* 離開了rtmp服務器
*/
@Override
public void leave(IClient arg0, IScope arg1) {
System.out.println("leave");
super.leave(arg0, arg1);
}
}
重新發布啓動後,如果新的application中的console沒有打印,則可能是新版本發佈失敗。
[appConnect]**********
請求域:/liveOnline
id:1
name:1
****************
SpringMVC
注意一下:Spring jar包我們放到WEB-INF/libs 中,然後引用它,這裏如果使用默認的lib目錄,最後發佈red5+springMVC的時候red5的rtmp會無法訪問,會拋出Scope not found!!!!雖然這個問題在google百度github都沒有解釋,但是我最後還是找到的解決方案!如果你用lib沒有問題,那麼只能說是系統環境了。。我用的是win7 64bit。linux或者mac或許沒問題吧
首先我們把WEB-INF 下面的lib改成libs,如果沒有lib新建一個libs即可
我們將spring的jar包拷貝到libs,爲什麼選擇4.3.6因爲我的red5 server下lib中的spring就是4.3.6的保持版本與red5一致!
Add JARS到項目
web.xml
<!-- ********************** Spring配置 ********************** -->
<!-- 配置DispatchcerServlet -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置Spring mvc下的配置文件的位置和名稱 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<!-- 靜態資源處理交給默認servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 配置@ResponseBody 保證返回值爲UTF-8 -->
<!-- 因爲StringHttpMessageConverter默認是ISO8859-1 -->
<bean id="utf8Charset" class="java.nio.charset.Charset"
factory-method="forName">
<constructor-arg value="UTF-8" />
</bean>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg ref="utf8Charset" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 配置自動掃描的包 -->
<context:component-scan base-package="com.action"></context:component-scan>
<!-- 配置視圖解析器 如何把handler 方法返回值解析爲實際的物理視圖 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
測試類
package com.action.room;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.state.Ram;
/**
*
* @author Allen 2017年4月10日
*
*/
@Controller
public class room {
@ResponseBody
@RequestMapping("/roomsize")
public String hello() {
return "當前房間數: "+Ram.roomHm.size() ;
}
}