View Javadoc

1   package org.musicontroller.streaming;
2   
3   import java.io.FilterOutputStream;
4   import java.io.IOException;
5   import java.io.OutputStream;
6   
7   import org.apache.log4j.Logger;
8   
9   /**
10   * A ShoutcastOutputStream adds metadata to an OutputStream in the Shoutcast-format.
11   * @author Varienaja
12   */
13  public class ShoutcastOutputStream extends FilterOutputStream {
14  	private static final Logger log = Logger.getLogger(ShoutcastOutputStream.class);
15  
16  	private final static int DEFAULT_METADATA_INTERVAL = 16384;
17  	private final static int METADATA_REPEAT_INTERVAL = 1024*1024/DEFAULT_METADATA_INTERVAL;
18  	private int metadata_interval;
19  	private int intervalcounter;
20  	private int count;
21  	private byte[] buffer;
22  	private String metadata;
23  	private boolean metadatachanged;
24  	private boolean dooutputmetadata;
25  	
26  	//TODO Logging-information about bitrate would be nice :-) 
27  	//TODO Especially difference between song bitrate and stream-bitrate
28  	
29  	public ShoutcastOutputStream(OutputStream out) {
30  		this(out,DEFAULT_METADATA_INTERVAL,true);
31  	}
32  	
33  	public ShoutcastOutputStream(OutputStream out, boolean outputmetadata) {
34  		this(out,DEFAULT_METADATA_INTERVAL,outputmetadata);
35  	}
36  	
37  	/**
38  	 * Creates a new ShoutcastOutputStream-object
39  	 * @param out The outputstream to use
40  	 * @param interval The interval, specifying the amount of bytes that is copied
41  	 * to the outputstream before metadata is inserted into the stream.
42  	 * @param outputmetadata Whether or not to output metadata into the stream
43  	 */
44  	public ShoutcastOutputStream(OutputStream out, int interval, boolean outputmetadata) {
45  		super(out);
46  		
47  		metadata_interval = interval;
48  		dooutputmetadata = outputmetadata;
49  		buffer = new byte[metadata_interval];
50  		count = 0;
51  		log.debug("New ShoutcastOutputStream created");
52  	}
53  	
54  	private void flushBuffer() throws IOException {
55  		if (count==metadata_interval) {
56  			out.write(buffer,0,metadata_interval);
57  			count = 0;
58  			intervalcounter++;
59  			if (intervalcounter>METADATA_REPEAT_INTERVAL) {
60  				intervalcounter = 0;
61  				metadatachanged = true;
62  			}
63  			if (dooutputmetadata) writeMetaData();
64  		} else {
65  			log.debug("Incomplete flush, ignoring.");
66  		}
67  	}
68  	
69  	public synchronized void write(int b) throws IOException {
70  		if (count >= buffer.length) {
71  			flushBuffer();
72  		}
73  		buffer[count++] = (byte) b;
74  	}
75  	
76  	public synchronized void write(byte[] b, int off, int len) throws IOException {
77  		int towrite = len;
78  		while (towrite>metadata_interval-count) {
79  			System.arraycopy(b, off+len-towrite, buffer, count, metadata_interval-count);
80  			towrite -= metadata_interval-count;
81  			count+=metadata_interval-count;
82  			flushBuffer();
83  		}
84  		if (towrite>0) {
85  			System.arraycopy(b, off+len-towrite, buffer, count, towrite);
86  			count+=towrite;
87  		}
88  	}
89  	
90  	public synchronized void flush() throws IOException {
91  		flushBuffer();
92  		out.flush();
93  	}
94  	
95  	/**
96  	 * Sets the current metadata
97  	 * @param streamtitle The title of the stream
98  	 * @param streamurl The url of the stream
99  	 */
100 	public void setMetadata(String streamtitle, String streamurl) {
101 		metadata = 	"StreamTitle='" + streamtitle + "';" +
102 					"StreamUrl='" + streamurl + "';"; 
103 		metadatachanged = true;
104 	}
105 	
106 	/**
107 	 * Writes the metadata to the outputstream
108 	 * @throws IOException
109 	 */
110 	private void writeMetaData() throws IOException {
111 		byte[] size = new byte[1];
112 		if (metadatachanged) {
113 			byte[] bytes = metadata.getBytes("ISO-8859-1");
114 			// Since the first byte tells the length of the metadata divided by 16
115 			// The metadata itself must be a multitude of 16 bytes in length.
116 			int bytesToAdd = (16 - (bytes.length % 16)) % 16;
117 			byte[] toAdd = new byte[bytesToAdd];
118 			
119 			size[0] = Integer.valueOf((bytes.length+toAdd.length) / 16).byteValue();
120 
121 			out.write(size);
122 			if (size[0]!=0) {
123 				out.write(bytes);
124 				out.write(toAdd);
125 			}
126 			metadatachanged = false;
127 			log.debug("Streaming metadata: "+metadata);
128 		} else { //Write empty message if metadata has not changed to spare some bandwith
129 			size[0]=0;
130 			out.write(size);
131 		}
132 	}
133 
134 }