From c8869551574b13a665e1775a1e084d5a293c8496 Mon Sep 17 00:00:00 2001 From: nocturnalprelude Date: Thu, 17 Jul 2014 13:00:28 +0200 Subject: [PATCH 01/13] Update VideoStream.java This trick will avoid to have stretched video preview when starting the camera --- .../streaming/video/VideoStream.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/src/net/majorkernelpanic/streaming/video/VideoStream.java b/src/net/majorkernelpanic/streaming/video/VideoStream.java index 165fb8db..9966f9a1 100644 --- a/src/net/majorkernelpanic/streaming/video/VideoStream.java +++ b/src/net/majorkernelpanic/streaming/video/VideoStream.java @@ -683,6 +683,133 @@ protected void unlockCamera() { } + + + +public void setCameraPreviewSize(int w, int h) { + + List size = getSizes(); + Size size_to_use = getOptimalSize(size, w, h); + + System.out.println("Setting preview to: "+ w + h); + + if (mCamera != null) { + + if (mStreaming && mMode == MODE_MEDIARECORDER_API) { + lockCamera(); + } + + Parameters parameters = mCamera.getParameters(); + + parameters.setPreviewSize(size_to_use.width, size_to_use.height); + //parameters.setPreviewSize(176, 144); + try { + mCamera.setParameters(parameters); + System.out.println("Setting preview to: "+ size_to_use.width + size_to_use.height + " DONE!!"); + Size z = parameters.getPreviewSize(); + int w_temp = z.width; + int h_temp = z.height; + System.out.println("Saved preview is: "+ w_temp + h_temp); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + else { + System.out.println("Setting preview to: "+ w + h + " FAILED!!"); + } + + + + } + + + public synchronized List getSizes() { + + List size = null; + + if (mCamera != null) { + + if (mStreaming && mMode == MODE_MEDIARECORDER_API) { + lockCamera(); + } + + + try { + + Parameters parameters = mCamera.getParameters(); + size = parameters.getSupportedPreviewSizes(); + + System.out.println("Some of the supported preview is " + size.get(0).width + "x" + size.get(0).height); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + + + + } + + else { + System.out.println("Failed to get preview sizes"); + } + + return size; + } + + + + private Size getOptimalSize(List sizes, int w, int h) { + + final double ASPECT_TOLERANCE = 0.2; + double targetRatio = (double) w / h; + if (sizes == null) + return null; + Size optimalSize = null; + double minDiff = Double.MAX_VALUE; + int targetHeight = h; + // Try to find an size match aspect ratio and size + for (Size size : sizes) + { + + double ratio = (double) size.width / size.height; + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) + continue; + if (Math.abs(size.height - targetHeight) < minDiff) + { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + // Cannot find the one match the aspect ratio, ignore the requirement + + if (optimalSize == null) + { + minDiff = Double.MAX_VALUE; + for (Size size : sizes) { + if (Math.abs(size.height - targetHeight) < minDiff) + { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + } + + + + return optimalSize; + } + + + + + + + /** * Computes the average frame rate at which the preview callback is called. * We will then use this average framerate with the MediaCodec. From fb9b3d297258baf3d79d579a4a264bd51c108ea0 Mon Sep 17 00:00:00 2001 From: nocturnalprelude Date: Thu, 17 Jul 2014 13:02:40 +0200 Subject: [PATCH 02/13] Update Session.java This trick will avoid to have stretched video preview when starting the camera --- .../majorkernelpanic/streaming/Session.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/net/majorkernelpanic/streaming/Session.java b/src/net/majorkernelpanic/streaming/Session.java index a904fd4f..a846746e 100644 --- a/src/net/majorkernelpanic/streaming/Session.java +++ b/src/net/majorkernelpanic/streaming/Session.java @@ -642,6 +642,51 @@ public void run() { }); } + + + +public void setSize(final int w, final int h) { + + sHandler.post(new Runnable() { + + @Override + public void run() { + if (mVideoStream != null) { + try { + + mVideoStream.setCameraPreviewSize(w,h); + } catch (RuntimeException e) { + + } + } + } + }); + + } + + + // + public List getAvailableSizes() { + + sHandler.post(new Runnable() { + + @Override + public void run() { + if (mVideoStream != null) { + try { + size = mVideoStream.getSizes(); + } catch (RuntimeException e) { + + } + } + } + }); + return size; + } + + + + /** Deletes all existing tracks & release associated resources. */ public void release() { removeAudioTrack(); From e9e71d96478fd1119e3ff7d027b072f96325980c Mon Sep 17 00:00:00 2001 From: Eder Bastos Date: Wed, 30 Jul 2014 14:59:12 -0400 Subject: [PATCH 03/13] Allow for generating SDPs that target a user-defined port for the video RTP stream. --- src/net/majorkernelpanic/streaming/SessionBuilder.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/net/majorkernelpanic/streaming/SessionBuilder.java b/src/net/majorkernelpanic/streaming/SessionBuilder.java index 1ba39c41..0fd3b4ef 100644 --- a/src/net/majorkernelpanic/streaming/SessionBuilder.java +++ b/src/net/majorkernelpanic/streaming/SessionBuilder.java @@ -74,6 +74,7 @@ public class SessionBuilder { private SurfaceView mSurfaceView = null; private String mOrigin = null; private String mDestination = null; + private int mDestinationVideoPort = 5006; private Session.Callback mCallback = null; // Removes the default public constructor @@ -141,7 +142,7 @@ public Session build() { video.setVideoQuality(mVideoQuality); video.setSurfaceView(mSurfaceView); video.setPreviewOrientation(mOrientation); - video.setDestinationPorts(5006); + video.setDestinationPorts(mDestinationVideoPort); } if (session.getAudioTrack()!=null) { @@ -169,6 +170,13 @@ public SessionBuilder setDestination(String destination) { return this; } + /** Sets the destination IP & video port for the session. */ + public SessionBuilder setDestination(String destination, int videoPort) { + mDestination = destination; + mDestinationVideoPort = videoPort; + return this; + } + /** Sets the origin of the session. It appears in the SDP of the session. */ public SessionBuilder setOrigin(String origin) { mOrigin = origin; From 0dfda909a4896e7ec8c6db5a837a5d31f8451a96 Mon Sep 17 00:00:00 2001 From: nocturnalprelude Date: Mon, 25 Aug 2014 16:34:07 +0200 Subject: [PATCH 04/13] Update VideoStream.java --- src/net/majorkernelpanic/streaming/video/VideoStream.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net/majorkernelpanic/streaming/video/VideoStream.java b/src/net/majorkernelpanic/streaming/video/VideoStream.java index 165fb8db..021b7609 100644 --- a/src/net/majorkernelpanic/streaming/video/VideoStream.java +++ b/src/net/majorkernelpanic/streaming/video/VideoStream.java @@ -336,6 +336,8 @@ protected void encodeWithMediaRecorder() throws IOException, ConfNotSupportedExc destroyCamera(); createCamera(); + + mCamera.stopPreview(); // The camera must be unlocked before the MediaRecorder can use it unlockCamera(); From 3e5e958e80d8f5edff01b46162ee84e30338d109 Mon Sep 17 00:00:00 2001 From: nocturnalprelude Date: Tue, 26 Aug 2014 12:27:30 +0200 Subject: [PATCH 05/13] Update VideoStream.java enabling autofocus --- src/net/majorkernelpanic/streaming/video/VideoStream.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net/majorkernelpanic/streaming/video/VideoStream.java b/src/net/majorkernelpanic/streaming/video/VideoStream.java index 165fb8db..a1c20857 100644 --- a/src/net/majorkernelpanic/streaming/video/VideoStream.java +++ b/src/net/majorkernelpanic/streaming/video/VideoStream.java @@ -582,6 +582,7 @@ public void onError(int error, Camera camera) { // If the phone has a flash, we turn it on/off according to mFlashEnabled // setRecordingHint(true) is a very nice optimization if you plane to only use the Camera for recording Parameters parameters = mCamera.getParameters(); + parameters.setFocusMode("continuous-picture"); if (parameters.getFlashMode()!=null) { parameters.setFlashMode(mFlashEnabled?Parameters.FLASH_MODE_TORCH:Parameters.FLASH_MODE_OFF); } @@ -636,6 +637,7 @@ protected synchronized void updateCamera() throws RuntimeException { } Parameters parameters = mCamera.getParameters(); + parameters.setFocusMode("continuous-picture"); mQuality = VideoQuality.determineClosestSupportedResolution(parameters, mQuality); int[] max = VideoQuality.determineMaximumSupportedFramerate(parameters); From 88e7df5d46c24f901a7c4d0d34b1a73cef4393dc Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 11:01:30 -0300 Subject: [PATCH 06/13] change suggested codec to MODE_MEDIACODEC_API_2 --- src/net/majorkernelpanic/streaming/MediaStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/majorkernelpanic/streaming/MediaStream.java b/src/net/majorkernelpanic/streaming/MediaStream.java index 95fcecdb..27683289 100644 --- a/src/net/majorkernelpanic/streaming/MediaStream.java +++ b/src/net/majorkernelpanic/streaming/MediaStream.java @@ -79,7 +79,7 @@ public abstract class MediaStream implements Stream { try { Class.forName("android.media.MediaCodec"); // Will be set to MODE_MEDIACODEC_API at some point... - sSuggestedMode = MODE_MEDIACODEC_API; + sSuggestedMode = MODE_MEDIACODEC_API_2; Log.i(TAG,"Phone supports the MediaCoded API"); } catch (ClassNotFoundException e) { sSuggestedMode = MODE_MEDIARECORDER_API; From ca6161d5f427a15b39c4ae8f4dc8185c6f0ee228 Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 11:03:31 -0300 Subject: [PATCH 07/13] log error to give some feedback and comment not compiling (and not used) piece of code --- src/net/majorkernelpanic/streaming/Session.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/net/majorkernelpanic/streaming/Session.java b/src/net/majorkernelpanic/streaming/Session.java index a846746e..0d3590a1 100644 --- a/src/net/majorkernelpanic/streaming/Session.java +++ b/src/net/majorkernelpanic/streaming/Session.java @@ -39,6 +39,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.util.Log; /** * You should instantiate this class with the {@link SessionBuilder}.
@@ -129,7 +130,7 @@ public Session() { mOrigin = "127.0.0.1"; } - /** + /** * The callback interface you need to implement to get some feedback * Those will be called from the UI thread. */ @@ -369,7 +370,9 @@ public void configure() { public void run() { try { syncConfigure(); - } catch (Exception e) {}; + } catch (Exception e) { + Log.e(TAG, Log.getStackTraceString(e)); + }; } }); } @@ -622,11 +625,14 @@ public int getCamera() { } + /** * Toggles the LED of the phone if it has one. * You can get the current state of the flash with * {@link Session#getVideoTrack()} and {@link VideoStream#getFlashState()}. **/ + + /* public void toggleFlash() { mHandler.post(new Runnable() { @Override @@ -684,7 +690,7 @@ public void run() { return size; } - +*/ /** Deletes all existing tracks & release associated resources. */ From 5334d8209777cd42407a7960664db7119694a795 Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 11:08:44 -0300 Subject: [PATCH 08/13] fixed error in the calculation of a closest supported resolution --- src/net/majorkernelpanic/streaming/video/VideoQuality.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/majorkernelpanic/streaming/video/VideoQuality.java b/src/net/majorkernelpanic/streaming/video/VideoQuality.java index 8c857f86..a4f558f2 100644 --- a/src/net/majorkernelpanic/streaming/video/VideoQuality.java +++ b/src/net/majorkernelpanic/streaming/video/VideoQuality.java @@ -79,7 +79,7 @@ public boolean equals(VideoQuality quality) { } public VideoQuality clone() { - return new VideoQuality(resX,resY,framerate,bitrate); + return new VideoQuality(resX,resY,framerate, bitrate); } public static VideoQuality parseQuality(String str) { @@ -113,7 +113,7 @@ public static VideoQuality determineClosestSupportedResolution(Camera.Parameters for (Iterator it = supportedSizes.iterator(); it.hasNext();) { Size size = it.next(); supportedSizesStr += size.width+"x"+size.height+(it.hasNext()?", ":""); - int dist = Math.abs(quality.resX - size.width); + int dist = Math.abs(quality.resX - size.width) + Math.abs(quality.resY - size.height); if (dist Date: Thu, 19 Feb 2015 11:17:01 -0300 Subject: [PATCH 09/13] Check if using MediaCodec API with the buffer (not with surface) method and a high resolution before changing the mode to use MediaRecorder --- src/net/majorkernelpanic/streaming/video/H264Stream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/majorkernelpanic/streaming/video/H264Stream.java b/src/net/majorkernelpanic/streaming/video/H264Stream.java index a45034e1..a67661b6 100644 --- a/src/net/majorkernelpanic/streaming/video/H264Stream.java +++ b/src/net/majorkernelpanic/streaming/video/H264Stream.java @@ -125,7 +125,7 @@ private MP4Config testMediaCodecAPI() throws RuntimeException, IOException { createCamera(); updateCamera(); try { - if (mQuality.resX>=640) { + if (mQuality.resX>=640 && mMode == MODE_MEDIACODEC_API) { // Using the MediaCodec API with the buffer method for high resolutions is too slow mMode = MODE_MEDIARECORDER_API; } From 7ef7a01654965b38d04315d1e06fc9f39648b35f Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 11:36:25 -0300 Subject: [PATCH 10/13] Video Changes * Added info to logs * Fixed null pointer exception * Added constants to define Camera Picture, Preview and SurfaceView sizes and use them * Added method to list information about media codecs available on the device * Changed method to encode using MediaCodec with surface to avoid the encoding framerate to be changed --- .../streaming/video/VideoStream.java | 93 ++++++++++++++----- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/src/net/majorkernelpanic/streaming/video/VideoStream.java b/src/net/majorkernelpanic/streaming/video/VideoStream.java index 6ef04f91..a23aad70 100644 --- a/src/net/majorkernelpanic/streaming/video/VideoStream.java +++ b/src/net/majorkernelpanic/streaming/video/VideoStream.java @@ -23,6 +23,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -43,6 +45,7 @@ import android.hardware.Camera.Parameters; import android.media.MediaCodec; import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.media.MediaFormat; import android.media.MediaRecorder; import android.os.Looper; @@ -58,6 +61,15 @@ public abstract class VideoStream extends MediaStream { protected final static String TAG = "VideoStream"; + /* Input sizes are used to define the Camera Picture and Preview size, and the SurfaceView size. + * Output size (720p) is only used to encode the output video taken from the SurfaceView (the published video). + * Notice that both input and output sizes have the same ratio. + */ + public static final int VIDEO_WIDTH_INPUT = 640; + public static final int VIDEO_HEIGHT_INPUT = 480; + private static final int VIDEO_WIDTH_OUTPUT = 640; + private static final int VIDEO_HEIGHT_OUTPUT = 480; + protected VideoQuality mRequestedQuality = VideoQuality.DEFAULT_VIDEO_QUALITY.clone(); protected VideoQuality mQuality = mRequestedQuality.clone(); protected SurfaceHolder.Callback mSurfaceHolderCallback = null; @@ -154,7 +166,7 @@ public synchronized void setSurfaceView(SurfaceView view) { if (mSurfaceHolderCallback != null && mSurfaceView != null && mSurfaceView.getHolder() != null) { mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback); } - if (mSurfaceView.getHolder() != null) { + if (mSurfaceView != null && mSurfaceView.getHolder() != null) { mSurfaceHolderCallback = new Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { @@ -276,7 +288,7 @@ public synchronized void configure() throws IllegalStateException, IOException { public synchronized void start() throws IllegalStateException, IOException { if (!mPreviewStarted) mCameraOpenedManually = false; super.start(); - Log.d(TAG,"Stream configuration: FPS: "+mQuality.framerate+" Width: "+mQuality.resX+" Height: "+mQuality.resY); + Log.d(TAG,"Stream configuration: FPS: "+mQuality.framerate+" Width: "+mQuality.resX+" Height: "+mQuality.resY + " bitrate: " + mQuality.bitrate); } /** Stops the stream. */ @@ -327,7 +339,7 @@ public synchronized void stopPreview() { */ protected void encodeWithMediaRecorder() throws IOException, ConfNotSupportedException { - Log.d(TAG,"Video encoded using the MediaRecorder API"); + Log.i(TAG,"Video encoded using the MediaRecorder API"); // We need a local socket to forward data output by the camera to the packetizer createSockets(); @@ -410,7 +422,7 @@ protected void encodeWithMediaCodec() throws RuntimeException, IOException { @SuppressLint("NewApi") protected void encodeWithMediaCodecMethod1() throws RuntimeException, IOException { - Log.d(TAG,"Video encoded using the MediaCodec API with a buffer"); + Log.i(TAG,"Video encoded using the MediaCodec API with a buffer"); // Updates the parameters of the camera if needed createCamera(); @@ -480,6 +492,27 @@ public void onPreviewFrame(byte[] data, Camera camera) { } + private void listCodecInfo() { + int codecCount = MediaCodecList.getCodecCount(); + for(int i=0; i size = getSizes(); - Size size_to_use = getOptimalSize(size, w, h); + List size = getSizes(); + Camera.Size size_to_use = getOptimalSize(size, w, h); System.out.println("Setting preview to: "+ w + h); @@ -710,7 +753,7 @@ public void setCameraPreviewSize(int w, int h) { try { mCamera.setParameters(parameters); System.out.println("Setting preview to: "+ size_to_use.width + size_to_use.height + " DONE!!"); - Size z = parameters.getPreviewSize(); + Camera.Size z = parameters.getPreviewSize(); int w_temp = z.width; int h_temp = z.height; System.out.println("Saved preview is: "+ w_temp + h_temp); @@ -730,9 +773,9 @@ public void setCameraPreviewSize(int w, int h) { } - public synchronized List getSizes() { + public synchronized List getSizes() { - List size = null; + List size = null; if (mCamera != null) { @@ -767,17 +810,17 @@ public synchronized List getSizes() { - private Size getOptimalSize(List sizes, int w, int h) { + private Camera.Size getOptimalSize(List sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.2; double targetRatio = (double) w / h; if (sizes == null) return null; - Size optimalSize = null; + Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size - for (Size size : sizes) + for (Camera.Size size : sizes) { double ratio = (double) size.width / size.height; @@ -794,7 +837,7 @@ private Size getOptimalSize(List sizes, int w, int h) { if (optimalSize == null) { minDiff = Double.MAX_VALUE; - for (Size size : sizes) { + for (Camera.Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; From 4b2c666b482cf436ab44a0e9f0a264bedc62648c Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 11:42:47 -0300 Subject: [PATCH 11/13] Added constructor to SurfaceView to create a dummy surface view --- src/net/majorkernelpanic/streaming/gl/SurfaceView.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/net/majorkernelpanic/streaming/gl/SurfaceView.java b/src/net/majorkernelpanic/streaming/gl/SurfaceView.java index d78b1721..f499f67a 100644 --- a/src/net/majorkernelpanic/streaming/gl/SurfaceView.java +++ b/src/net/majorkernelpanic/streaming/gl/SurfaceView.java @@ -90,9 +90,15 @@ public SurfaceView(Context context, AttributeSet attrs) { super(context, attrs); mHandler = new Handler(); getHolder().addCallback(this); - } + } + + public SurfaceView(Context context) { + super(context); + mHandler = new Handler(); + getHolder().addCallback(this); + } - public void setAspectRatioMode(int mode) { + public void setAspectRatioMode(int mode) { mAspectRatioMode = mode; } From 9280aae27a6704e14da91705e04af6217797f44f Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 11:45:00 -0300 Subject: [PATCH 12/13] added error to InvalidSurfaceException so it doesn't loose the error itself --- .../streaming/exceptions/InvalidSurfaceException.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/net/majorkernelpanic/streaming/exceptions/InvalidSurfaceException.java b/src/net/majorkernelpanic/streaming/exceptions/InvalidSurfaceException.java index 45cd5b16..f7423b44 100644 --- a/src/net/majorkernelpanic/streaming/exceptions/InvalidSurfaceException.java +++ b/src/net/majorkernelpanic/streaming/exceptions/InvalidSurfaceException.java @@ -27,5 +27,8 @@ public class InvalidSurfaceException extends RuntimeException { public InvalidSurfaceException(String message) { super(message); } + public InvalidSurfaceException(String message, Throwable t) { + super(message, t); + } } From c4f277bf194f22091fa6e3ce68976aba9b238914 Mon Sep 17 00:00:00 2001 From: Ashi Date: Thu, 19 Feb 2015 12:04:59 -0300 Subject: [PATCH 13/13] reverted change --- src/net/majorkernelpanic/streaming/MediaStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/majorkernelpanic/streaming/MediaStream.java b/src/net/majorkernelpanic/streaming/MediaStream.java index 27683289..95fcecdb 100644 --- a/src/net/majorkernelpanic/streaming/MediaStream.java +++ b/src/net/majorkernelpanic/streaming/MediaStream.java @@ -79,7 +79,7 @@ public abstract class MediaStream implements Stream { try { Class.forName("android.media.MediaCodec"); // Will be set to MODE_MEDIACODEC_API at some point... - sSuggestedMode = MODE_MEDIACODEC_API_2; + sSuggestedMode = MODE_MEDIACODEC_API; Log.i(TAG,"Phone supports the MediaCoded API"); } catch (ClassNotFoundException e) { sSuggestedMode = MODE_MEDIARECORDER_API;