diff options
author | Kevin Burdette <kburdet1@ford.com> | 2015-04-13 07:13:09 -0400 |
---|---|---|
committer | Rapitis, Markos (M.J.) <mrapitis@ford.com> | 2015-05-06 10:37:54 -0400 |
commit | 68770af144c96083aabbce78435a820edfab94bd (patch) | |
tree | f2f39726b8962dddb3b60f4e29e86a3f8ffc3034 | |
parent | fd24f320d4536203d466d8b9abe2708722012f49 (diff) | |
download | sdl_android-68770af144c96083aabbce78435a820edfab94bd.tar.gz |
Added encoding API's to proxy
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;
|