Created new red5 app, videoproxy and adapted VideoReceiver from bbb-bot using flazr.

TO-DO:
	Adapt VideoPublisher from bot
	Logic with url.
This commit is contained in:
Hugo Lazzari 2014-01-21 10:42:44 -02:00
parent ce4107bf50
commit 1379f2d086
74 changed files with 1015 additions and 19 deletions

View File

@ -41,7 +41,7 @@ import org.red5.server.stream.message.RTMPMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.red5.client.net.rtmp.RTMPClient;
import org.red5.client.net.rtmp.INetStreamEventHandler;
/**
@ -124,7 +124,7 @@ public class StreamingProxy implements IPushableConsumer, IPipeConnectionListene
synchronized public void pushMessage(IPipe pipe, IMessage message) throws IOException {
if (state >= PUBLISHED && message instanceof RTMPMessage) {
RTMPMessage rtmpMsg = (RTMPMessage) message;
rtmpClient.publishStreamData(streamId, rtmpMsg);
//rtmpClient.publishStreamData(streamId, rtmpMsg);
} else {
frameBuffer.add(message);
}
@ -152,10 +152,10 @@ public class StreamingProxy implements IPushableConsumer, IPipeConnectionListene
log.debug("<:{}", code);
if (StatusCodes.NS_PUBLISH_START.equals(code)) {
state = PUBLISHED;
rtmpClient.invoke("FCPublish", new Object[] { publishName }, this);
while (frameBuffer.size() > 0) {
rtmpClient.publishStreamData(streamId, frameBuffer.remove(0));
}
//rtmpClient.invoke("FCPublish", new Object[] { publishName }, this);
//while (frameBuffer.size() > 0) {
// rtmpClient.publishStreamData(streamId, frameBuffer.remove(0));
//}
}
}
@ -174,7 +174,7 @@ public class StreamingProxy implements IPushableConsumer, IPipeConnectionListene
if ("connect".equals(call.getServiceMethodName())) {
state = STREAM_CREATING;
System.out.println("CRIANDO STREAM");
rtmpClient.createStream(this);
//rtmpClient.createStream(this);
} else if ("createStream".equals(call.getServiceMethodName())) {
System.out.println("CRIANDO STREAM 2");
state = PUBLISHING;
@ -183,7 +183,7 @@ public class StreamingProxy implements IPushableConsumer, IPipeConnectionListene
Integer streamIdInt = (Integer) result;
streamId = streamIdInt.intValue();
log.debug("Publishing: {}", state);
rtmpClient.publish(streamIdInt.intValue(), publishName, publishMode, this);
//rtmpClient.publish(streamIdInt.intValue(), publishName, publishMode, this);
} else {
rtmpClient.disconnect();
state = STOPPED;

View File

@ -56,6 +56,13 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
@Override
public boolean appConnect(IConnection conn, Object[] params) {
log.info("oflaDemo appConnect");
System.out.println("TESTE 2");
System.out.println("TESTE 2");
System.out.println("TESTE 2");
System.out.println("TESTE 2");
System.out.println(conn);
System.out.println(params);
return super.appConnect(conn, params);
}
@ -104,15 +111,15 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
System.out.println("TESTE");
System.out.println("TESTE");
System.out.println("TESTE");
IScope scope = stream.getScope();
IBroadcastScope bsScope = getBroadcastScope(scope, stream.getPublishedName());
StreamingProxy proxy = new StreamingProxy();
proxy.setHost("0.0.0.0");
proxy.setApp("video");
proxy.setPort(1935);
proxy.init();
bsScope.subscribe(proxy, null);
proxy.start("MY_STRING", StreamingProxy.LIVE, null);
//IScope scope = stream.getScope();
//IBroadcastScope bsScope = getBroadcastScope(scope, stream.getPublishedName());
//StreamingProxy proxy = new StreamingProxy();
//proxy.setHost("143.54.10.163");
//proxy.setApp("video");
//proxy.setPort(1935);
//proxy.init();
//bsScope.subscribe(proxy, null);
//proxy.start("teste", StreamingProxy.LIVE, null);
//streamingProxyMap.put(stream.getPublishedName(), proxy);
//stream.addStreamListener(this);
}

131
bbb-videoproxy/build.gradle Executable file
View File

@ -0,0 +1,131 @@
usePlugin 'java'
usePlugin 'war'
usePlugin 'eclipse'
version = '0.7'
jar.enabled = true
archivesBaseName = 'videoproxy'
task dependencies(type: Copy) {
into('lib')
from configurations.default
from configurations.default.allArtifacts*.file
}
task resolveDeps(dependsOn: configurations.default.buildArtifacts, type: Copy) {
into('lib')
from configurations.default
from configurations.default.allArtifacts*.file
}
task resolveDependencies(dependsOn: configurations.default.buildArtifacts, type: Copy) {
into('lib')
from configurations.default
from configurations.default.allArtifacts*.file
}
repositories {
add(new org.apache.ivy.plugins.resolver.ChainResolver()) {
name = 'remote'
returnFirst = true
add(new org.apache.ivy.plugins.resolver.URLResolver()) {
name = "googlecode"
addArtifactPattern "http://red5.googlecode.com/svn/repository/[artifact](-[revision]).[ext]"
addArtifactPattern "http://red5.googlecode.com/svn/repository/[organisation]/[artifact](-[revision]).[ext]"
}
add(new org.apache.ivy.plugins.resolver.URLResolver()) {
name = "blindside-repos"
addArtifactPattern "http://blindside.googlecode.com/svn/repository/[artifact](-[revision]).[ext]"
addArtifactPattern "http://blindside.googlecode.com/svn/repository/[organisation]/[artifact](-[revision]).[ext]"
}
add(new org.apache.ivy.plugins.resolver.URLResolver()) {
name = "maven2-central"
m2compatible = true
addArtifactPattern "http://repo1.maven.org/maven2/[organisation]/[module]/[revision]/[artifact](-[revision]).[ext]"
addArtifactPattern "http://repo1.maven.org/maven2/[organisation]/[artifact]/[revision]/[artifact](-[revision]).[ext]"
}
add(new org.apache.ivy.plugins.resolver.URLResolver()) {
name = "testng_ibiblio_maven2"
m2compatible = true
addArtifactPattern "http://repo1.maven.org/maven2/[organisation]/[module]/[revision]/[artifact](-[revision])-jdk15.[ext]"
addArtifactPattern "http://repo1.maven.org/maven2/[organisation]/[artifact]/[revision]/[artifact](-[revision])-jdk15.[ext]"
}
add(new org.apache.ivy.plugins.resolver.URLResolver()) {
name = "netty-dependency"
m2compatible = true
addArtifactPattern "http://repository.jboss.org/nexus/content/groups/public-jboss/[organisation]/[module]/[revision]/[artifact](-[revision]).[ext]"
addArtifactPattern "http://repo1.maven.org/maven2/[organisation]/[artifact]/[revision]/[artifact](-[revision]).[ext]"
}
add(new org.apache.ivy.plugins.resolver.URLResolver()) {
name = "spring-bundles"
m2compatible = true
addArtifactPattern "http://repository.springsource.com/maven/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
addArtifactPattern "http://repository.springsource.com/maven/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
}
}
}
dependencies {
// Servlet
providedCompile 'javax.servlet:servlet-api:2.5@jar'
// Mina
providedCompile 'org.apache.mina:mina-core:2.0.7@jar'
providedCompile 'org.apache.mina:mina-integration-beans:2.0.7@jar'
providedCompile 'org.apache.mina:mina-integration-jmx:2.0.7@jar'
// Spring
providedCompile 'org.springframework:spring-web:3.1.1.RELEASE@jar'
providedCompile 'org.springframework:spring-beans:3.1.1.RELEASE@jar'
providedCompile 'org.springframework:spring-context:3.1.1.RELEASE@jar'
providedCompile 'org.springframework:spring-core:3.1.1.RELEASE@jar'
// Red5
providedCompile 'org/red5:red5:1.0r4643@jar'
compile files('./lib/flazr-git.jar')
compile files('./lib/netty-3.2.7.Final.jar')
// Logging
providedCompile 'ch.qos.logback:logback-core:1.0.9@jar'
providedCompile 'ch.qos.logback:logback-classic:1.0.9@jar'
providedCompile 'org.slf4j:log4j-over-slf4j:1.7.2@jar'
providedCompile 'org.slf4j:jcl-over-slf4j:1.7.2@jar'
providedCompile 'org.slf4j:jul-to-slf4j:1.7.2@jar'
providedCompile 'org.slf4j:slf4j-api:1.7.2@jar'
// Needed for the JVM shutdown hook but needs to be put into red5/lib dir.
// Otherwise we get exception on aop utils class not found.
providedCompile 'org.springframework:spring-aop:3.0.6.RELEASE@jar'
compile 'aopalliance:aopalliance:1.0@jar'
// Java Concurrency In Practice
providedCompile 'net.jcip:jcip-annotations:1.0@jar'
// Testing
compile 'org.testng:testng:5.8@jar'
compile 'org.easymock:easymock:2.4@jar'
//redis
compile 'redis.clients:jedis:2.0.0'
providedCompile 'commons-pool:commons-pool:1.5.6'
}
test {
useTestNG()
}
war.doLast {
ant.unzip(src: war.archivePath, dest: "$buildDir/videoproxy")
}
task deploy() << {
def red5AppsDir = '/usr/share/red5/webapps'
def videoDir = new File("${red5AppsDir}/videoproxy")
if (videoDir.exists()) ant.delete(dir: videoDir)
ant.mkdir(dir: videoDir)
ant.copy(todir: videoDir) {
fileset(dir: "$buildDir/videoproxy")
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="VIDEOPROXY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/videoproxy.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>log/videoproxy.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 5 days worth of history -->
<MaxHistory>5</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [%thread] %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="VIDEOPROXY" />
</root>
</configuration>

Binary file not shown.

View File

@ -0,0 +1,4 @@
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: 20.0-b12 (Sun Microsystems Inc.)

View File

@ -0,0 +1,2 @@
redis.host=127.0.0.1
redis.port=6379

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="VIDEOPROXY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/videoproxy.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>log/videoproxy.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 5 days worth of history -->
<MaxHistory>5</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [%thread] %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="VIDEOPROXY" />
</root>
</configuration>

View File

@ -0,0 +1,2 @@
webapp.contextPath=/videoproxy
webapp.virtualHosts=*, localhost, localhost:8088, 127.0.0.1:8088

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>/WEB-INF/red5-web.properties</value>
<value>/WEB-INF/bigbluebutton-video.properties</value>
</list>
</property>
</bean>
<bean id="web.context" class="org.red5.server.Context"
autowire="byType" />
<bean id="web.scope" class="org.red5.server.scope.WebScope" init-method="register">
<property name="server" ref="red5.server" />
<property name="parent" ref="global.scope" />
<property name="context" ref="web.context" />
<property name="handler" ref="web.handler" />
<property name="contextPath" value="${webapp.contextPath}" />
<property name="virtualHosts" value="${webapp.virtualHosts}" />
</bean>
<bean id="web.handler" class="org.bigbluebutton.app.videoproxy.VideoApplication">
<property name="recordVideoStream" value="true"/>
<property name="eventRecordingService" ref="redisRecorder"/>
</bean>
<bean id="redisRecorder" class="org.bigbluebutton.app.videoproxy.EventRecordingService">
<constructor-arg index="0" value="${redis.host}"/>
<constructor-arg index="1" value="${redis.port}"/>
</bean>
</beans>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>BigBlueButton Video Proxy Application</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>/videoproxy</param-value>
</context-param>
<listener>
<listener-class>org.red5.logging.ContextLoggingListener</listener-class>
</listener>
<filter>
<filter-name>LoggerContextFilter</filter-name>
<filter-class>org.red5.logging.LoggerContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggerContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Forbidden</web-resource-name>
<url-pattern>/streams/*</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
</web-app>

View File

@ -0,0 +1 @@
Do not delete the streams directory. This is where the video recordings will be saved.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,43 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.app.videoproxy;
import java.util.Map;
import redis.clients.jedis.Jedis;
public class EventRecordingService {
private static final String COLON = ":";
private final String host;
private final int port;
public EventRecordingService(String host, int port) {
this.host = host;
this.port = port;
}
public void record(String meetingId, Map<String, String> event) {
Jedis jedis = new Jedis(host, port);
Long msgid = jedis.incr("global:nextRecordedMsgId");
jedis.hmset("recording:" + meetingId + COLON + msgid, event);
jedis.rpush("meeting:" + meetingId + COLON + "recordings", msgid.toString());
}
}

View File

@ -0,0 +1,88 @@
package org.bigbluebutton.app.videoproxy;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.flazr.rtmp.client.ClientHandler;
import com.flazr.rtmp.client.ClientOptions;
public abstract class RtmpConnection extends ClientHandler implements ChannelFutureListener {
private static final Logger log = LoggerFactory.getLogger(RtmpConnection.class);
public RtmpConnection(ClientOptions options) {
super(options);
}
private ClientBootstrap bootstrap = null;
private ChannelFuture future = null;
private ChannelFactory factory = null;
public boolean connect() {
if (factory == null)
factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
bootstrap = new ClientBootstrap(factory);
bootstrap.setPipelineFactory(pipelineFactory());
future = bootstrap.connect(new InetSocketAddress(options.getHost(), options.getPort()));
future.addListener(this);
return true;
}
public void disconnect() {
if (future != null) {
if (future.getChannel().isConnected()) {
log.debug("Channel is connected, disconnecting");
future.getChannel().close(); //ClosedChannelException
future.getChannel().getCloseFuture().awaitUninterruptibly();
}
future.removeListener(this);
factory.releaseExternalResources();
future = null; factory = null; bootstrap = null;
}
}
abstract protected ChannelPipelineFactory pipelineFactory();
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess())
onConnectedSuccessfully();
else
onConnectedUnsuccessfully();
}
protected void onConnectedUnsuccessfully() {
}
protected void onConnectedSuccessfully() {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
String exceptionMessage = e.getCause().getMessage();
if (exceptionMessage != null && exceptionMessage.contains("ArrayIndexOutOfBoundsException") && exceptionMessage.contains("bad value / byte: 101 (hex: 65)")) {
log.debug("Ignoring malformed metadata");
return;
} else {
super.exceptionCaught(ctx, e);
//for (OnExceptionListener listener : context.getExceptionListeners())
// listener.onException(e.getCause());
}
}
}

View File

@ -0,0 +1,165 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.app.videoproxy;
import java.util.HashMap;
import java.util.Map;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.scope.IBasicScope;
import org.red5.server.api.scope.IBroadcastScope;
import org.red5.server.api.scope.ScopeType;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.IServerStream;
import org.red5.server.api.stream.IStreamListener;
import org.red5.server.stream.ClientBroadcastStream;
import org.slf4j.Logger;
public class VideoApplication extends MultiThreadedApplicationAdapter {
private static Logger log = Red5LoggerFactory.getLogger(VideoApplication.class, "video");
private IScope appScope;
private IServerStream serverStream;
private boolean recordVideoStream = false;
private EventRecordingService recordingService;
private final Map<String, IStreamListener> streamListeners = new HashMap<String, IStreamListener>();
@Override
public boolean appStart(IScope app) {
super.appStart(app);
log.info("oflaDemo appStart");
System.out.println("oflaDemo appStart");
appScope = app;
return true;
}
@Override
public boolean appConnect(IConnection conn, Object[] params) {
log.info("oflaDemo appConnect");
return super.appConnect(conn, params);
}
@Override
public void appDisconnect(IConnection conn) {
log.info("oflaDemo appDisconnect");
if (appScope == conn.getScope() && serverStream != null) {
serverStream.close();
}
super.appDisconnect(conn);
}
@Override
public void streamPublishStart(IBroadcastStream stream) {
super.streamPublishStart(stream);
}
public IBroadcastScope getBroadcastScope(IScope scope, String name) {
IBasicScope basicScope = scope.getBasicScope(ScopeType.BROADCAST, name);
if (!(basicScope instanceof IBroadcastScope)) {
return null;
} else {
return (IBroadcastScope) basicScope;
}
}
@Override
public void streamBroadcastStart(IBroadcastStream stream) {
IConnection conn = Red5.getConnectionLocal();
super.streamBroadcastStart(stream);
log.info("streamBroadcastStart " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + conn.getScope().getName());
System.out.println("TESTE");
System.out.println(conn.getScope().getName() + "-" + stream.getPublishedName());
//VideoProxyReceiver(String host, String appPath, String conference, String streamName)
/*if (recordVideoStream) {
recordStream(stream);
VideoStreamListener listener = new VideoStreamListener();
listener.setEventRecordingService(recordingService);
stream.addStreamListener(listener);
streamListeners.put(conn.getScope().getName() + "-" + stream.getPublishedName(), listener);
}
*/
VideoProxyReceiver v = new VideoProxyReceiver("143.54.10.63", "videoproxy", "conferencia", stream.getPublishedName());
v.start();
}
@Override
public void streamBroadcastClose(IBroadcastStream stream) {
IConnection conn = Red5.getConnectionLocal();
super.streamBroadcastClose(stream);
if (recordVideoStream) {
IStreamListener listener = streamListeners.remove(conn.getScope().getName() + "-" + stream.getPublishedName());
if (listener != null) {
stream.removeStreamListener(listener);
}
long publishDuration = (System.currentTimeMillis() - stream.getCreationTime()) / 1000;
log.info("streamBroadcastClose " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + conn.getScope().getName());
Map<String, String> event = new HashMap<String, String>();
event.put("module", "WEBCAM");
event.put("timestamp", new Long(System.currentTimeMillis()).toString());
event.put("meetingId", conn.getScope().getName());
event.put("stream", stream.getPublishedName());
event.put("duration", new Long(publishDuration).toString());
event.put("eventName", "StopWebcamShareEvent");
recordingService.record(conn.getScope().getName(), event);
}
}
/**
* A hook to record a stream. A file is written in webapps/video/streams/
* @param stream
*/
private void recordStream(IBroadcastStream stream) {
IConnection conn = Red5.getConnectionLocal();
long now = System.currentTimeMillis();
String recordingStreamName = stream.getPublishedName(); // + "-" + now; /** Comment out for now...forgot why I added this - ralam */
try {
log.info("Recording stream " + recordingStreamName );
ClientBroadcastStream cstream = (ClientBroadcastStream) this.getBroadcastStream(conn.getScope(), stream.getPublishedName());
cstream.saveAs(recordingStreamName, false);
} catch(Exception e) {
log.error("ERROR while recording stream " + e.getMessage());
e.printStackTrace();
}
}
public void setRecordVideoStream(boolean recordVideoStream) {
this.recordVideoStream = recordVideoStream;
}
public void setEventRecordingService(EventRecordingService s) {
recordingService = s;
}
}

View File

@ -0,0 +1,94 @@
/*
* GT-Mconf: Multiconference system for interoperable web and mobile
* http://www.inf.ufrgs.br/prav/gtmconf
* PRAV Labs - UFRGS
*
* This file is part of Mconf-Mobile.
*
* Mconf-Mobile is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mconf-Mobile is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Mconf-Mobile. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bigbluebutton.app.videoproxy;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.flazr.rtmp.client.ClientOptions;
import com.flazr.rtmp.message.Video;
import com.flazr.util.Utils;
public class VideoProxyReceiver {
protected class VideoConnection extends VideoProxyReceiverConnection {
public VideoConnection(ClientOptions options) {
super(options);
}
@Override
protected void onVideo(Video video) {
VideoProxyReceiver.this.onVideo(video);
}
}
private static final Logger log = LoggerFactory.getLogger(VideoProxyReceiver.class);
private String streamName;
private VideoConnection videoConnection;
public VideoProxyReceiver(String host, String appPath, String conference, String streamName) {
System.out.println("INIT");
ClientOptions opt = new ClientOptions();
opt.setClientVersionToUse(Utils.fromHex("00000000"));
opt.setHost(host);
opt.setAppName(appPath + "/" + conference);
this.streamName = streamName;
opt.setWriterToSave(null);
opt.setStreamName(streamName);
videoConnection = new VideoConnection(opt);
System.out.println("COMP");
}
protected void onVideo(Video video) {
//Retransmit video package
//Received Video
System.out.println("Received Video data " + video.getHeader().getTime());
log.debug("received video package: {}", video.getHeader().getTime());
}
public void start() {
if (videoConnection != null)
videoConnection.connect();
}
public void stop() {
if (videoConnection != null)
videoConnection.disconnect();
}
public String getStreamName() {
return streamName;
}
}

View File

@ -0,0 +1,82 @@
/*
* GT-Mconf: Multiconference system for interoperable web and mobile
* http://www.inf.ufrgs.br/prav/gtmconf
* PRAV Labs - UFRGS
*
* This file is part of Mconf-Mobile.
*
* Mconf-Mobile is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mconf-Mobile is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Mconf-Mobile. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bigbluebutton.app.videoproxy;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.flazr.rtmp.RtmpDecoder;
import com.flazr.rtmp.RtmpEncoder;
import com.flazr.rtmp.RtmpMessage;
import com.flazr.rtmp.client.ClientHandshakeHandler;
import com.flazr.rtmp.client.ClientOptions;
import com.flazr.rtmp.message.Command;
import com.flazr.rtmp.message.MessageType;
import com.flazr.rtmp.message.Video;
public abstract class VideoProxyReceiverConnection extends RtmpConnection {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(VideoProxyReceiverConnection.class);
public VideoProxyReceiverConnection(ClientOptions options) {
super(options);
}
@Override
protected ChannelPipelineFactory pipelineFactory() {
return new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
final ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("handshaker", new ClientHandshakeHandler(options));
pipeline.addLast("decoder", new RtmpDecoder());
pipeline.addLast("encoder", new RtmpEncoder());
pipeline.addLast("handler", VideoProxyReceiverConnection.this);
return pipeline;
}
};
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
options.setArgs((Object[]) null);
writeCommandExpectingResult(e.getChannel(), Command.connect(options));
}
@Override
protected void onMultimedia(Channel channel, RtmpMessage message) {
super.onMultimedia(channel, message);
if (message.getHeader().getMessageType() == MessageType.VIDEO) {
onVideo((Video) message);
}
}
abstract protected void onVideo(Video video);
}

View File

@ -0,0 +1,80 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.app.videoproxy;
import java.util.HashMap;
import java.util.Map;
import org.apache.mina.core.buffer.IoBuffer;
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.IStreamListener;
import org.red5.server.api.stream.IStreamPacket;
import org.red5.server.net.rtmp.event.VideoData;
/**
* Class to listen for the first video packet of the webcam.
* We need to listen for the first packet and send a startWebcamEvent.
* The reason is that when starting the webcam, sometimes Flash Player
* needs to prompt the user for permission to access the webcam. However,
* while waiting for the user to click OK to the prompt, Red5 has already
* called the startBroadcast method which we take as the start of the recording.
* When the user finally clicks OK, the packets then start to flow through.
* This introduces a delay of when we assume the start of the recording and
* the webcam actually publishes video packets. When we do the ingest and
* processing of the video and multiplex the audio, the video and audio will
* be un-synched by at least this amount of delay.
* @author Richard Alam
*
*/
public class VideoStreamListener implements IStreamListener {
private EventRecordingService recordingService;
private volatile boolean firstPacketReceived = false;
@Override
public void packetReceived(IBroadcastStream stream, IStreamPacket packet) {
IoBuffer buf = packet.getData();
if (buf != null)
buf.rewind();
if (buf == null || buf.remaining() == 0){
return;
}
if (packet instanceof VideoData) {
if (! firstPacketReceived) {
firstPacketReceived = true;
IConnection conn = Red5.getConnectionLocal();
Map<String, String> event = new HashMap<String, String>();
event.put("module", "WEBCAM");
event.put("timestamp", new Long(System.currentTimeMillis()).toString());
event.put("meetingId", conn.getScope().getName());
event.put("stream", stream.getPublishedName());
event.put("eventName", "StartWebcamShareEvent");
recordingService.record(conn.getScope().getName(), event);
}
}
}
public void setEventRecordingService(EventRecordingService s) {
recordingService = s;
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="VIDEOPROXY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/videoproxy.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>log/videoproxy.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 5 days worth of history -->
<MaxHistory>5</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [%thread] %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="VIDEOPROXY" />
</root>
</configuration>

View File

@ -0,0 +1,2 @@
redis.host=127.0.0.1
redis.port=6379

View File

@ -0,0 +1,2 @@
webapp.contextPath=/videoproxy
webapp.virtualHosts=*, localhost, localhost:8088, 127.0.0.1:8088

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>/WEB-INF/red5-web.properties</value>
<value>/WEB-INF/bigbluebutton-video.properties</value>
</list>
</property>
</bean>
<bean id="web.context" class="org.red5.server.Context"
autowire="byType" />
<bean id="web.scope" class="org.red5.server.scope.WebScope" init-method="register">
<property name="server" ref="red5.server" />
<property name="parent" ref="global.scope" />
<property name="context" ref="web.context" />
<property name="handler" ref="web.handler" />
<property name="contextPath" value="${webapp.contextPath}" />
<property name="virtualHosts" value="${webapp.virtualHosts}" />
</bean>
<bean id="web.handler" class="org.bigbluebutton.app.videoproxy.VideoApplication">
<property name="recordVideoStream" value="true"/>
<property name="eventRecordingService" ref="redisRecorder"/>
</bean>
<bean id="redisRecorder" class="org.bigbluebutton.app.videoproxy.EventRecordingService">
<constructor-arg index="0" value="${redis.host}"/>
<constructor-arg index="1" value="${redis.port}"/>
</bean>
</beans>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>BigBlueButton Video Proxy Application</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>/videoproxy</param-value>
</context-param>
<listener>
<listener-class>org.red5.logging.ContextLoggingListener</listener-class>
</listener>
<filter>
<filter-name>LoggerContextFilter</filter-name>
<filter-class>org.red5.logging.LoggerContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggerContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Forbidden</web-resource-name>
<url-pattern>/streams/*</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
</web-app>

View File

@ -0,0 +1 @@
Do not delete the streams directory. This is where the video recordings will be saved.

View File

@ -46,7 +46,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
public function get uri():String {
LogUtil.debug("URL eh: " + _attributes.uri + "/" + _attributes.room);
return _attributes.uri + "/" + _attributes.room;
//return _attributes.uri + "/" + _attributes.room;
return "rtmp://143.54.10.63/videoproxy" + "/" + _attributes.room;
}
public function get username():String {

View File

@ -69,7 +69,9 @@ package org.bigbluebutton.modules.videoconf.business
}
public function connect():void {
nc.connect(_url);
LogUtil.debug("OLD: " + _url);
_url = "rtmp://143.54.10.63/videoproxy/conferencia";
nc.connect(_url);
}
private function onAsyncError(event:AsyncErrorEvent):void{

View File

@ -215,6 +215,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
LogUtil.debug("Playing MY_STRING 2");
this.streamName = "MY_STRING";
ns.play("MY_STRING");
this.width = _video.width + paddingHorizontal;
this.height = _video.height + paddingVertical;