diff --git a/.classpath b/.classpath
index 5b1771a..2aceb57 100644
--- a/.classpath
+++ b/.classpath
@@ -5,5 +5,6 @@
+
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index c4c5eb8..59df3f4 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -52,23 +52,23 @@
android:key="http_server_enabled"
android:summary="@string/settings23"
android:title="@string/settings22" />
-
+ android:title="@string/settings24" />
-
+ android:title="@string/settings26" />
diff --git a/src/net/majorkernelpanic/http/TinyHttpServer.java b/src/net/majorkernelpanic/http/TinyHttpServer.java
index e98dab7..d0e3c23 100644
--- a/src/net/majorkernelpanic/http/TinyHttpServer.java
+++ b/src/net/majorkernelpanic/http/TinyHttpServer.java
@@ -665,9 +665,13 @@ public void run() {
} catch (HttpException e) {
Log.e(TAG,"Unrecoverable HTTP protocol violation: " + e.getMessage());
} finally {
- /*try {
+ try {
+ OutputStream sockOutOStream = socket.getOutputStream();
+ sockOutOStream.write(new byte[0]);
+ sockOutOStream.flush();
socket.close();
- } catch (IOException e) {}*/
+ } catch (IOException e) {
+ }
try {
this.conn.shutdown();
} catch (Exception ignore) {}
diff --git a/src/net/majorkernelpanic/spydroid/SpydroidApplication.java b/src/net/majorkernelpanic/spydroid/SpydroidApplication.java
index 8d82cef..bb07e34 100644
--- a/src/net/majorkernelpanic/spydroid/SpydroidApplication.java
+++ b/src/net/majorkernelpanic/spydroid/SpydroidApplication.java
@@ -34,7 +34,6 @@
import net.majorkernelpanic.streaming.SessionBuilder;
import net.majorkernelpanic.streaming.video.VideoQuality;
-import org.acra.ACRA;
import org.acra.annotation.ReportsCrashes;
import android.content.BroadcastReceiver;
@@ -88,7 +87,7 @@ public void onCreate() {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
notificationEnabled = settings.getBoolean("notification_enabled", true);
-
+
// On android 3.* AAC ADTS is not supported so we set the default encoder to AMR-NB, on android 4.* AAC is the default encoder
audioEncoder = (Integer.parseInt(android.os.Build.VERSION.SDK)<14) ? SessionBuilder.AUDIO_AMRNB : SessionBuilder.AUDIO_AAC;
audioEncoder = Integer.parseInt(settings.getString("audio_encoder", String.valueOf(audioEncoder)));
@@ -137,14 +136,14 @@ else if (key.equals("video_bitrate")) {
}
else if (key.equals("audio_encoder") || key.equals("stream_audio")) {
- audioEncoder = Integer.parseInt(sharedPreferences.getString("audio_encoder", "0"));
+ audioEncoder = Integer.parseInt(sharedPreferences.getString("audio_encoder", String.valueOf(audioEncoder)));
SessionBuilder.getInstance().setAudioEncoder( audioEncoder );
if (!sharedPreferences.getBoolean("stream_audio", false))
SessionBuilder.getInstance().setAudioEncoder(0);
}
else if (key.equals("stream_video") || key.equals("video_encoder")) {
- videoEncoder = Integer.parseInt(sharedPreferences.getString("video_encoder", "0"));
+ videoEncoder = Integer.parseInt(sharedPreferences.getString("video_encoder", String.valueOf(videoEncoder)));
SessionBuilder.getInstance().setVideoEncoder( videoEncoder );
if (!sharedPreferences.getBoolean("stream_video", true))
SessionBuilder.getInstance().setVideoEncoder(0);
diff --git a/src/net/majorkernelpanic/spydroid/ui/OptionsActivity.java b/src/net/majorkernelpanic/spydroid/ui/OptionsActivity.java
index 42c4a0a..8c053b8 100644
--- a/src/net/majorkernelpanic/spydroid/ui/OptionsActivity.java
+++ b/src/net/majorkernelpanic/spydroid/ui/OptionsActivity.java
@@ -21,6 +21,7 @@
package net.majorkernelpanic.spydroid.ui;
import static net.majorkernelpanic.http.TinyHttpServer.KEY_HTTPS_ENABLED;
+import static net.majorkernelpanic.http.TinyHttpServer.KEY_HTTPS_PORT;
import static net.majorkernelpanic.http.TinyHttpServer.KEY_HTTP_ENABLED;
import static net.majorkernelpanic.http.TinyHttpServer.KEY_HTTP_PORT;
@@ -37,6 +38,7 @@
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
+import android.util.Log;
@SuppressWarnings("deprecation")
public class OptionsActivity extends PreferenceActivity {
@@ -59,16 +61,15 @@ protected void onCreate(Bundle savedInstanceState){
final ListPreference videoBitrate = (ListPreference) findPreference("video_bitrate");
final ListPreference videoFramerate = (ListPreference) findPreference("video_framerate");
final CheckBoxPreference httpEnabled = (CheckBoxPreference) findPreference("http_server_enabled");
- //final CheckBoxPreference httpsEnabled = (CheckBoxPreference) findPreference("use_https");
+ final CheckBoxPreference httpsEnabled = (CheckBoxPreference) findPreference("use_https");
final Preference httpPort = findPreference(KEY_HTTP_PORT);
- //final Preference httpsPort = findPreference(KEY_HTTPS_PORT);
+ final Preference httpsPort = findPreference(KEY_HTTPS_PORT);
boolean videoState = settings.getBoolean("stream_video", true);
videoEncoder.setEnabled(videoState);
videoResolution.setEnabled(videoState);
videoBitrate.setEnabled(videoState);
videoFramerate.setEnabled(videoState);
- audioEncoder.setEnabled(settings.getBoolean("stream_audio", true));
videoEncoder.setValue(String.valueOf(mApplication.videoEncoder));
audioEncoder.setValue(String.valueOf(mApplication.audioEncoder));
@@ -80,6 +81,8 @@ protected void onCreate(Bundle savedInstanceState){
videoFramerate.setSummary(getString(R.string.settings1)+" "+videoFramerate.getValue()+"fps");
videoBitrate.setSummary(getString(R.string.settings2)+" "+videoBitrate.getValue()+"kbps");
+ audioEncoder.setEnabled(settings.getBoolean("stream_audio", false));
+
httpEnabled.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean state = (Boolean)newValue;
@@ -93,20 +96,20 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
editor.putBoolean(KEY_HTTPS_ENABLED, false);
} else {
// HTTP/HTTPS, it's one or the other
- /**if (httpsEnabled.isChecked()) {
+ if (httpsEnabled.isChecked()) {
editor.putBoolean(KEY_HTTPS_ENABLED, true);
editor.putBoolean(KEY_HTTP_ENABLED, false);
- } else {*/
+ } else {
editor.putBoolean(KEY_HTTPS_ENABLED, false);
editor.putBoolean(KEY_HTTP_ENABLED, true);
- //}
+ }
}
editor.commit();
return true;
}
});
- /*httpsEnabled.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ httpsEnabled.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean state = (Boolean)newValue;
Editor editor = settings.edit();
@@ -127,7 +130,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
editor.commit();
return true;
}
- });*/
+ });
videoResolution.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
diff --git a/src/net/majorkernelpanic/spydroid/ui/SpydroidActivity.java b/src/net/majorkernelpanic/spydroid/ui/SpydroidActivity.java
index ff9f4f1..59c957c 100644
--- a/src/net/majorkernelpanic/spydroid/ui/SpydroidActivity.java
+++ b/src/net/majorkernelpanic/spydroid/ui/SpydroidActivity.java
@@ -118,7 +118,7 @@ public void onCreate(Bundle savedInstanceState) {
((LinearLayout)findViewById(R.id.adcontainer)).removeAllViews();
}
- // Prevents the phone to go to sleep mode
+ // Prevents the phone from going to sleep mode
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "net.majorkernelpanic.spydroid.wakelock");
@@ -233,20 +233,6 @@ private void quitSpydroid() {
// Returns to home menu
finish();
}
-
- private SurfaceHolder.Callback mHolderCallback = new SurfaceHolder.Callback() {
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
-
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {}
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}
-
- };
private ServiceConnection mRtspServiceConnection = new ServiceConnection() {
diff --git a/src/net/majorkernelpanic/streaming/SessionBuilder.java b/src/net/majorkernelpanic/streaming/SessionBuilder.java
index 2f88995..8df5c32 100644
--- a/src/net/majorkernelpanic/streaming/SessionBuilder.java
+++ b/src/net/majorkernelpanic/streaming/SessionBuilder.java
@@ -43,22 +43,22 @@ public class SessionBuilder {
public final static String TAG = "SessionBuilder";
/** Can be used with {@link #setVideoEncoder}. */
- public final static int VIDEO_NONE = 0x00;
+ public final static int VIDEO_NONE = 0;
/** Can be used with {@link #setVideoEncoder}. */
- public final static int VIDEO_H264 = 0x01;
+ public final static int VIDEO_H264 = 1;
/** Can be used with {@link #setVideoEncoder}. */
- public final static int VIDEO_H263 = 0x02;
+ public final static int VIDEO_H263 = 2;
/** Can be used with {@link #setAudioEncoder}. */
- public final static int AUDIO_NONE = 0x00;
+ public final static int AUDIO_NONE = 0;
/** Can be used with {@link #setAudioEncoder}. */
- public final static int AUDIO_AMRNB = 0x03;
+ public final static int AUDIO_AMRNB = 3;
/** Can be used with {@link #setAudioEncoder}. */
- public final static int AUDIO_AAC = 0x05;
+ public final static int AUDIO_AAC = 5;
// Default configuration
private VideoQuality mVideoQuality = VideoQuality.defaultVideoQualiy.clone();
diff --git a/src/net/majorkernelpanic/streaming/audio/AACStream.java b/src/net/majorkernelpanic/streaming/audio/AACStream.java
index 2c78331..bb2340a 100644
--- a/src/net/majorkernelpanic/streaming/audio/AACStream.java
+++ b/src/net/majorkernelpanic/streaming/audio/AACStream.java
@@ -27,6 +27,7 @@
import net.majorkernelpanic.streaming.exceptions.AACNotSupportedException;
import net.majorkernelpanic.streaming.rtp.AACADTSPacketizer;
+import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.MediaRecorder;
@@ -76,6 +77,7 @@ public class AACStream extends AudioStream {
private int mProfile, mSamplingRateIndex, mChannel, mConfig;
private SharedPreferences mSettings = null;
+ @SuppressLint("InlinedApi")
public AACStream() throws IOException {
super();
@@ -83,20 +85,32 @@ public AACStream() throws IOException {
setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+ if (!AACStreamingSupported()) {
+ Log.e(TAG,"AAC ADTS not supported on this phone");
+ throw new AACNotSupportedException();
+ }
+
try {
Field name = MediaRecorder.OutputFormat.class.getField("AAC_ADTS");
- Log.d(TAG,"AAC ADTS seems to be supported: AAC_ADTS="+name.getInt(null));
setOutputFormat(name.getInt(null));
- } catch (Exception e) {
- Log.e(TAG,"AAC ADTS not supported on this phone");
- throw new AACNotSupportedException();
}
+ catch (Exception ignore) {}
setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
setAudioSamplingRate(16000);
}
+ private static boolean AACStreamingSupported() {
+ if (Integer.parseInt(android.os.Build.VERSION.SDK)<14) return false;
+ try {
+ MediaRecorder.OutputFormat.class.getField("AAC_ADTS");
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
/**
* Some data (the actual sampling rate) needs to be stored once {@link #generateSessionDescription()} is called.
* @param prefs The SharedPreferences that will be used to store the sampling rate
diff --git a/src/net/majorkernelpanic/streaming/rtcp/SenderReport.java b/src/net/majorkernelpanic/streaming/rtcp/SenderReport.java
index 3b0c906..b453d69 100644
--- a/src/net/majorkernelpanic/streaming/rtcp/SenderReport.java
+++ b/src/net/majorkernelpanic/streaming/rtcp/SenderReport.java
@@ -38,7 +38,6 @@ public class SenderReport {
private byte[] buffer = new byte[MTU];
private int ssrc, port = -1;
private int octetCount = 0, packetCount = 0;
- private long ntp = 0;
public SenderReport() throws IOException {
@@ -79,6 +78,17 @@ public void send() throws IOException {
usock.send(upack);
}
+ /** Sends the RTCP packet over the network. */
+ public void send(long ntpts, long rtpts) throws IOException {
+ long hb = ntpts/1000000000;
+ long lb = ( ( ntpts - hb*1000000000 ) * 4294967296L )/1000000000;
+ setLong(hb, 8, 12);
+ setLong(lb, 12, 16);
+ setLong(rtpts, 16, 20);
+ upack.setLength(28);
+ usock.send(upack);
+ }
+
/**
* Updates the number of packets sent, and the total amount of data sent.
* @param length The length of the packet
@@ -97,18 +107,8 @@ public void setRtpTimestamp(long ts) {
/** Sets the NTP timestamp of the sender report. */
public void setNtpTimestamp(long ts) {
- ntp = ts;
- long hb = ntp/1000000000;
- long lb = ( ( ntp - hb*1000000000 ) * 4294967296L )/1000000000;
- setLong(hb, 8, 12);
- setLong(lb, 12, 16);
- }
-
- /** Updates the NTP timestamp of the sender report. */
- public void updateNtpTimestamp(long delta) {
- ntp += delta;
- long hb = ntp/1000000000;
- long lb = ( ( ntp - hb*1000000000 ) * 4294967296L )/1000000000;
+ long hb = ts/1000000000;
+ long lb = ( ( ts - hb*1000000000 ) * 4294967296L )/1000000000;
setLong(hb, 8, 12);
setLong(lb, 12, 16);
}
diff --git a/src/net/majorkernelpanic/streaming/rtp/AACADTSPacketizer.java b/src/net/majorkernelpanic/streaming/rtp/AACADTSPacketizer.java
index 8a6f145..79a3170 100644
--- a/src/net/majorkernelpanic/streaming/rtp/AACADTSPacketizer.java
+++ b/src/net/majorkernelpanic/streaming/rtp/AACADTSPacketizer.java
@@ -43,7 +43,6 @@ public class AACADTSPacketizer extends AbstractPacketizer implements Runnable {
private Thread t;
private int samplingRate = 8000;
- private Statistics stats = new Statistics();
public AACADTSPacketizer() throws IOException {
super();
@@ -79,10 +78,8 @@ public void run() {
// Adts header fields that we need to parse
boolean protection;
int frameLength, sum, length, nbau, nbpk;
- long oldtime = System.nanoTime(), now = oldtime, delta = 5000, measured, lastmeasured = 5000, expected, interval = 0;
+ long oldtime = System.nanoTime(), now = oldtime, measured = 0, lastmeasured = 5000, expected = 0;
- stats.init(1024*1000000000/samplingRate);
-
try {
while (!Thread.interrupted()) {
@@ -118,11 +115,11 @@ public void run() {
socket.updateTimestamp(ts);
// We send one RTCP Sender Report every 5 secs
- if (delta>5000) {
- delta = 0;
- report.setNtpTimestamp(now);
- report.setRtpTimestamp(ts);
- report.send();
+ if (intervalBetweenReports>0) {
+ if (delta>=intervalBetweenReports) {
+ delta = 0;
+ report.send(now,ts);
+ }
}
sum = 0;
@@ -161,9 +158,7 @@ public void run() {
// We wait a little to avoid sending to many packets too quickly
now = System.nanoTime();
- interval = now-oldtime;
- measured = interval/1000000;
- stats.push(interval);
+ measured = (now-oldtime)/1000000;
delta += measured;
oldtime = now;
expected = nbau*1024*1000 / (nbpk*samplingRate);
diff --git a/src/net/majorkernelpanic/streaming/rtp/AMRNBPacketizer.java b/src/net/majorkernelpanic/streaming/rtp/AMRNBPacketizer.java
index 7e68abc..5a71923 100644
--- a/src/net/majorkernelpanic/streaming/rtp/AMRNBPacketizer.java
+++ b/src/net/majorkernelpanic/streaming/rtp/AMRNBPacketizer.java
@@ -66,7 +66,7 @@ public void stop() {
public void run() {
- int frameLength, frameType, delta = 10000;
+ int frameLength, frameType;
long now = System.nanoTime(), oldtime = now, measured;
long expected = 20, lastmeasured = 10000, intervalNs = 0;
@@ -115,11 +115,11 @@ public void run() {
Thread.sleep( 2*expected/3-measured );
}
- if (delta>5000) {
- delta = 0;
- report.setNtpTimestamp(now);
- report.setRtpTimestamp(ts);
- report.send();
+ if (intervalBetweenReports>0) {
+ if (delta>=intervalBetweenReports) {
+ delta = 0;
+ report.send(now,ts);
+ }
}
send(rtphl+1+AMR_FRAME_HEADER_LENGTH+frameLength);
diff --git a/src/net/majorkernelpanic/streaming/rtp/AbstractPacketizer.java b/src/net/majorkernelpanic/streaming/rtp/AbstractPacketizer.java
index 1264213..4e1962c 100644
--- a/src/net/majorkernelpanic/streaming/rtp/AbstractPacketizer.java
+++ b/src/net/majorkernelpanic/streaming/rtp/AbstractPacketizer.java
@@ -40,7 +40,8 @@ abstract public class AbstractPacketizer {
protected SenderReport report = null;
protected InputStream is = null;
protected byte[] buffer;
- protected long ts = 0;
+
+ protected long ts = 0, intervalBetweenReports = 5000, delta = 0;
public AbstractPacketizer() throws IOException {
int ssrc = new Random().nextInt();
@@ -89,6 +90,16 @@ public void setDestination(InetAddress dest, int rtpPort, int rtcpPort) {
report.setDestination(dest, rtcpPort);
}
+ /**
+ * Sets the temporal interval between two RTCP Sender Reports.
+ * Default interval is set to 5 secondes.
+ * Set 0 to disable RTCP.
+ * @param interval The interval in milliseconds
+ */
+ public void setSenderReportsInterval(long interval) {
+ intervalBetweenReports = interval;
+ }
+
public abstract void start() throws IOException;
public abstract void stop();
diff --git a/src/net/majorkernelpanic/streaming/rtp/H263Packetizer.java b/src/net/majorkernelpanic/streaming/rtp/H263Packetizer.java
index acadbbc..5abbb2c 100644
--- a/src/net/majorkernelpanic/streaming/rtp/H263Packetizer.java
+++ b/src/net/majorkernelpanic/streaming/rtp/H263Packetizer.java
@@ -64,7 +64,7 @@ public void stop() {
public void run() {
long time, duration = 0;
- int i = 0, j = 0, tr, delta = 10000;
+ int i = 0, j = 0, tr;
boolean firstFragment = true;
// This will skip the MPEG4 header if this step fails we can't stream anything :(
@@ -106,11 +106,11 @@ public void run() {
if (j>0) {
// We send one RTCP Sender Report every 5 secs
delta += duration/1000000;
- if (delta>5000 && duration/1000000>10) {
- delta = 0;
- report.setRtpTimestamp(ts);
- report.setNtpTimestamp(System.nanoTime());
- report.send();
+ if (intervalBetweenReports>0) {
+ if (delta>=intervalBetweenReports && duration/1000000>10) {
+ delta = 0;
+ report.send(System.nanoTime(),ts);
+ }
}
// We have found the end of the frame
stats.push(duration);
diff --git a/src/net/majorkernelpanic/streaming/rtp/H264Packetizer.java b/src/net/majorkernelpanic/streaming/rtp/H264Packetizer.java
index 3235416..58095e0 100644
--- a/src/net/majorkernelpanic/streaming/rtp/H264Packetizer.java
+++ b/src/net/majorkernelpanic/streaming/rtp/H264Packetizer.java
@@ -66,7 +66,8 @@ public void stop() {
public void run() {
- long duration = 0, oldtime = 0, delta = 10000, sum1 = 0, sum2 = 0;
+ long duration = 0, oldtime = 0, sum = 0, start = 0, drift = 0, now, count = 0;
+ boolean initoffset = false;
// This will skip the MPEG4 header if this step fails we can't stream anything :(
try {
@@ -82,6 +83,8 @@ public void run() {
return;
}
+ start = System.nanoTime();
+
// We read a NAL units from the input stream and we send them
try {
while (!Thread.interrupted()) {
@@ -90,22 +93,38 @@ public void run() {
oldtime = System.nanoTime();
send();
duration = System.nanoTime() - oldtime;
- sum2 += duration;
- // We send one RTCP Sender Report every 5 secs
delta += duration/1000000;
- if (delta>5000) {
- delta = 0;
- report.setRtpTimestamp(ts);
- report.setNtpTimestamp(System.nanoTime());
- report.send();
- //Log.d(TAG, "sum1: "+sum1/1000000+" sum2: "+sum2/1000000);
+ if (intervalBetweenReports>0) {
+ if (delta>=intervalBetweenReports) {
+ // We send a Sender Report
+ report.send(oldtime+duration,ts);
+ }
}
- // Calculates the average duration of a NAL unit
- stats.push(duration);
+ count += duration/1000000;
+ if (count>=7000) {
+ // We estimate the drift of the steam
+ now = System.nanoTime();
+ if (!initoffset || (now - start < 0)) {
+ sum = (now - start);
+ initoffset = true;
+ }
+ drift = (now - start) - sum;
+ count = 0;
+ //Log.d(TAG, "sum1: "+sum/1000000+" sum2: "+(now-start)/1000000+" drift: "+drift/1000000);
+ }
+
+
+ if (drift!=0) {
+ // Compensates if the stream is drifting
+ stats.push(duration+drift);
+ drift = 0;
+ } else stats.push(duration);
+ // Computes the average duration of a NAL unit
delay = stats.average();
- sum1 += delay;
+ sum += delay;
+ //Log.d(TAG,"duration: "+duration+" delay: "+delay);
}
} catch (IOException e) {
diff --git a/src/net/majorkernelpanic/streaming/rtsp/UriParser.java b/src/net/majorkernelpanic/streaming/rtsp/UriParser.java
index a5aa2be..a0cddd4 100644
--- a/src/net/majorkernelpanic/streaming/rtsp/UriParser.java
+++ b/src/net/majorkernelpanic/streaming/rtsp/UriParser.java
@@ -44,8 +44,7 @@
import android.hardware.Camera.CameraInfo;
/**
- * This class parses URIs and configures Sessions accordingly.
- * It is used by the HttpServer and the Rtsp server of this package to configure Sessions
+ * This class parses URIs received by the RTSP server and configures a Session accordingly.
*/
public class UriParser {
@@ -68,7 +67,7 @@ public static Session parse(String uri) throws IllegalStateException, IOExceptio
if (params.size()>0) {
builder.setAudioEncoder(AUDIO_NONE).setVideoEncoder(VIDEO_NONE);
-
+
// Those parameters must be parsed first or else they won't necessarily be taken into account
for (Iterator it = params.iterator();it.hasNext();) {
NameValuePair param = it.next();
@@ -90,7 +89,7 @@ else if (param.getValue().equalsIgnoreCase("front"))
}
// MULTICAST -> the stream will be sent to a multicast group
- // The default mutlicast address is 228.5.6.7, but the client can specify one
+ // The default mutlicast address is 228.5.6.7, but the client can specify another
else if (param.getName().equalsIgnoreCase("multicast")) {
if (param.getValue()!=null) {
try {
@@ -109,7 +108,7 @@ else if (param.getName().equalsIgnoreCase("multicast")) {
}
}
- // UNICAST -> the client can use this so specify where he wants the stream to be sent
+ // UNICAST -> the client can use this to specify where he wants the stream to be sent
else if (param.getName().equalsIgnoreCase("unicast")) {
if (param.getValue()!=null) {
try {
@@ -156,7 +155,7 @@ else if (param.getName().equalsIgnoreCase("amrnb") || param.getName().equalsIgno
else if (param.getName().equalsIgnoreCase("aac")) {
builder.setAudioEncoder(AUDIO_AAC);
}
-
+
}
}
@@ -166,9 +165,9 @@ else if (param.getName().equalsIgnoreCase("aac")) {
builder.setVideoEncoder(b.getVideoEncoder());
builder.setAudioEncoder(b.getAudioEncoder());
}
-
+
return builder.build();
-
+
}
}
diff --git a/src/net/majorkernelpanic/streaming/video/VideoStream.java b/src/net/majorkernelpanic/streaming/video/VideoStream.java
index 8baaf4e..2dc9328 100644
--- a/src/net/majorkernelpanic/streaming/video/VideoStream.java
+++ b/src/net/majorkernelpanic/streaming/video/VideoStream.java
@@ -204,7 +204,7 @@ public void prepare() throws IllegalStateException, IOException {
// Resets the recorder in case it is in a bad state
mMediaRecorder.reset();
- if (mSurfaceHolder == null)
+ if (mSurfaceHolder == null || mSurfaceHolder.getSurface() == null || !mSurfaceHolder.getSurface().isValid())
throw new IllegalStateException("Invalid surface holder !");
if (mCamera == null) {