All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.red5.client.StreamRelay Maven / Gradle / Ivy

/*
 * RED5 Open Source Flash Server - https://github.com/Red5/ Copyright 2006-2015 by respective authors (see below). All rights reserved. Licensed under the Apache License, Version
 * 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
 * required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */

package org.red5.client;

import java.io.IOException;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.red5.client.net.rtmp.ClientExceptionHandler;
import org.red5.client.net.rtmp.INetStreamEventHandler;
import org.red5.client.net.rtmp.RTMPClient;
import org.red5.io.utils.ObjectMap;
import org.red5.proxy.StreamingProxy;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.event.IEventDispatcher;
import org.red5.server.api.service.IPendingServiceCall;
import org.red5.server.api.service.IPendingServiceCallback;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.net.rtmp.event.Notify;
import org.red5.server.net.rtmp.status.StatusCodes;
import org.red5.server.stream.message.RTMPMessage;

/**
 * Relay a stream from one location to another via RTMP.
 *
 * @author Paul Gregoire ([email protected])
 */
public class StreamRelay {

    // our consumer
    private static RTMPClient client;

    // our publisher
    private static StreamingProxy proxy;

    // task timer
    private static Timer timer;

    // the source being relayed
    private static String sourceStreamName;

    /**
     * Creates a stream client to consume a stream from an end point and a proxy to relay the stream to another end point.
     *
     * @param args
     *            application arguments
     */
    public static void main(String... args) {
        // handle the args
        if (args == null || args.length < 7) {
            System.out.println("Not enough args supplied. Usage:       ");
        } else {
            // parse the args
            String sourceHost = args[0], destHost = args[3];
            String sourceApp = args[1], destApp = args[4];
            int sourcePort = 1935, destPort = 1935;
            sourceStreamName = args[2];
            String destStreamName = args[5];
            String publishMode = args[6]; //live, record, or append
            // look to see if port was included in host string
            int colonIdx = sourceHost.indexOf(':');
            if (colonIdx > 0) {
                sourcePort = Integer.valueOf(sourceHost.substring(colonIdx + 1));
                sourceHost = sourceHost.substring(0, colonIdx);
                System.out.printf("Source host: %s port: %d\n", sourceHost, sourcePort);
            }
            colonIdx = destHost.indexOf(':');
            if (colonIdx > 0) {
                destPort = Integer.valueOf(destHost.substring(colonIdx + 1));
                destHost = destHost.substring(0, colonIdx);
                System.out.printf("Destination host: %s port: %d\n", destHost, destPort);
            }
            // create a timer
            timer = new Timer();
            // create our publisher
            proxy = new StreamingProxy();
            proxy.setHost(destHost);
            proxy.setPort(destPort);
            proxy.setApp(destApp);
            proxy.init();
            proxy.setConnectionClosedHandler(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Publish connection has been closed, source will be disconnected");
                    client.disconnect();
                }
            });
            proxy.setExceptionHandler(new ClientExceptionHandler() {
                @Override
                public void handleException(Throwable throwable) {
                    throwable.printStackTrace();
                    System.exit(2);
                }
            });
            proxy.start(destStreamName, publishMode, new Object[] {});
            // wait for the publish state
            do {
                try {
                    Thread.sleep(100L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (!proxy.isPublished());
            System.out.println("Publishing...");

            // create the consumer
            client = new RTMPClient();
            client.setStreamEventDispatcher(new StreamEventDispatcher());
            client.setStreamEventHandler(new INetStreamEventHandler() {
                @Override
                public void onStreamEvent(Notify notify) {
                    System.out.printf("onStreamEvent: %s\n", notify);
                    ObjectMap map = (ObjectMap) notify.getCall().getArguments()[0];
                    String code = (String) map.get("code");
                    System.out.printf("<:%s\n", code);
                    if (StatusCodes.NS_PLAY_STREAMNOTFOUND.equals(code)) {
                        System.out.println("Requested stream was not found");
                        client.disconnect();
                    } else if (StatusCodes.NS_PLAY_UNPUBLISHNOTIFY.equals(code) || StatusCodes.NS_PLAY_COMPLETE.equals(code)) {
                        System.out.println("Source has stopped publishing or play is complete");
                        client.disconnect();
                    }
                }
            });
            client.setConnectionClosedHandler(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Source connection has been closed, proxy will be stopped");
                    proxy.stop();
                }
            });
            client.setExceptionHandler(new ClientExceptionHandler() {
                @Override
                public void handleException(Throwable throwable) {
                    throwable.printStackTrace();
                    System.exit(1);
                }
            });
            // connect the consumer
            Map defParams = client.makeDefaultConnectionParams(sourceHost, sourcePort, sourceApp);
            // add pageurl and swfurl
            defParams.put("pageUrl", "");
            defParams.put("swfUrl", "app:/Red5-StreamRelay.swf");
            // indicate for the handshake to generate swf verification data
            client.setSwfVerification(true);
            // connect the client
            client.connect(sourceHost, sourcePort, defParams, new IPendingServiceCallback() {
                @Override
                public void resultReceived(IPendingServiceCall call) {
                    System.out.println("connectCallback");
                    ObjectMap map = (ObjectMap) call.getResult();
                    String code = (String) map.get("code");
                    if ("NetConnection.Connect.Rejected".equals(code)) {
                        System.out.printf("Rejected: %s\n", map.get("description"));
                        client.disconnect();
                        proxy.stop();
                    } else if ("NetConnection.Connect.Success".equals(code)) {
                        // 1. Wait for onBWDone
                        timer.schedule(new BandwidthStatusTask(), 2000L);
                    } else {
                        System.out.printf("Unhandled response code: %s\n", code);
                    }
                }
            });
            // keep sleeping main thread while the proxy runs
            do {
                try {
                    Thread.sleep(100L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (!proxy.isRunning());
            // kill the timer
            //timer.cancel();
            System.out.println("Stream relay exit");
        }

    }

    /**
     * Dispatches consumer events.
     */
    private static final class StreamEventDispatcher implements IEventDispatcher {

        @Override
        public void dispatchEvent(IEvent event) {
            System.out.println("ClientStream.dispachEvent()" + event.toString());
            try {
                proxy.pushMessage(null, RTMPMessage.build((IRTMPEvent) event));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * Handles result from subscribe call.
     */
    private static final class SubscribeStreamCallBack implements IPendingServiceCallback {

        @Override
        public void resultReceived(IPendingServiceCall call) {
            System.out.println("resultReceived: " + call);
        }

    }

    /**
     * Creates a "stream" via playback, this is the source stream.
     */
    private static final class CreateStreamCallback implements IPendingServiceCallback {

        @Override
        public void resultReceived(IPendingServiceCall call) {
            System.out.println("resultReceived: " + call);
            Double streamId = (Double) call.getResult();
            System.out.println("stream id: " + streamId);
            // send our buffer size request
            if (sourceStreamName.endsWith(".flv") || sourceStreamName.endsWith(".f4v") || sourceStreamName.endsWith(".mp4")) {
                client.play(streamId, sourceStreamName, 0, -1);
            } else {
                client.play(streamId, sourceStreamName, -1, 0);
            }
        }

    }

    /**
     * Continues to check for onBWDone
     */
    private static final class BandwidthStatusTask extends TimerTask {

        @Override
        public void run() {
            // check for onBWDone
            System.out.println("Bandwidth check done: " + client.isBandwidthCheckDone());
            // cancel this task
            this.cancel();
            // create a task to wait for subscribed
            timer.schedule(new PlayStatusTask(), 1000L);
            // 2. send FCSubscribe
            client.subscribe(new SubscribeStreamCallBack(), new Object[] { sourceStreamName });
        }

    }

    private static final class PlayStatusTask extends TimerTask {

        @Override
        public void run() {
            // checking subscribed
            System.out.println("Subscribed: " + client.isSubscribed());
            // cancel this task
            this.cancel();
            // 3. create stream
            client.createStream(new CreateStreamCallback());
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy