package com.pugh.sockso.music.stream;
import com.pugh.sockso.Utils;
import com.pugh.sockso.music.Track;
import com.pugh.sockso.web.Response;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import org.apache.log4j.Logger;
/**
* For client requested streaming from arbitrary starting point
*
* @author Nathan Perrier
*/
public class RangeStream extends AbstractMusicStream {
private static final Logger log = Logger.getLogger(RangeStream.class);
private Range range;
public RangeStream( final Track track, final Range range ) {
super(track);
this.range = range;
}
@Override
public void setHeaders( final Response response ) {
super.setHeaders(response);
final long trackLength = new File(this.track.getPath()).length();
// set headers required to satisfy Range requests:
// Content-Length: 2980
final long contentLength = range.getLength();
// Content-Range: bytes 1000-3979/3980
final String contentRange = "bytes " + range.getStart() + "-" + range.getEnd() + "/" + trackLength;
response.setStatus(206); // Partial Content
response.addHeader("Content-Range", contentRange);
response.addHeader("Content-Length", Long.toString(contentLength));
response.addHeader("Accept-Ranges", "bytes");
}
/**
* Sends the music stream to the client, optionally handling range
* requests
*
* @throws java.io.IOException
*
*/
public void sendAudioStream( final DataOutputStream client ) throws IOException {
DataInputStream audio = getAudioStream();
long contentLength = this.range.getLength();
byte[] buffer = new byte[STREAM_BUFFER_SIZE];
int readBlock = STREAM_BUFFER_SIZE;
long totalBytes = 0;
try {
audio.skip(this.range.start);
log.debug("Skipped " + this.range.start + " bytes");
int nextRead = -1;
for ( int bytesRead = 0; bytesRead >= 0 && totalBytes < contentLength; bytesRead = audio.read(buffer, 0, readBlock) ) {
totalBytes += bytesRead;
if ( totalBytes + readBlock > contentLength ) {
readBlock = (int) (contentLength - totalBytes);
}
client.write(buffer, 0, bytesRead);
if ( totalBytes > nextRead ) {
nextRead += STREAM_BUFFER_SIZE * 12; // print ~every 100 KB
log.debug(String.format("Sent %2d%%", bytesRead));
}
}
} finally {
Utils.close(audio);
Utils.close(client);
}
}
/**
* Simple pair of values with the constraint that start < end
*/
public static class Range {
private long start;
private long end;
public Range( long start, long end ) {
assert start >= 0;
assert end > 0;
assert start < end;
this.start = start;
this.end = end;
}
public long getStart() {
return this.start;
}
public long getEnd() {
return this.end;
}
// start and end represent byte positions, so we need to +1
public long getLength() {
return end - start + 1;
}
}
}