ultrasonic-app-subsonic-and.../src/com/thejoshwa/ultrasonic/androidapp/util/StreamProxy.java

250 lines
7.0 KiB
Java
Raw Normal View History

2013-04-06 21:47:24 +02:00
package com.thejoshwa.ultrasonic.androidapp.util;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
import org.apache.http.HttpRequest;
import org.apache.http.message.BasicHttpRequest;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
2013-04-06 21:47:24 +02:00
import com.thejoshwa.ultrasonic.androidapp.service.DownloadFile;
import com.thejoshwa.ultrasonic.androidapp.service.DownloadService;
import android.util.Log;
public class StreamProxy implements Runnable {
private static final String TAG = StreamProxy.class.getSimpleName();
private Thread thread;
private boolean isRunning;
private ServerSocket socket;
private int port;
private DownloadService downloadService;
public StreamProxy(DownloadService downloadService) {
// Create listening socket
try {
socket = new ServerSocket(0, 0, InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
socket.setSoTimeout(5000);
port = socket.getLocalPort();
this.downloadService = downloadService;
} catch (UnknownHostException e) { // impossible
} catch (IOException e) {
Log.e(TAG, "IOException initializing server", e);
}
}
public int getPort() {
return port;
}
public void start() {
thread = new Thread(this);
thread.start();
}
public void stop() {
isRunning = false;
thread.interrupt();
}
@Override
public void run() {
isRunning = true;
while (isRunning) {
try {
Socket client = socket.accept();
if (client == null) {
continue;
}
Log.i(TAG, "client connected");
StreamToMediaPlayerTask task = new StreamToMediaPlayerTask(client);
if (task.processRequest()) {
new Thread(task).start();
}
} catch (SocketTimeoutException e) {
// Do nothing
} catch (IOException e) {
Log.e(TAG, "Error connecting to client", e);
}
}
Log.i(TAG, "Proxy interrupted. Shutting down.");
}
private class StreamToMediaPlayerTask implements Runnable {
String localPath;
Socket client;
int cbSkip;
public StreamToMediaPlayerTask(Socket client) {
this.client = client;
}
private HttpRequest readRequest() {
HttpRequest request = null;
InputStream is;
String firstLine;
try {
is = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8192);
firstLine = reader.readLine();
} catch (IOException e) {
Log.e(TAG, "Error parsing request", e);
return request;
}
if (firstLine == null) {
Log.i(TAG, "Proxy client closed connection without a request.");
return request;
}
StringTokenizer st = new StringTokenizer(firstLine);
String method = st.nextToken();
String uri = st.nextToken();
String realUri = uri.substring(1);
Log.i(TAG, realUri);
request = new BasicHttpRequest(method, realUri);
return request;
}
public boolean processRequest() {
HttpRequest request = readRequest();
if (request == null) {
return false;
}
// Read HTTP headers
Log.i(TAG, "Processing request");
try {
localPath = URLDecoder.decode(request.getRequestLine().getUri(), Constants.UTF_8);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unsupported encoding", e);
return false;
}
Log.i(TAG, "Processing request for file " + localPath);
File file = new File(localPath);
if (!file.exists()) {
Log.e(TAG, "File " + localPath + " does not exist");
return false;
}
return true;
}
@Override
public void run() {
Log.i(TAG, "Streaming song in background");
DownloadFile downloadFile = downloadService.getCurrentPlaying();
MusicDirectory.Entry song = downloadFile.getSong();
// Create HTTP header
String headers = "HTTP/1.0 200 OK\r\n";
headers += "Content-Type: " + "application/octet-stream" + "\r\n";
Integer contentLength = downloadFile.getContentLength();
long fileSize;
if (contentLength == null) {
fileSize = downloadFile.getBitRate() * ((song.getDuration() != null) ? song.getDuration() : 0) * 1000 / 8;
} else {
fileSize = contentLength;
headers += "Content-Length: " + fileSize + "\r\n";
}
Log.i(TAG, "Streaming fileSize: " + fileSize);
headers += "Connection: close\r\n";
headers += "\r\n";
long cbToSend = fileSize - cbSkip;
OutputStream output = null;
byte[] buff = new byte[64 * 1024];
try {
output = new BufferedOutputStream(client.getOutputStream(), 32*1024);
output.write(headers.getBytes());
if(!downloadFile.isWorkDone()) {
// Loop as long as there's stuff to send
while (isRunning && !client.isClosed()) {
// See if there's more to send
File file = new File(localPath);
int cbSentThisBatch = 0;
if (file.exists()) {
FileInputStream input = new FileInputStream(file);
input.skip(cbSkip);
int cbToSendThisBatch = input.available();
while (cbToSendThisBatch > 0) {
int cbToRead = Math.min(cbToSendThisBatch, buff.length);
int cbRead = input.read(buff, 0, cbToRead);
if (cbRead == -1) {
break;
}
cbToSendThisBatch -= cbRead;
cbToSend -= cbRead;
output.write(buff, 0, cbRead);
output.flush();
cbSkip += cbRead;
cbSentThisBatch += cbRead;
}
input.close();
}
// Done regardless of whether or not it thinks it is
if(downloadFile.isWorkDone() && cbSkip >= file.length()) {
break;
}
// If we did nothing this batch, block for a second
if (cbSentThisBatch == 0) {
Log.d(TAG, "Blocking until more data appears (" + cbToSend + ")");
Thread.sleep(1000);
}
}
} else {
Log.w(TAG, "Requesting data for completely downloaded file");
}
}
catch (SocketException socketException) {
Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
}
catch (Exception e) {
Log.e(TAG, "Exception thrown from streaming task:");
Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
}
// Cleanup
try {
if (output != null) {
output.close();
}
client.close();
}
catch (IOException e) {
Log.e(TAG, "IOException while cleaning up streaming task:");
Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
}
}
}
}