/* * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This source code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This source code 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 source code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ package local.media; import local.net.RtpPacket; import local.net.RtpSocket; import java.io.InputStream; import java.net.InetAddress; import java.net.DatagramSocket; import com.sesca.misc.Logger; /** RtpStreamSender is a generic stream sender. * It takes an InputStream and sends it through RTP. */ public class RtpStreamSender extends Thread { /** Whether working in debug mode. */ //private static final boolean DEBUG=true; public static boolean DEBUG=false; // Testing int skip=0; int skipIndex=0; /** The InputStream */ InputStream input_stream=null; /** The RtpSocket */ RtpSocket rtp_socket=null; /** Payload type */ int p_type; /** Number of frame per second */ long frame_rate; /** Number of bytes per frame */ int frame_size; /** Whether the socket has been created here */ boolean socket_is_local=false; /** Whether it works synchronously with a local clock, or it it acts as slave of the InputStream */ //boolean do_sync=true; boolean do_sync=false; /** Synchronization correction value, in milliseconds. * It accellarates the sending rate respect to the nominal value, * in order to compensate program latencies. */ int sync_adj=0; /** Whether it is running */ boolean running=false; /** Constructs a RtpStreamSender. * @param input_stream the stream source * @param do_sync whether time synchronization must be performed by the RtpStreamSender, * or it is performed by the InputStream (e.g. the system audio input) * @param payload_type the payload type * @param frame_rate the frame rate, i.e. the number of frames that should be sent per second; * it is used to calculate the nominal packet time and,in case of do_sync==true, the next departure time * @param frame_size the size of the payload * @param dest_addr the destination address * @param dest_port the destination port */ public RtpStreamSender(InputStream input_stream, boolean do_sync, int payload_type, long frame_rate, int frame_size, String dest_addr, int dest_port) { init(input_stream,do_sync,payload_type,frame_rate,frame_size,null,dest_addr,dest_port); } /** Constructs a RtpStreamSender. * @param input_stream the stream source * @param do_sync whether time synchronization must be performed by the RtpStreamSender, * or it is performed by the InputStream (e.g. the system audio input) * @param payload_type the payload type * @param frame_rate the frame rate, i.e. the number of frames that should be sent per second; * it is used to calculate the nominal packet time and,in case of do_sync==true, the next departure time * @param frame_size the size of the payload * @param src_port the source port * @param dest_addr the destination address * @param dest_port the destination port */ //public RtpStreamSender(InputStream input_stream, boolean do_sync, int payload_type, long frame_rate, int frame_size, int src_port, String dest_addr, int dest_port) //{ init(input_stream,do_sync,payload_type,frame_rate,frame_size,null,src_port,dest_addr,dest_port); //} /** Constructs a RtpStreamSender. * @param input_stream the stream to be sent * @param do_sync whether time synchronization must be performed by the RtpStreamSender, * or it is performed by the InputStream (e.g. the system audio input) * @param payload_type the payload type * @param frame_rate the frame rate, i.e. the number of frames that should be sent per second; * it is used to calculate the nominal packet time and,in case of do_sync==true, the next departure time * @param frame_size the size of the payload * @param src_socket the socket used to send the RTP packet * @param dest_addr the destination address * @param dest_port the thestination port */ public RtpStreamSender(InputStream input_stream, boolean do_sync, int payload_type, long frame_rate, int frame_size, DatagramSocket src_socket, String dest_addr, int dest_port) { init(input_stream,do_sync,payload_type,frame_rate,frame_size,src_socket,dest_addr,dest_port); } /** Inits the RtpStreamSender */ private void init(InputStream input_stream, boolean do_sync, int payload_type, long frame_rate, int frame_size, DatagramSocket src_socket, /*int src_port,*/ String dest_addr, int dest_port) { // testing skip = 0; skipIndex=0; this.input_stream=input_stream; this.p_type=payload_type; this.frame_rate=frame_rate; this.frame_size=frame_size; this.do_sync=do_sync; try { if (src_socket==null) { //if (src_port>0) src_socket=new DatagramSocket(src_port); else src_socket=new DatagramSocket(); socket_is_local=true; } rtp_socket=new RtpSocket(src_socket,InetAddress.getByName(dest_addr),dest_port); } catch (Exception e) { e.printStackTrace(); } } /** Sets the synchronization adjustment time (in milliseconds). */ public void setSyncAdj(int millisecs) { sync_adj=millisecs; } /** Whether is running */ public boolean isRunning() { return running; } /** Stops running */ public void halt() { running=false; } /** Runs it in a new Thread. */ public void run() { if (rtp_socket==null || input_stream==null) return; //else byte[] buffer=new byte[frame_size+12]; RtpPacket rtp_packet=new RtpPacket(buffer,0); rtp_packet.setPayloadType(p_type); int seqn=0; long time=0; //long start_time=System.currentTimeMillis(); long byte_rate=frame_rate*frame_size; running=true; if (DEBUG) println("Reading blocks of "+(buffer.length-12)+" bytes"); try { while (running) { //if (DEBUG) System.out.print("o"); int num=input_stream.read(buffer,12,buffer.length-12); //if (DEBUG) System.out.print("*"); if (num>0) { rtp_packet.setSequenceNumber(seqn++); //Logger.debug("sequence number="+seqn); rtp_packet.setTimestamp(time); //Logger.debug("time stamp="+time); rtp_packet.setPayloadLength(num); //testing //if (skip==skipIndex){rtp_socket.send(rtp_packet);skipIndex=0;} //else skipIndex++; // update rtp timestamp (in milliseconds) //long frame_time=(num*1000)/byte_rate; //time+=frame_time; time+=num; rtp_socket.send(rtp_packet); // wait fo next departure /* if (do_sync) { // wait before next departure.. //long frame_time=start_time+time-System.currentTimeMillis(); // accellerate in order to compensate possible program latency.. ;) frame_time-=sync_adj; try { Thread.sleep(frame_time); } catch (Exception e) {} } */ } else if (num<0) { running=false; if (DEBUG) println("Error reading from InputStream"); } } } catch (Exception e) { running=false; e.printStackTrace(); } //if (DEBUG) println("rtp time: "+time); //if (DEBUG) println("real time: "+(System.currentTimeMillis()-start_time)); // close RtpSocket and local DatagramSocket DatagramSocket socket=rtp_socket.getDatagramSocket(); rtp_socket.close(); if (socket_is_local && socket!=null) socket.close(); // free all input_stream=null; rtp_socket=null; if (DEBUG) println("rtp sender terminated"); } /** Debug output */ private static void println(String str) { } }