diff --git a/cloudphone/src/main/cpp/CasCommon.h b/cloudphone/src/main/cpp/CasCommon.h index 840f9b69ae9e1ebfd4e17d1b43ce6aaab10bc979..1e0367c124880e55b0887a75ff73057c3ecfa8bc 100644 --- a/cloudphone/src/main/cpp/CasCommon.h +++ b/cloudphone/src/main/cpp/CasCommon.h @@ -83,8 +83,9 @@ enum { CAS_SWITCH_FOREGROUND_ERROR = 0x1501, CAS_ORIENTATION = 0x1600, - CAS_EXIT = 0x1700, + CAS_EXIT = 0x1700, CAS_BACK_HOME = 0x1900, + CAS_REQUEST_CAMERA_KEY_FRAME = 0x2000, }; const int CAS_THREAD_RUNNING = 1; diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java index cfafdee3119a41945ee71b7aea2b0495cce31630..931afdfc6a521cd84f02477f063ceea444ac48d9 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -51,7 +51,10 @@ import com.huawei.cloudphone.common.CasState; import com.huawei.cloudphone.jniwrapper.JNIWrapper; import com.huawei.cloudphone.service.CasProcessor; import com.huawei.cloudphone.virtualdevice.VirtualDeviceSession; +import com.huawei.cloudphone.virtualdevice.camera.VirtualCameraManager; import com.huawei.cloudphone.virtualdevice.common.RingBufferVirtualDeviceIO; +import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager; +import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol; import java.util.HashMap; import java.util.Map; @@ -61,6 +64,7 @@ import java.util.TimerTask; import static android.view.KeyEvent.KEYCODE_BACK; import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; import static android.view.KeyEvent.KEYCODE_VOLUME_UP; +import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_CAMERA; import static com.huawei.cloudphone.api.CloudPhoneParas.DisplayMode.DISPLAY_MODE_FILL; import static com.huawei.cloudphone.common.CasState.CAS_CONNECT_EXCEPTION; import static com.huawei.cloudphone.utils.CasMediaUtils.isValidMediaConfig; @@ -571,7 +575,6 @@ public class CloudPhoneImpl implements ICloudPhone { } private void handleStateChangeCmd(Message msg) { - //当cae返回如下这些状态时,sdk主动发起stop synchronized (mCloudPhoneLock) { try { int state = msg.arg1; @@ -606,6 +609,16 @@ public class CloudPhoneImpl implements ICloudPhone { if (mVirtualDeviceSession != null) { mVirtualDeviceSession.start(); } + } else if (state == CasState.CAS_REQUEST_CAMERA_KEY_FRAME) { + VirtualDeviceProtocol virtualDevice = mVirtualDeviceSession.getVirtualDevice(); + if (virtualDevice == null) { + return; + } + VirtualDeviceManager virtualDeviceManager = virtualDevice.getVirtualDeviceManager(DEV_TYPE_CAMERA); + if (virtualDeviceManager == null) { + return; + } + ((VirtualCameraManager) virtualDeviceManager).generateKeyFrame(); } } catch (Exception e) { CASLog.e(TAG, "handleStateChangeCmd failed. " + e.getMessage()); diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/common/CasState.java b/cloudphone/src/main/java/com/huawei/cloudphone/common/CasState.java index 163f2ed827c7f8d2f4e6761d201bd27871e8e850..6f5a6bf54509e18848973462055faa05f19cc22c 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/common/CasState.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/common/CasState.java @@ -54,6 +54,7 @@ public class CasState { static public final int CAS_SWITCH_BACKGROUND_ERROR = 0x1301; static public final int CAS_SWITCH_FOREGROUND_SUCCESS = 0x1400; static public final int CAS_EXIT = 0x1700; + static public final int CAS_REQUEST_CAMERA_KEY_FRAME = 0x2000; static public final int CAS_INVALID_CMD = 0xFFFF; public static class CasStateMsg { diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/VirtualDeviceSession.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/VirtualDeviceSession.java index a36a03c8225104052accafcaf34b6f5664c447c3..ce0a5eb3b81eac797c042e3e4ab2199dcf0ba9bf 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/VirtualDeviceSession.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/VirtualDeviceSession.java @@ -36,14 +36,17 @@ public class VirtualDeviceSession { mContext = context; } - public void setCameraSurfaceHolder(SurfaceHolder surfaceHolder) { - mParamBundle.setSurfaceHolder(surfaceHolder); - } - public void setVirtualDeviceIoHook(IVirtualDeviceIO virtualDeviceIO) { mVirtualDeviceIO = virtualDeviceIO; } + public VirtualDeviceProtocol getVirtualDevice() { + if (mVirtualDevice != null) { + return mVirtualDevice; + } + return null; + } + public void start() { synchronized (mLock) { mVirtualDevice = new VirtualDeviceProtocol(mVirtualDeviceIO, mContext); diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/AvcEncoder.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/AvcEncoder.java index f488194d5c69d5b06d62718c9bdaada352a2d159..f0b3bd9acbd208d1072b5c45791e295aaa07d1f5 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/AvcEncoder.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/AvcEncoder.java @@ -17,12 +17,15 @@ package com.huawei.cloudphone.virtualdevice.camera; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar; +import static android.os.Build.VERSION_CODES.M; import android.graphics.ImageFormat; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.MediaFormat; +import android.os.Build; +import android.os.Bundle; import android.util.Log; import java.io.IOException; @@ -33,7 +36,7 @@ public class AvcEncoder { private static final String MIME_TYPE = "video/avc"; private static final int TIMEOUT_USC = 1000; private static final int MICROSECONDS_PER_SECOND = 1000000; - private static final int I_FRAME_INTERVAL = 15; + private static final int I_FRAME_INTERVAL = 150; private static int colorFormat = COLOR_FormatYUV420Planar; private MediaCodec mediaCodec; @@ -42,10 +45,21 @@ public class AvcEncoder { private int mFrameRate; private long genIndex; private byte[] configByte; + private boolean isRequestKeyFrame = false; private AvcEncoderDataHandler dataHandler; - public void getIFrame() { - mediaCodec.flush(); + public void flush() { + if (mediaCodec != null) { + mediaCodec.flush(); + } + } + + public void generateKeyFrame() { + if (Build.VERSION.SDK_INT >= M && mediaCodec != null) { + Bundle params = new Bundle(); + params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); + mediaCodec.setParameters(params); + } } public AvcEncoder(int width, int height, int frameRate, int bitrate, AvcEncoderDataHandler dataHandler) { @@ -90,10 +104,12 @@ public class AvcEncoder { inputBuffer(frame); int outBufLen = 0; while (true) { + if (genIndex % I_FRAME_INTERVAL == 0 && !isRequestKeyFrame) { + generateKeyFrame(); + isRequestKeyFrame = true; + } MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outBufId = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USC); - Log.i(TAG, "bufferInfo: size = " + bufferInfo.size + ", offset = " + bufferInfo.offset + - ", flags = " + bufferInfo.flags + ", presentationTimeUs = " + bufferInfo.presentationTimeUs); if (outBufId >= 0) { ByteBuffer outBuf = mediaCodec.getOutputBuffer(outBufId); byte[] h264Buf = new byte[bufferInfo.size]; @@ -113,11 +129,11 @@ public class AvcEncoder { dataHandler.handleData(encodeBuff, outBufLen); } mediaCodec.releaseOutputBuffer(outBufId, false); + isRequestKeyFrame = false; } else if (outBufId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { Log.e(TAG, "Output format changed : " + outBufId); return; } else if (outBufId == MediaCodec.INFO_TRY_AGAIN_LATER) { - Log.e(TAG, "Codec waiting : " + outBufId); return; } else { Log.e(TAG, "Unknown outBufId : " + outBufId); diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCamera.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCamera.java index 12993c776b7cf53039e62c656690ec5ca2121218..5401b4160c2594984cdaa7cca0baf333a5c300de 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCamera.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCamera.java @@ -23,9 +23,7 @@ import static android.hardware.Camera.Parameters.FOCUS_MODE_INFINITY; import static android.hardware.Camera.Parameters.FOCUS_MODE_MACRO; import android.graphics.ImageFormat; -import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.graphics.YuvImage; import android.hardware.Camera; import android.opengl.GLES11Ext; import android.util.Log; @@ -34,7 +32,6 @@ import android.view.SurfaceHolder; import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener; import com.huawei.cloudphone.virtualdevice.common.ParamBundle; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; @@ -142,7 +139,7 @@ public class VirtualCamera implements Camera.PreviewCallback { mAvcCodec = new AvcEncoder(mWidth, mHeight, param.getPreviewFrameRate(), mBitrate , new H264DataHandler()); mH264Buffer = new byte[mWidth * mHeight * 3 / 2]; - mAvcCodec.getIFrame(); + mAvcCodec.flush(); } mCamera.startPreview(); return 0; @@ -268,6 +265,12 @@ public class VirtualCamera implements Camera.PreviewCallback { return param; } + public void generateKeyFrame() { + if (mAvcCodec != null) { + mAvcCodec.generateKeyFrame(); + } + } + class H264DataHandler implements AvcEncoderDataHandler { @Override public void handleData(byte[] data, int length) { diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCameraManager.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCameraManager.java index 606eae61509a5531c355ffd3dfce72d8860121ab..6619a3df70b0119811bc99d33907612bb13598b8 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCameraManager.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/camera/VirtualCameraManager.java @@ -210,6 +210,11 @@ public class VirtualCameraManager extends VirtualDeviceManager { mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA); } + public void generateKeyFrame() { + if (mVirtualCamera != null) { + mVirtualCamera.generateKeyFrame(); + } + } class CameraDataListener implements IVirtualDeviceDataListener { @Override diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java index ae769efea30df8981ebb023e30f73bab01666b59..579ff7eca6c0ed7544e307fad3b588f37d5b85b3 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java @@ -82,6 +82,13 @@ public class VirtualDeviceProtocol { mContext.registerReceiver(mPermissionResultReceiver, intentFilter); } + public VirtualDeviceManager getVirtualDeviceManager(short devType) { + if (virtualDeviceManagers == null) { + return null; + } + return virtualDeviceManagers.get(devType); + } + public void processMsg(MsgHeader header, byte[] body) { VirtualDeviceManager virtualDeviceManager = virtualDeviceManagers.get(header.mDeviceType); if (virtualDeviceManager == null) {