/**
 * Copyright (c) 2006 - 2012 Smaxe Ltd (www.smaxe.com).
 * All rights reserved.
 */

import com.smaxe.io.ByteArray;
import com.smaxe.uv.client.rtsp.IMessageInterceptor;
import com.smaxe.uv.client.rtsp.IPresentation;
import com.smaxe.uv.client.rtsp.IVideo;
import com.smaxe.uv.client.rtsp.RtspClient;
import com.smaxe.uv.client.rtsp.RtspPlaySession;
import com.smaxe.uv.client.rtsp.RtspRequest;
import com.smaxe.uv.client.rtsp.RtspResponse;
import com.smaxe.uv.client.rtsp.video.AbstractVideo;
import com.smaxe.uv.client.rtsp.video.VideoFactory;
import com.smaxe.uv.media.core.MediaContainer;
import com.smaxe.uv.media.core.MediaTrackInfo;
import com.smaxe.uv.stream.MediaData;

import java.util.Arrays;

/**
 * <code>ExRtspPlayStream</code> - {@link RtspClient} usage example.
 * <p> Note:
 * <br> - The example shows how to 'PLAY' server stream.
 * <br> - The played stream is saved to the local file. 
 * 
 * @author Andrei Sochirca
 * @see <a href="http://www.smaxe.com/product.jsf?id=juv-rtsp-client" target="_blank">JUV RTSP/RTP Client</a>
 */
public final class ExRtspPlayStream extends Object
{
    /**
     * Entry point.
     * 
     * @param args
     * @throws Exception if an exception occurred
     */
    public static void main(final String[] args) throws Exception
    {
        // NOTE:
        // you can get Evaluation Key at:
        // http://www.smaxe.com/order.jsf#request_evaluation_key
        // or buy at:
        // http://www.smaxe.com/order.jsf
        
        // Android-specific:
        // - please add permission to the AndroidManifest.xml : 
        // <uses-permission android:name="android.permission.INTERNET" />
        // - please use separate thread to connect to the server (not UI thread) : 
        // RtspClient#connect() connects to the remote server in the invocation thread,
        // so it causes NetworkOnMainThreadException on 4.0 (http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html)
        com.smaxe.uv.client.rtsp.License.setKey("SET-YOUR-KEY");
        
        // url
        final String url = args.length == 0 ? "rtsp://username:password@184.72.239.149/vod/mp4:BigBuckBunny_175k.mov" : args[0];
        
        // 
        final boolean unicast = true;
        // set true to record incoming stream, false - to log incoming data to console
        final boolean record = false;
        
        final RtspClient client = new RtspClient();
        
        // configure RtspClient
        client.configuration().put(RtspClient.Configuration.DATAGRAM_JITTER_BUFFER_MARGIN, 16 /*packets*/);
        client.configuration().put(RtspClient.Configuration.DATAGRAM_SO_ENABLE_CONFIGURATION, true);
        client.configuration().put(RtspClient.Configuration.DATAGRAM_SO_RCVBUF, 128 * 1024);
        
        client.configuration().put(RtspClient.Configuration.RESPONSE_TIMEOUT, 10 /*seconds*/);
        client.configuration().put(RtspClient.Configuration.MESSAGE_INTERCEPTOR, new IMessageInterceptor()
        {
            public RtspRequest onRequest(final String source, final RtspRequest request)
            {
                // NOTE: please use the method below to add custom headers
                // request.putHeader("Custom-header", "value");
                
                System.out.println("onRequest: " + source + " " + request);
                
                return request;
            }
            
            public RtspResponse onResponse(final String source, final RtspResponse response)
            {
                System.out.println("onResponse: " + source + " " + response);
                
                return response;
            }
        });
        
        // set listener
        client.addListener(new RtspClient.ListenerAdapter()
        {
            @Override
            public void onConnect()
            {
                System.out.println("RtspClient.IListener#onConnect(): ");
            }
            
            @Override
            public void onStateChange(final int from, final int to)
            {
                System.out.println("RtspClient.IListener#onStateChange(): " +
                        RtspClient.getStateDescription(from) + " -> " + RtspClient.getStateDescription(to));
            }
            
            @Override
            public void onDisconnect(final String reason)
            {
                System.out.println("RtspClient.IListener#onDisconnect(): " + reason);
            }
        });
        
        // connect
        client.connect(url); // or client.connect(url, "username", "password");
        
        try
        {
            // get and print media stream presentation (SDP)
            final IPresentation presentation = client.describe(url);
            
            System.out.println("*** Presentation ***");
            System.out.println(presentation);
            
            
            // NOTE: if you need to get part of presentation media then use RtspClient.createPartOfPresentation*(*) methods
            // final IPresentation videoOnlyPartOfPresentation = RtspClient.createPartOfPresentationVideoOnly(presentation);
            // 
            // System.out.println("*** Video only part of presentation ***");
            // System.out.println(videoOnlyPartOfPresentation);
            
            
            // create 'PLAY' session
            final RtspPlaySession session = client.createPlaySession(presentation);
            
            // unicast mode
            if (unicast)
            {
                // sets up RTSP session for the INTERLEAVED (RTP/AVP/TCP) mode
                if (!RtspClient.isOK(session.setup()))
                {
                    // if RTP/AVP/TCP mode is not supported then use RTP/AVP/UDP
                    session.setup(23120 /*rtpPort*/);
                }
            }
            else
            // multicast mode
            {
                // session.setProfile("MP2T/H2221/UDP");
                
                final boolean isMulticastAddressDefined = true;
                
                if (isMulticastAddressDefined)
                {
                    session.setMulticastAddress("231.2.23.12");
                    session.setup(23120);
                }
                else
                {
                    session.setMulticastAddress("0.0.0.0");
                    session.setup();
                }
            }
            
            // set RTCP Handler callback (optional)
            session.setCallback(new RtspClient.IRtcpHandler.CallbackAdapter()
            {
                @Override
                public void onApp(final MediaTrackInfo track, final int subtype, final String name, final ByteArray data)
                {
                    // NOTE: please use the method below to send RTCP 'APP' event to the server
                    // session.sendApp(track, subtype, name, data);
                    
                    System.out.println("RtspClient.IRtcpHandler.ICallback#onApp: " + track + " " + subtype + " " + name + " " + data.length);
                }
            });
            
            // set listener (optional)
            session.setListener(new RtspPlaySession.ListenerAdapter()
            {
                @Override
                public void onMediaDataTimeout(final MediaTrackInfo track, final int timeout)
                {
                    System.out.println("PlaySession.IListener#onMediaDataTimeout: " + track + " " + timeout);
                }
                
                @Override
                public void onPacketLoss(final MediaTrackInfo track, final long lost, final long total, final int margin)
                {
                    // NOTE: please use the method below to adjust jitter buffer margin
                    // session.setJitterBufferMargin(track, 12);
                    
                    System.out.println("PlaySession.IListener#onPacketLoss: " + track + " " + lost + " " + total);
                }
            });
            
            final MediaContainer[] containers = VideoFactory.findMediaContainer(presentation);
            if (record && containers.length == 0) throw new Exception("Presentation can't be written to the file, no appropriate container found");
            
            final MediaContainer container = record ? containers[0] : null;
            if (container != null) System.out.println("Selected media container is " + container);
            
            // NOTE: file extension is added by the "VideoFactory#create(*)" implementation
            final IVideo video = record ? VideoFactory.create("d:/rtsp", container) :
                new AbstractVideo()
                {
                    @Override
                    public void onPlay(final MediaTrackInfo[] tracks)
                    {
                        System.out.println("IVideo#onPlay: "  + Arrays.toString(tracks));
                    }
                    
                    @Override
                    public void onMediaData(final MediaTrackInfo track, final MediaData data)
                    {
                        // NOTE: please use the method below to get media data content
                        // ByteArray payload = MediaDataFactory.getMediaDataPayload(data);                    
                        
                        System.out.println("IVideo#onMediaData: "  + data + " " + track);
                    }
                    
                    @Override
                    public void onTeardown()
                    {
                        System.out.println("IVideo#onTeardown");
                    }
                };
            
            session.play(video); // equivalent to session.play(video, RtspClient.createRangeNpt(0 /*start*/));
            
            Thread.sleep(10 * 1000);
            
            if (session.pause())
            {
                Thread.sleep(10 * 1000);
                
                session.resume(); // equivalent to session.play(video, (String) null /*range*/);
            }
            
            Thread.sleep(10 * 1000);
            
            session.teardown();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            client.close("ExRtspPlayStream is completed");
        }
    }
    
    /**
     * Constructor.
     */
    private ExRtspPlayStream()
    {
    }
}