/*
Milenia Grafter Server
Copyright (c) 2007-2008 by Milan Toth. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.milgra.server;
/**
ClientController class
@mail milgra@milgra.hu
@author Milan Toth
@version 20080315
Tasks of Clientcontroller
- Client initialization
- Invoke and RTMP message flow control
**/
import com.milgra.server.api.Client;
import com.milgra.server.api.Stream;
import com.milgra.server.api.Wrapper;
import com.milgra.server.api.WrapperMap;
import com.milgra.server.api.WrapperList;
import com.milgra.server.api.InvokeEvent;
import com.milgra.server.api.StatusEvent;
import com.milgra.server.api.IApplication;
import com.milgra.server.api.EventListener;
import com.milgra.server.encoder.Encoder;
import com.milgra.server.encoder.RtmpFactory;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.ArrayList;
public class ClientController extends OProcess
{
// static id to make client id creation atomic and global
public static long globalId = 0;
// client - related client api object
// application - related custom application instance
public Client client;
public IApplication application;
// closed - controller is closed
// accepted - controller is accepted
public boolean closed;
public boolean accepted;
// counter - rtmo update counter
// id - unique id
// mode - client mode, active or passive
public int counter;
public int stepRound;
public long id;
public long currentTime;
public String mode;
public String ip;
public String agent;
public String referrer;
public long ping;
public long bytesIn = 0;
public long bytesOut = 0;
public double bandIn = 0;
public double bandOut = 0;
// readStepClient - how much bytes to receive before next read message
// readStepServer - how much bytes to send before next read message from client
public int readStepClient;
public int readStepServer;
// lastRead - last read bytes
// lastBand - last bandwidth
// lastPing - last ping send
// lastUpdate - last update message
// lastPingRead - last ping received
public long lastRead;
public long lastBand;
public long lastPing;
public long lastPingRead;
// lastBytesIn - last received amount for bw calculus
// lastBytesOut - last sent amout for bw calculus
public long lastBytesIn;
public long lastBytesOut;
// event listeners
public EventListener streamListener;
public EventListener invokeListener;
public EventListener statusListener;
// controller
public SocketController socketController;
public StreamController streamController;
// packet containers
public ArrayList < RtmpPacket > incomingList;
public ArrayList < RtmpPacket > outgoingList;
// container for incoming invoke channels and ids
public HashMap < Double , String > incomingInvokes;
public HashMap < Double , String > outgoingInvokes;
/**
* ClientController constructor
*/
public ClientController ( )
{
// System.out.println( System.currentTimeMillis() + " ClientController.setApplication" );
// create
outgoingList = new ArrayList < RtmpPacket > ( );
incomingList = new ArrayList < RtmpPacket > ( );
incomingInvokes = new HashMap < Double , String > ( );
outgoingInvokes = new HashMap < Double , String > ( );
socketController = new SocketController( this );
streamController = new StreamController( this , socketController );
// set
// id - generate unique identifier for client
// mode - default mode is passive
// counter - update time counter
// closed - controller not closed by default
// accepted - controller is not accepted yet
id = globalId++;
mode = "passive";
counter = 0;
stepRound = Math.round( 1000 / Library.STEPTIME );
closed = false;
accepted = false;
// read stepping is 250000 by default
readStepClient = 250000;
readStepServer = 250000;
// init byte counters
lastRead = 0;
lastBytesIn = 0;
lastBytesOut = 0;
// start
// invoke channels must be over 0
incomingInvokes.put( ( double ) 0 , null );
}
/**
* Sets host application for an active client
* @param customX
*/
public void setApplication ( IApplication customX )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.setApplication" );
// mode swithces to active
mode = "active";
// store host application
application = customX;
// pair client with application
Server.registerClient( customX , this );
}
/**
* Connects controller to an opened socket passed by ServerSocketConnector
* @param socketX
*/
public void connect ( SocketChannel channelX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.connect " + channelX + " " + closed);
if ( !closed )
{
// connecting socketcontroller to our socketchannel
socketController.connect( channelX , mode );
// registering processes
Server.registerProcess( this , "client" );
Server.registerProcess( socketController , "socket" );
Server.registerProcess( streamController , "stream" );
}
}
/**
* Connects controller to a remote server with a simple argument
* @param urlX remote server's url
* @param argumentsX connection arguments
*/
public void connect ( String urlX , Wrapper argumentX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.connect " + urlX );
connect( urlX , new WrapperList( argumentX ) );
}
/**
* Connects controller to a remote server with wrapperlist argument
* @param urlX remote server's url
* @param argumentsX connection arguments
*/
public void connect ( String urlX , WrapperList argumentsX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.connect " + urlX );
if ( !closed )
{
// url - part after rtmp://
// host - host address
// appid - application id
String url = urlX.substring( 7 );
String host = url.split( "/" )[0 ];
String appid = url.split( "/" )[1 ];
// invokeChannel - new inckeChannel
// info - invoke info message
// message - invoke rtmp packet
double invokeChannel = incomingInvokes.size( );
WrapperMap info = new WrapperMap( Library.CONNECTKEYS , Library.MACPLAYERARR );
RtmpPacket message = new RtmpPacket( );
WrapperList arguments = new WrapperList( );
// store application and tcUrl
info.put( "app" , appid );
info.put( "tcUrl" , urlX );
// message creation, no null needed after invokechannel at connect
arguments.add( new Wrapper( "connect" ) );
arguments.add( new Wrapper( invokeChannel ) );
arguments.add( new Wrapper( info ) );
arguments.addAll( argumentsX );
// rtmp message
message.bodyType = 0x14;
message.rtmpChannel = 0x03;
message.body = Encoder.encode( arguments );
// put packet on outgoing list
addOutgoingPacket( message );
// invokeChannel 1 is reserved for connect
incomingInvokes.put( invokeChannel , "connect" );
// start connecting
Server.socketConnector.connect( host , this );
}
}
/**
* Channel connection failed
*/
public void connectFailed ( )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.connectFailed " );
// status info
WrapperMap info = new WrapperMap( Library.STATUSKEYS , Library.FAILUREARR );
// dispatch status event
if ( statusListener != null ) statusListener.onEvent( new StatusEvent( info , client ) );
// close controller
close( );
}
/**
* Closes clientcontroller, cleanup
*/
public synchronized void close ( )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.close " );
// we are closed, no more invokes allowed
closed = true;
// if streamcontroller isn't closed
// if close isn't triggered by socketcontroller
if ( !streamController.closed ) streamController.close( );
if ( !socketController.closed ) socketController.close( "detach" );
// if we had an application
if ( application != null )
{
// if we were passive, telling application that we left
// if we were active, dispatch event to status listener
if ( mode.equals( "passive" ) ) dispatchLeave( );
if ( mode.equals( "active" ) ) dispatchClosure( );
}
// unpair client with application
Server.unregisterClient( application , this );
// unregister processes
Server.unregisterProcess( this , "client" );
Server.unregisterProcess( socketController , "socket" );
Server.unregisterProcess( streamController , "stream" );
}
/**
* Dispathes leave event to paired application
*/
public void dispatchLeave ( )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.dispatchLeave " );
// Syncing is needed, multiple clients from multiple threads can disconnect at the same time
synchronized ( application )
{
// notify application
application.onLeave( client );
}
}
/**
* Dispatches closure vent to status listeners
*/
public void dispatchClosure ( )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.dispatchClosure " );
// create status object
WrapperMap info = new WrapperMap( Library.STATUSKEYS , Library.CLOSUREARR );
// if listener exist dispatch
if ( statusListener != null ) statusListener.onEvent( new StatusEvent( info , client ) );
}
/**
* Synchronized packet pushing, multiple client threads can trigger this function
* @param packetX RtmpPacket
*/
public void addOutgoingPacket ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.addOutgoingPacket " + packetX );
synchronized ( outgoingList )
{
outgoingList.add( packetX );
}
}
/**
* Execution step
* syncing is needed from previous function
**/
public void step ( )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.step " + outgoingList.size() );
synchronized ( outgoingList )
{
// packets exchange
socketController.takePackets( outgoingList );
socketController.giveDataPackets( incomingList );
try
{
// decode packets
for ( RtmpPacket packet : incomingList ) receivePacket( packet );
}
catch ( Exception exception )
{
// notify user on standard output about the error
System.out.println( Library.CODEEX );
exception.printStackTrace( );
}
}
// update rtmp message every one second
if ( ++counter > stepRound )
{
update( );
counter = 0;
}
}
/**
* updates RTMP flow related events
**/
public void update ( )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.update" );
currentTime = System.currentTimeMillis( );
if ( accepted )
{
// sending out ping
if ( currentTime - lastPing > Library.PINGTIME ) sendPing( 6 , ( int ) lastPing & 0xffffffff , -1 , -1 );
// updating bandwidth information
if ( currentTime - lastBand > Library.BANDTIME ) updateBand( currentTime - lastBand );
}
}
/**
* Updates bandwidth data
* @param delayX time delay since last band update
**/
public void updateBand ( long delayX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.updateBand " + socketController.bytesIn );
lastBand = System.currentTimeMillis( );
bytesIn = socketController.bytesIn;
bytesOut = socketController.bytesOut;
bandIn = ( bytesIn - lastBytesIn ) / ( delayX / 1000 );
bandOut = ( bytesOut - lastBytesOut ) / ( delayX / 1000 );
lastBytesIn = bytesIn;
lastBytesOut = bytesOut;
// if we exceeded incoming byte step, send new read
if ( bytesIn - lastRead > readStepClient ) sendRead( );
}
/**
* Sorts incoming data packets
* @param packetX RtmpPacket packet to sort
**/
public void receivePacket ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.receivePacket " + packetX );
switch ( packetX.bodyType )
{
// 0x03 - Read amount
// 0x04 - Ping received
// 0x05 - Server or donwloading bandwidth
// 0x06 - Clint or uploading bandwidth
// 0x12 - Stream metadata
// 0x14 - Invoke
case 0x03 : receiveRead( packetX ); break;
case 0x04 : receivePing( packetX ); break;
case 0x05 : receiveReadStepClient( packetX ); break;
case 0x06 : receiveReadStepServer( packetX ); break;
case 0x12 : receiveInvoke( packetX ); break;
case 0x14 : receiveInvoke( packetX ); break;
default : receiveUnknown( packetX ); break;
}
}
/**
* Sends read bytes amount to client
**/
public void sendRead ( )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.sendRead " + bytesIn );
RtmpPacket packet = new RtmpPacket( );
// sending received bytes amount as a 4 byte plain integer
packet.rtmpChannel = 0x02;
packet.bodyType = 0x03;
packet.body = Encoder.concatenate( Encoder.intToBytes( bytesIn , 8 ) );
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.sendRead " + Encoder.getHexa( packet.body ) );
lastRead = bytesIn;
addOutgoingPacket( packet );
}
/**
* Receives read notification from client, have to compare with our write amount, if there is a big
* difference, we have to decrease data output
* @param packetX RtmpPacket incoming packet
**/
public void receiveRead ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.receiveRead " + packetX );
long clientRead = Encoder.bytesToInt( packetX.body );
// System.out.println( System.currentTimeMillis() + " clientRead: " + clientRead + " difference: " + difference );
// if difference is big, drop frames
if ( clientRead < ( bytesOut - 10000 ) ) streamController.addDropping( );
// if difference is small, remove dropping
if ( clientRead >= ( bytesOut - 10000 ) ) streamController.removeDropping( );
}
/**
* Sends a ping message
* @param typeX type of ping
* @param p1X...p3X ping parts
**/
public void sendPing ( int typeX , int p1X , int p2X , int p3X )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.sendPing " + typeX + " " + p1X + " " +p2X + " " + p3X );
// checking ping timeout
if ( ping > Library.PINGTIMEOUT ) detach( );
else
{
addOutgoingPacket( RtmpFactory.ping( typeX , p1X , p2X , p3X ) );
lastPing = System.currentTimeMillis( );
}
}
/**
* Receives ping from client
* receivePing : rtmp flow control ping uzenetek. elso ket byte a tipus. 00 - stream reset , 01 - stream buffer clear,
* 03 - stream buffer meret beallitas , 06 - ping , 07 -pong, 08 - first ping talan
* @param packetX
**/
public void receivePing ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " RTMPFlowController.receivePing " + packetX );
byte [ ] body = packetX.body;
// extracting parts from body
int type = Encoder.bytesToInt( new byte [ ] { body[ 0 ] , body[ 1 ] } );
int part1 = Encoder.bytesToInt( new byte [ ] { body[ 2 ] , body[ 3 ] , body[ 4 ] , body[ 5 ] } );
int part2 = body.length > 6 ? Encoder.bytesToInt( new byte [ ] { body[ 6 ] , body[ 7 ] , body[ 8 ] , body[ 9 ] } ) : 0;
int part3 = body.length > 10 ? Encoder.bytesToInt( new byte [ ] { body[ 10 ] , body[ 11 ] , body[ 12 ] , body[13]} ) : 0;
switch ( type )
{
// stream buffer length, sending it to router, and sending buffer clear ping message
// part1 is the flv channel this case
case 3 : streamController.setBufferLength( part1 , part2 ); break;
// normal ping request, sending pong
case 6 : sendPing( 7 , part1 , part2 , part3 ); break;
// normal pong, checking roundtrip delay
case 7 : ping = System.currentTimeMillis() - lastPing; break;
// first ping ?
case 8 : break;
// unknown
// default : System.out.println( "Ping: " + Encoder.getHexa( packetX.body ) ); break;
}
}
/**
* Sends server ( server side download ) bandwidth in passive connection
* @param packetX
**/
public void sendReadStepServer ( int stepX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.sendReadStepServer " );
RtmpPacket packet = new RtmpPacket ( );
packet.bodyType = 0x05;
packet.rtmpChannel = 0x02;
packet.body = Encoder.intToBytes( stepX , 4 );
addOutgoingPacket( packet );
}
/**
* Receives server ( server side download ) bandwidth in active connection
* @param packetX
*/
public void receiveReadStepClient ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis() + " " + id + " ClientController.receiveReadStepClient " + packetX );
// System.out.println( Encoder.bytesToInt( packetX.body ) );
}
/**
* Sends client ( client side download ) bandwidth in passive connection
* @param packetX
**/
public void sendReadStepClient ( int multiX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.sendReadStepClient " );
RtmpPacket packet = new RtmpPacket ( );
packet.bodyType = 0x06;
packet.rtmpChannel = 0x02;
packet.body = Encoder.concatenate( Encoder.intToBytes( multiX , 4 ) , new byte [ ] { 0x02 } );
addOutgoingPacket( packet );
}
/**
* Receives client ( client side download ) bandwidth in active connection
* @param packetX
**/
public void receiveReadStepServer ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis() + " " + id + " RTMPFlowController.receiveReadStepServer " + packetX );
// System.out.println( Encoder.bytesToInt( packetX.body ) );
}
/**
* Unknown packet type
* @param packet
*/
public void receiveUnknown ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis() + " " + id + "Unknown RtmpPacket " + packetX.toString( ) );
}
/**
* Receives invoke
* @param packetX rtmp packet
**/
public void receiveInvoke ( RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.receiveInvoke " );
try
{
// decode packet
WrapperList arguments = Encoder.decode( packetX.body );
String invokeId = arguments.getString( 0 );
// System.out.println( "invoke: " + invokeId );
if ( invokeId.equals( "receiveAudio" ) ) streamController.onAudioReceiveState ( arguments , packetX ); else
if ( invokeId.equals( "receiveVideo" ) ) streamController.onVideoReceiveState ( arguments , packetX ); else
if ( invokeId.equals( "createStream" ) ) streamController.onStreamCreateRequest ( arguments , packetX ); else
if ( invokeId.equals( "deleteStream" ) ) streamController.onStreamDeleteRequest ( arguments , packetX ); else
if ( invokeId.equals( "closeStream" ) ) streamController.onStreamCloseRequest ( arguments , packetX ); else
if ( invokeId.equals( "play" ) ) streamController.onStreamPlayRequest ( arguments , packetX ); else
if ( invokeId.equals( "pause" ) ) streamController.onStreamPauseRequest ( arguments , packetX ); else
if ( invokeId.equals( "seek" ) ) streamController.onStreamSeekRequest ( arguments , packetX ); else
if ( invokeId.equals( "speed" ) ) streamController.onStreamSpeedRequest ( arguments , packetX ); else
if ( invokeId.equals( "publish" ) ) streamController.onStreamPublishRequest( arguments , packetX ); else
if ( invokeId.equals( "measure" ) ) onMeasure ( arguments , packetX ); else
if ( invokeId.equals( "_error" ) ) onResult ( arguments , packetX ); else
if ( invokeId.equals( "_result" ) ) onResult ( arguments , packetX ); else
if ( invokeId.equals( "onStatus" ) ) onStatus ( arguments , packetX ); else
if ( invokeId.equals( "connect" ) ) onConnect ( arguments ); else
onInvoke ( arguments );
}
catch ( IOException exception )
{
// decode exception happened, trace it
System.out.println( Library.CODEEX );
exception.printStackTrace( );
// create status info
WrapperMap status = new WrapperMap( Library.STATUSKEYS , Library.AMFERRORARR );
status.put( "description" , exception.getMessage( ) );
call( "onStatus" , new Wrapper( status ) );
}
}
/**
* Connection object from client, connection initialization
* @param argumentsX Conneciton arguments
**/
public void onConnect ( WrapperList argumentsX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.onConnect " + argumentsX.size( ) );
// get info object, which is the third part after invoke id and channel
WrapperMap info = argumentsX.getMap( 2 );
String customId = info.get( "app" ).stringValue;
// get agent info
ip = socketController.socket.socket( ).getInetAddress( ).getHostName( );
agent = info.get( "flashVer" ).stringValue;
referrer = info.get( "swfUrl" ).stringValue;
application = Server.getApplication( customId );
if ( application != null )
{
// setting read notify stepping
sendReadStepServer( readStepServer );
sendReadStepClient( readStepClient );
// first ping event
//sendPing( 8 , 0 , -1 , -1 );
sendPing( 0 , 0 , -1 , -1 );
// remove unnecessary arguments
argumentsX.remove( 0 );
argumentsX.remove( 0 );
argumentsX.remove( 0 );
// enter client, sync is needed because multiple client threads can enter an application
synchronized ( application ) { application.onEnter( client , argumentsX ); }
}
else reject( new Wrapper( Library.NOINST + customId ) );
}
/**
* Normal invoke from client
* @param argumentsX arguments
**/
public void onInvoke ( WrapperList argumentsX )
{
if ( invokeListener != null )
{
String invokeId = argumentsX.getString( 0 );
double invokeChannel = argumentsX.getDouble( 1 );
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.onInvoke " + invokeId );
// remove id, channel and null
argumentsX.remove( 0 );
argumentsX.remove( 0 );
argumentsX.remove( 0 );
// store invoke id for further response, dispatch event
if ( invokeChannel != 0 ) outgoingInvokes.put( invokeChannel , invokeId );
invokeListener.onEvent( new InvokeEvent( invokeId , invokeChannel , client , argumentsX ) );
}
}
/**
* Invoke result from client.
* @param argumentsX arguments
**/
public void onResult ( WrapperList argumentsX , RtmpPacket packetX )
{
// get channel and id
double invokeChannel = argumentsX.getDouble( 1 );
String invokeId = incomingInvokes.remove( invokeChannel );
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.onResult " + invokeChannel + " " + invokeId );
if ( invokeId.equals( "connect" ) )
{
// get result info
WrapperMap info = argumentsX.getMap( 3 );
String code = info.getString( "code" );
// following invoke channels must be over 1
incomingInvokes.put( ( double ) 1 , invokeId );
// if success, set accepted true, else close
if ( code.equals( StatusEvent.SUCCESS ) )
{
accepted = true;
// dispatch success
if ( statusListener != null ) statusListener.onEvent( new StatusEvent( info , client ) );
}
else
{
// first dispatch close
if ( statusListener != null ) statusListener.onEvent( new StatusEvent( info , client ) );
// then close
close( );
}
}
else
if ( invokeId.equals( "createStream" ) )
{
// invoke result for createStream
streamController.onCreateStream( argumentsX.getDouble( 3 ) );
}
}
/**
* Status message from client
* @param argumentsX arguments info
* @param packetX rtmp packet
*/
public void onStatus ( WrapperList argumentsX , RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.onStatus " + argumentsX );
//for ( Wrapper wrapper : argumentsX ) System.out.println( wrapper );
if ( packetX.bodyType == 0x12 )
{
WrapperMap info = argumentsX.getMap( 1 );
for ( String key : info.keySet() ) System.out.println( key + " " + info.get( key ) );
return;
}
WrapperMap info = argumentsX.getMap( 3 );
if ( statusListener != null ) statusListener.onEvent( new StatusEvent( info , client ) );
}
/**
* Bandwidth meause request from client, storing read time and create string if needed
* @param argumentsX arguments
* @param packetX packet
**/
public void onMeasure ( WrapperList argumentsX , RtmpPacket packetX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.onMeasure " );
// store read time immediately
// extract chanel
double read = System.currentTimeMillis( );
double size = argumentsX.getMap( 3 ).getDouble( "size" );
double channel = argumentsX.getDouble( 1 );
String value;
String oldvalue = argumentsX.getMap( 3 ).getString( "value" );
// create string with desired length - desired bytes
//System.out.println( "size: " + size + " " + oldvalue.length( ) );
char [ ] chars = new char [ ( int ) size ];
value = new String( chars );
// create message
WrapperMap message = new WrapperMap( );
message.put( "read" , read );
message.put( "send" , System.currentTimeMillis( ) );
message.put( "value" , value );
//System.out.println( "read: " + (long)message.getDouble( "read" ) );
//System.out.println( "send: " + (long)message.getDouble( "send" ) );
//System.out.println( "value: " + message.getString( "value" ).length( ) );
// send back message with values
invoke( "_result" , channel , 0 , new WrapperList( new Wrapper( message ) ) );
}
/**
* Invoke calls to client
* @param idX Invoke Id
* @param argumentsX Invoke arguments
*/
public void call ( String callIdX ) { invoke( callIdX , 0 , 0 , new WrapperList( ) ); }
public void call ( String callIdX , Wrapper argumentX ) { invoke( callIdX , 0 , 0 , new WrapperList( argumentX ) ); }
public void call ( String callIdX , Wrapper argumentX , int flvChannelX ) { invoke( callIdX , 0 , flvChannelX , new WrapperList( argumentX ) ); }
public void call ( String callIdX , WrapperList argumentsX ) { invoke( callIdX , 0 , 0 , argumentsX ); }
public void call ( String callIdX , WrapperList argumentsX , int flvChannelX ) { invoke( callIdX , 0 , flvChannelX , argumentsX ); }
public void callResult ( double channelX , Wrapper argumentX ) { invoke( "_result" , channelX , 0 , new WrapperList( argumentX ) ); }
public void callResult ( double channelX , WrapperList argumentsX ) { invoke( "_result" , channelX , 0 , argumentsX ); }
public void callResponse ( String callIdX , WrapperList argumentsX , int flvChannelX )
{
double invokeChannel = incomingInvokes.size( );
incomingInvokes.put( invokeChannel , callIdX );
invoke( callIdX , invokeChannel , flvChannelX , argumentsX );
}
/**
* Invoke to client
* @param channelX invoke channel
* @param argumentsX arguments
**/
public void invoke ( String callIdX , double invokeChannelX , int flvChannelX , WrapperList argumentsX )
{
// System.out.println( System.currentTimeMillis() + " ClientController.invoke " + callIdX + " " + invokeChannelX );
// create invoke packet
RtmpPacket call = new RtmpPacket( );
WrapperList arguments = new WrapperList( );
// create invoke object
arguments.add( 0 , new Wrapper( callIdX ) );
arguments.add( 1 , new Wrapper( invokeChannelX ) );
arguments.add( 2 , new Wrapper( ) );
arguments.addAll( argumentsX );
// fill up packet
call.flvChannel = flvChannelX;
call.bodyType = 0x14;
call.body = Encoder.encode( arguments );
// send
if ( !closed ) addOutgoingPacket( call );
}
/**
* Accepts the client
* @param wrapperX - an information object passed to the client
**/
public void accept ( Wrapper wrapperX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.accept" + closed + " " + mode );
if ( !closed && mode.equals( "passive" ) )
{
// create acception status
WrapperMap info = new WrapperMap( Library.STATUSKEYS , Library.SUCCESSARR );
info.put( "application" , wrapperX );
RtmpPacket call = new RtmpPacket( );
WrapperList message = new WrapperList( );
// create invoke object
message.add( new Wrapper( "_result" ) );
message.add( new Wrapper( 1 ) );
message.add( new Wrapper( ) );
message.add( new Wrapper( info ) );
// fill up packet
call.flvChannel = 0;
call.bodyType = 0x14;
call.body = Encoder.encode( message );
// send back as result, register app
addOutgoingPacket( call );
Server.registerClient( application , this );
accepted = true;
}
}
/**
* Rejects the client, called from a custom applications. Sends a standard rejection object as onStatus
* @param wrapperX - an information object passed to the client
*/
public void reject ( Wrapper wrapperX )
{
// System.out.println( System.currentTimeMillis( ) + " " + id + " ClientController.reject closed: " + closed + " mode: " + mode );
if ( !closed && mode.equals( "passive" ))
{
// create rejection status
WrapperMap info = new WrapperMap( Library.STATUSKEYS , Library.REJECTIONARR );
RtmpPacket call = new RtmpPacket( );
WrapperList message = new WrapperList( );
info.put( "application" , wrapperX );
// send back as result
// create invoke object
message.add( new Wrapper( "_result" ) );
message.add( new Wrapper( 1 ) );
message.add( new Wrapper( ) );
message.add( new Wrapper( info ) );
// fill up packet
call.flvChannel = 0;
call.bodyType = 0x14;
call.body = Encoder.encode( message );
// send back as result, register app
addOutgoingPacket( call );
socketController.takePackets( outgoingList );
socketController.closeInited = true;
}
}
/**
* Other methods
*/
public void detach ( ) { socketController.close( "detach" ); }
public void addStreamEventListener ( EventListener streamListenerX ) { streamListener = streamListenerX; }
public void addInvokeEventListener ( EventListener invokeListenerX ) { invokeListener = invokeListenerX; }
public void addStatusEventListener ( EventListener statusListenerX ) { statusListener = statusListenerX; }
public HashMap < Integer , Stream > getPlayers ( ) { if ( !closed ) return streamController.getPlayers( ); else return null; }
public HashMap < Integer , Stream > getRouters ( ) { if ( !closed ) return streamController.getRouters( ); else return null; }
}