Streaming and database connection with red5 media server

Part One - The server application
Milan Toth
Milan Toth is the Chief Flash Developer of Jasmin Media Group, he created one of the world's biggest flash media server system. He loves Eclipse and OS X, AS3 and JAVA, sci-fi and horror, metal and electronic.
We need a database first, I used mySQL 5.0, download and install it from here. For administration, use its own admin tool, or download phpMyAdmin from here.
Create a new table named red5first_users in the test database. In this table, create four fileds: counter( int , auto increment, primary ), ip ( varchar 256 ) . referrer ( varchar 256 ) , flashversion ( varchar 256 ) , connection ( double ). Then create a new table named red5first_streams with four fields: counter ( int , auto increment , primary ) , id ( varchar 256 ) , filename ( varchar 256 ) , duration ( integer 11 ).
After this download the java mysql driver from mysql home. Copy mysql-connector-java*.jar to the Eclipse project root. Switch back to Eclipse, Project menuitem -> Properties -> Build Path -> Libraries -> Add External JARs, and choose the connector java from project root.
We also need to copy the driver to red5/lib, and export it to the java classpath, so the red5 application will reach it.
On os x, it looks like this:
export CLASSPATH=$CLASSPATH:/Applications/Red5/lib/mysql-connector-java-5.0.6-bin.jar
Application code:
package com.milgra;
// amf object
import org.red5.io.utils.ObjectMap;
import java.util.Map;
import java.util.Vector;
import java.util.Iterator;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.red5.server.api.IScope;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.stream.ISubscriberStream;
import org.red5.server.api.service.IServiceCapableConnection;
import org.red5.server.api.scheduling.IScheduledJob;
import org.red5.server.api.scheduling.ISchedulingService;
import org.red5.server.adapter.ApplicationAdapter;
// IScheduledJob implementation is needed for timing
public class Application extends ApplicationAdapter implements IScheduledJob
{
//dbase adress
private static final String url = "jdbc:mysql://127.0.0.1:3306/test";
private static final String user = "youruser";
private static final String pass = "yourpass";
//logger
private static final Log log = LogFactory.getLog( Application.class );
// we want class initialization at start, so its static
static
{
try
{
Class.forName("com.mysql.jdbc.Driver");
}
catch ( ClassNotFoundException exception )
{
log.error( "ClassNotFoundException " + exception.getMessage( ) );
}
}
public boolean appStart ( IScope scope )
{
//start timer, cheking user presence every second
addScheduledJob( 1000 , this );
return true;
}
public void appStop ( IScope scope )
{
log.info( "Red5First.appStop" );
}
public boolean appConnect( IConnection conn , Object[] params )
{
log.info( "Red5First.appConnect " + conn.getClient().getId() );
// getting client parameters
Map properties = conn.getConnectParams();
//connection time
Long stamp = System.currentTimeMillis( );
// client ip
String ip = (String)conn.getRemoteAddress( );
// agent
String agent = (String)properties.get( "flashVer" );
// referrer
String referrer = (String)properties.get( "swfUrl" );
// this will be our stream list
Object[ ] streamList = { };
try
{
// trying to connect
Connection mySQLConn = DriverManager.getConnection( url , user , pass );
// we put the parameters into the dbase
Statement sttmnt = mySQLConn.createStatement( );
String insert = "INSERT INTO "
+ "`test`.`red5first_users` "
+ "(`counter`,`ip`,`referrer`,`flashversion`,`connection`)"
+ "VALUES(NULL,'" + ip + "','" + referrer + "','" + agent + "','" + stamp + "')";
// execute query
sttmnt.executeUpdate( insert );
// getting stream list
String query = "SELECT * FROM "
+ "`red5first_streams`";
ResultSet streamSet = sttmnt.executeQuery( query );
// converting resultSet to ObjectMap-based Vector
Vector results = new Vector( );
int counter = 0;
while ( streamSet.next( ) )
{
int duration = streamSet.getInt( “duration” );
String id = streamSet.getString( “id” );
String filename = streamSet.getString( “filename” );
ObjectMap oneRow = new ObjectMap( );
oneRow.put( “id” , id );
oneRow.put( “filename” , filename );
oneRow.put( “duration” , duration );
results.add( oneRow );
counter++;
}
//creating streamList
streamList = new Object[counter];
for ( int a = 0 ; a < results.size( ) ; a++ )
streamList[a] = results.get( a );
}
catch ( SQLException exception )
{
log.error( "SQLException at appConnect: " + exception.getMessage( ) );
}
// if streamlist is not empty, sending it to client
if ( streamList.length != 0 )
{
IServiceCapableConnection iconn = (IServiceCapableConnection)conn;
iconn.invoke( "message" , new Object[] {streamList} );
}
IClient client = conn.getClient( );
// setting client timestamp to 0, because it doesn't started any stream
client.setAttribute( "stamp" , new Long( 0 ) );
return true;
}
public void appDisconnect( IConnection conn , Object[] params )
{
log.info( "Red5First.appDisconnect " + conn.getClient().getId() );
}
public void streamSubscriberStart ( ISubscriberStream stream )
{
log.info( "Red5First.streamSubscriberStart" );
// somebody started a stream
IConnection conn = stream.getConnection( );
IClient client = conn.getClient( );
Long stamp = (Long)client.getAttribute( "stamp" );
// if client isn't already watching, setting stamp
if ( stamp == 0 ) client.setAttribute( "stamp" , System.currentTimeMillis( ) );
}
public void streamSubscriberStop ( ISubscriberStream stream )
{
log.info( "Red5First.streamSubscriberStop" );
}
// this function is called every second by SchedulingService
public void execute ( ISchedulingService isservice )
{
log.info( "Red5First.execute" );
long now = System.currentTimeMillis( );
Iterator it = this.getScope( ).getClients( ).iterator( );
// iterating through clients
while ( it.hasNext() )
{
IClient client = ( IClient )it.next( );
long stamp = ( Long ) client.getAttribute( "stamp" );
// if client is watching a stream
if ( stamp > 0 )
{
long duration = ( now - stamp ) / 1000;
// if time is up, disconnect
if ( duration > 20 ) client.disconnect( );
}
}
}
}
And that's it.
Create a directory in red5/webapps/firstapp called streams, and copy a few flv streams here, then fill up red5first_streams table in the database with the attributes of these streams. Our application will reach them by default, because if we check WEB-INF/web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>Forbidden</web-resource-name>
<url-pattern>/streams/*</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
This folder will be enabled by Tomcat.
After Eclipse auto-built the application, copy your project's WEB-INF under red5/webapps/firstapp, and you can start the server.


