summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Burdette <kburdet1@ford.com>2015-04-13 07:13:09 -0400
committerRapitis, Markos (M.J.) <mrapitis@ford.com>2015-05-06 10:37:54 -0400
commit68770af144c96083aabbce78435a820edfab94bd (patch)
treef2f39726b8962dddb3b60f4e29e86a3f8ffc3034
parentfd24f320d4536203d466d8b9abe2708722012f49 (diff)
downloadsdl_android-68770af144c96083aabbce78435a820edfab94bd.tar.gz
Added encoding API's to proxy
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java31
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/encoder/SdlEncoder.java149
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java55
3 files changed, 232 insertions, 3 deletions
diff --git a/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java b/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java
index 64020cf21..ae44f73a2 100644
--- a/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java
+++ b/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java
@@ -8,6 +8,9 @@ import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.Vector;
import android.util.Log;
+import android.view.Surface;
+
+import com.smartdevicelink.encoder.SdlEncoder;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.protocol.AbstractProtocol;
import com.smartdevicelink.protocol.IProtocolListener;
@@ -29,6 +32,7 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
StreamRPCPacketizer mRPCPacketizer = null;
StreamPacketizer mVideoPacketizer = null;
StreamPacketizer mAudioPacketizer = null;
+ SdlEncoder mSdlEncoder = null;
// Thread safety locks
Object TRANSPORT_REFERENCE_LOCK = new Object();
@@ -338,7 +342,7 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
{
mVideoPacketizer.stop();
return true;
- }
+ }
return false;
}
@@ -380,8 +384,31 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
return true;
}
return false;
+ }
+
+ public Surface createOpenGLInputSurface(int frameRate, int iFrameInterval, int width,
+ int height, int bitrate, SessionType sType, byte rpcSessionID) {
+ try {
+ mSdlEncoder = new SdlEncoder(frameRate, iFrameInterval, width,
+ height, bitrate, (PipedOutputStream) startStream(sType, rpcSessionID));
+ } catch (IOException e) {
+ return null;
+ }
+ return mSdlEncoder.prepareEncoder();
}
-
+
+ public void startEncoder () {
+ mSdlEncoder.startEncoder();
+ }
+
+ public void releaseEncoder() {
+ mSdlEncoder.releaseEncoder();
+ }
+
+ public void drainEncoder(boolean endOfStream) {
+ mSdlEncoder.drainEncoder(endOfStream);
+ }
+
@Override
public void sendStreamPacket(ProtocolMessage pm) {
sendMessage(pm);
diff --git a/sdl_android_lib/src/com/smartdevicelink/encoder/SdlEncoder.java b/sdl_android_lib/src/com/smartdevicelink/encoder/SdlEncoder.java
new file mode 100644
index 000000000..05dd95dad
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/encoder/SdlEncoder.java
@@ -0,0 +1,149 @@
+package com.smartdevicelink.encoder;
+
+import java.io.IOException;
+import java.io.PipedOutputStream;
+import java.nio.ByteBuffer;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.view.Surface;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class SdlEncoder {
+
+ // parameters for the encoder
+ private static final String _MIME_TYPE = "video/avc"; // H.264/AVC video
+ // private static final String MIME_TYPE = "video/mp4v-es"; //MPEG4 video
+ private static int _FRAME_RATE = 30;
+ private static int _IFRAME_INTERVAL = 5;
+ private static int _FRAME_WIDTH = 800;
+ private static int _FRAME_HEIGHT = 480;
+ private static int _BITRATE = 6000000;
+
+ // encoder state
+ private MediaCodec mEncoder;
+ private PipedOutputStream mOutputStream;
+
+ // allocate one of these up front so we don't need to do it every time
+ private MediaCodec.BufferInfo mBufferInfo;
+
+ public SdlEncoder (int frameRate, int iFrameInterval, int width,
+ int height, int bitrate, PipedOutputStream outputStream) {
+ _FRAME_RATE = frameRate;
+ _IFRAME_INTERVAL = iFrameInterval;
+ _FRAME_WIDTH = width;
+ _FRAME_HEIGHT = height;
+ _BITRATE = bitrate;
+ mOutputStream = outputStream;
+ }
+
+ public Surface prepareEncoder () {
+
+ mBufferInfo = new MediaCodec.BufferInfo();
+
+ MediaFormat format = MediaFormat.createVideoFormat(_MIME_TYPE, _FRAME_WIDTH,
+ _FRAME_HEIGHT);
+
+ // Set some properties. Failing to specify some of these can cause the
+ // MediaCodec
+ // configure() call to throw an unhelpful exception.
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, _BITRATE);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, _FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, _IFRAME_INTERVAL);
+
+ // Create a MediaCodec encoder, and configure it with our format. Get a
+ // Surface
+ // we can use for input and wrap it with a class that handles the EGL
+ // work.
+ //
+ // If you want to have two EGL contexts -- one for display, one for
+ // recording --
+ // you will likely want to defer instantiation of CodecInputSurface
+ // until after the
+ // "display" EGL context is created, then modify the eglCreateContext
+ // call to
+ // take eglGetCurrentContext() as the share_context argument.
+ try {
+ mEncoder = MediaCodec.createEncoderByType(_MIME_TYPE);
+ } catch (Exception e) {e.printStackTrace();}
+
+ mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ return mEncoder.createInputSurface();
+ }
+
+ public void startEncoder () {
+ mEncoder.start();
+ }
+
+ /**
+ * Releases encoder resources.
+ */
+ public void releaseEncoder() {
+ if (mEncoder != null) {
+ mEncoder.stop();
+ mEncoder.release();
+ mEncoder = null;
+ }
+ if (mOutputStream != null) {
+ try {
+ mOutputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Extracts all pending data from the encoder
+ * <p>
+ * If endOfStream is not set, this returns when there is no more data to
+ * drain. If it is set, we send EOS to the encoder, and then iterate until
+ * we see EOS on the output. Calling this with endOfStream set should be
+ * done once, right before stopping the muxer.
+ */
+ public void drainEncoder(boolean endOfStream) {
+ final int TIMEOUT_USEC = 10000;
+
+ if (endOfStream) {
+ mEncoder.signalEndOfInputStream();
+ }
+
+ ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();
+ while (true) {
+ int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo,
+ TIMEOUT_USEC);
+ if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ // no output available yet
+ if (!endOfStream) {
+ break; // out of while
+ }
+ } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ // not expected for an encoder
+ encoderOutputBuffers = mEncoder.getOutputBuffers();
+ } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ } else if (encoderStatus < 0) {
+ } else {
+ if (mBufferInfo.size != 0) {
+ byte[] dataToWrite = new byte[mBufferInfo.size];
+ encoderOutputBuffers[encoderStatus].get(dataToWrite,
+ mBufferInfo.offset, mBufferInfo.size);
+
+ try {
+ mOutputStream.write(dataToWrite, 0, mBufferInfo.size);
+ } catch (IOException e) {}
+ }
+
+ mEncoder.releaseOutputBuffer(encoderStatus, false);
+
+ if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ break; // out of while
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java
index f70bf4f43..0e3885efa 100644
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java
+++ b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java
@@ -30,6 +30,7 @@ import android.os.Handler;
import android.os.Looper;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.view.Surface;
import com.smartdevicelink.proxy.RPCRequestFactory;
import com.smartdevicelink.proxy.rpc.PutFile;
@@ -3117,7 +3118,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
pcmServiceEndResponseReceived = false;
pcmServiceEndResponse = false;
- sdlConn.stopAudioStream();
+ sdlConn.stopAudioStream();
FutureTask<Void> fTask = createFutureTask(new CallableMethod(2000));
ScheduledExecutorService scheduler = createScheduler();
@@ -3134,6 +3135,58 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
return false;
}
}
+
+ public Surface createOpenGLInputSurface(int frameRate, int iFrameInterval, int width,
+ int height, int bitrate) {
+
+ if (sdlSession == null) return null;
+ SdlConnection sdlConn = sdlSession.getSdlConnection();
+ if (sdlConn == null) return null;
+
+ navServiceStartResponseReceived = false;
+ navServiceStartResponse = false;
+ sdlConn.startService(SessionType.NAV, sdlSession.getSessionId());
+
+ FutureTask<Void> fTask = createFutureTask(new CallableMethod(2000));
+ ScheduledExecutorService scheduler = createScheduler();
+ scheduler.execute(fTask);
+
+ while (!navServiceStartResponseReceived && !fTask.isDone());
+ scheduler.shutdown();
+ scheduler = null;
+ fTask = null;
+
+ if (navServiceStartResponse) {
+ return sdlConn.createOpenGLInputSurface(frameRate, iFrameInterval, width,
+ height, bitrate, SessionType.NAV, sdlSession.getSessionId());
+ } else {
+ return null;
+ }
+ }
+
+ public void startEncoder () {
+ if (sdlSession == null) return;
+ SdlConnection sdlConn = sdlSession.getSdlConnection();
+ if (sdlConn == null) return;
+
+ sdlConn.startEncoder();
+ }
+
+ public void releaseEncoder() {
+ if (sdlSession == null) return;
+ SdlConnection sdlConn = sdlSession.getSdlConnection();
+ if (sdlConn == null) return;
+
+ sdlConn.releaseEncoder();
+ }
+
+ public void drainEncoder(boolean endOfStream) {
+ if (sdlSession == null) return;
+ SdlConnection sdlConn = sdlSession.getSdlConnection();
+ if (sdlConn == null) return;
+
+ sdlConn.drainEncoder(endOfStream);
+ }
private void NavServiceStarted() {
navServiceStartResponseReceived = true;