From e4d2e4bfe4a4ce53adab97accfeee086a0097388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 17 Aug 2021 19:02:29 +0900 Subject: [PATCH 01/40] =?UTF-8?q?=E6=8F=8F=E7=94=BB=E7=AF=84=E5=9B=B2?= =?UTF-8?q?=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recorder/AbstractMJPEGPreviewServer.java | 19 ++---- .../host/recorder/AbstractPreviewServer.java | 20 ++---- .../camera/CameraSurfaceDrawingThread.java | 19 ++---- .../ScreenCastSurfaceDrawingThread.java | 1 - .../recorder/uvc/UvcSurfaceDrawingThread.java | 1 - .../streaming/gles/EGLSurfaceBase.java | 22 ++++++ .../gles/EGLSurfaceDrawingThread.java | 68 +++++++++++-------- .../streaming/gles/SurfaceTextureManager.java | 7 ++ .../gles/SurfaceTextureRenderer.java | 5 ++ .../streaming/mjpeg/MJPEGQuality.java | 10 +++ .../streaming/mjpeg/SurfaceMJPEGEncoder.java | 15 ++-- .../streaming/video/SurfaceVideoEncoder.java | 2 +- .../streaming/video/VideoEncoder.java | 6 ++ .../streaming/video/VideoQuality.java | 19 ++++++ 14 files changed, 142 insertions(+), 72 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index 22804c2584..feeadc7adb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -132,20 +132,15 @@ private void setMJPEGQuality(MJPEGQuality quality) { HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - Rect rect = settings.getDrawingRange(); - if (rect != null) { - quality.setWidth(rect.width()); - quality.setHeight(rect.height()); - } else { - EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); - int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); - int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); - quality.setWidth(w); - quality.setHeight(h); - } + EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); + Size previewSize = settings.getPreviewSize(); + int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); + int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); + quality.setWidth(w); + quality.setHeight(h); quality.setFrameRate(settings.getPreviewMaxFrameRate()); quality.setQuality(settings.getPreviewQuality()); + quality.setDrawingRange(settings.getDrawingRange()); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java index e89537d3d1..7f9fd3a872 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java @@ -1,7 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.recorder; import android.content.Context; -import android.graphics.Rect; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioPlaybackCaptureConfiguration; @@ -192,18 +191,13 @@ public void setVideoQuality(VideoQuality videoQuality) { HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - Rect rect = settings.getDrawingRange(); - if (rect != null) { - videoQuality.setVideoWidth(rect.width()); - videoQuality.setVideoHeight(rect.height()); - } else { - EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); - int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); - int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); - videoQuality.setVideoWidth(w); - videoQuality.setVideoHeight(h); - } + EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); + Size previewSize = settings.getPreviewSize(); + int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); + int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); + videoQuality.setVideoWidth(w); + videoQuality.setVideoHeight(h); + videoQuality.setDrawingRange(settings.getDrawingRange()); videoQuality.setBitRate(settings.getPreviewBitRate()); videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java index 6c3e88350f..41fbcc5f03 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java @@ -71,24 +71,19 @@ protected void onStopped() { public void start() { HostMediaRecorder.Settings settings = mRecorder.getSettings(); Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - setSize(previewSize.getWidth(), previewSize.getHeight()); - setDrawingRange(settings.getDrawingRange()); - super.start(); - } + int w = isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); + int h = isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); + setSize(w, h); + super.start(); } @Override protected SurfaceTextureManager createStManager() { + HostMediaRecorder.Settings settings = mRecorder.getSettings(); + Size previewSize = settings.getPreviewSize(); SurfaceTextureManager manager = new SurfaceTextureManager(); SurfaceTexture st = manager.getSurfaceTexture(); - st.setDefaultBufferSize(getWidth(), getHeight()); - if (getDrawingRange() != null) { - // カメラは描画時に端末の向きによって回転するので、ここでは描画範囲の計算も回転してから行う - int w = isSwappedDimensions() ? getHeight() : getWidth(); - int h = isSwappedDimensions() ? getWidth() : getHeight(); - manager.setDrawingRange(getDrawingRange(), w, h); - } + st.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); return manager; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java index f47da6287e..d814b7794c 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java @@ -65,7 +65,6 @@ public void start() { int width = isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); int height = isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); setSize(width, height); - setDrawingRange(settings.getDrawingRange()); super.start(); } } diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java index 2505fc6cad..018fb2acad 100644 --- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java @@ -41,7 +41,6 @@ public void start() { MediaRecorder.Settings settings = mRecorder.getSettings(); Size previewSize = settings.getPreviewSize(); setSize(previewSize.getWidth(), previewSize.getHeight()); - setDrawingRange(settings.getDrawingRange()); super.start(); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java index ed04bc270c..1d24c976a2 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java @@ -1,5 +1,6 @@ package org.deviceconnect.android.libmedia.streaming.gles; +import android.graphics.Rect; import android.opengl.EGL14; import android.opengl.EGLSurface; import android.opengl.GLES20; @@ -15,6 +16,7 @@ public abstract class EGLSurfaceBase { private int mWidth = -1; private int mHeight = -1; private Object mTag; + private Rect mDrawingRange; EGLSurfaceBase() { } @@ -83,6 +85,26 @@ public int getHeight() { } } + /** + * 描画範囲を設定します. + * + * @param rect 描画範囲 + */ + public void setDrawingRange(Rect rect) { + mDrawingRange = rect; + } + + /** + * 描画範囲を取得します. + * + * 未設定の場合には null を返却します。 + * + * @return 描画範囲 + */ + public Rect getDrawingRange() { + return mDrawingRange; + } + /** * Discards all resources held by this class, notably the EGL context. Also releases the * Surface that was passed to our constructor. diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java index 583e6ade91..3d2c898935 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java @@ -50,11 +50,6 @@ public class EGLSurfaceDrawingThread { */ private DrawingThread mDrawingThread; - /** - * 描画範囲. - */ - private Rect mDrawingRange; - /** * イベントを通知するリスナーを追加します. * @@ -103,24 +98,6 @@ public int getTimeout() { return mTimeout; } - /** - * 描画範囲を設定します. - * - * @param rect 描画範囲 - */ - public void setDrawingRange(Rect rect) { - mDrawingRange = rect; - } - - /** - * 描画範囲設定を取得します. - * - * @return 描画範囲 - */ - public Rect getDrawingRange() { - return mDrawingRange; - } - /** * 描画を行う Surface のサイズを設定します. * @@ -196,7 +173,22 @@ public void addEGLSurfaceBase(EGLSurfaceBase eglSurfaceBase) { * @param surface 追加する EGLSurfaceBase に設定する Surface */ public void addEGLSurfaceBase(Surface surface) { - addEGLSurfaceBase(surface, surface); + addEGLSurfaceBase(surface, surface, null); + } + + /** + * 描画先の EGLSurfaceBase を追加します. + * + * 引数に設定した Surface の EGLSurfaceBase を作成します。 + * また、タグには、引数に指定された surface を設定します。 + * + * 作成した EGLSurfaceBase に引数で指定された drawingRange を描画範囲に設定します。 + * + * @param surface 追加する EGLSurfaceBase に設定する Surface + * @param drawingRange 描画範囲 + */ + public void addEGLSurfaceBase(Surface surface, Rect drawingRange) { + addEGLSurfaceBase(surface, surface, drawingRange); } /** @@ -206,9 +198,10 @@ public void addEGLSurfaceBase(Surface surface) { * * @param surface 追加する EGLSurfaceBase に設定する Surface */ - public void addEGLSurfaceBase(Surface surface, Object tag) { + public void addEGLSurfaceBase(Surface surface, Object tag, Rect drawingRange) { EGLSurfaceBase eglSurfaceBase = createEGLSurfaceBase(surface); eglSurfaceBase.setTag(tag); + eglSurfaceBase.setDrawingRange(drawingRange); addEGLSurfaceBase(eglSurfaceBase); } @@ -224,6 +217,22 @@ public void addEGLSurfaceBase(Surface surface, Object tag) { public void addEGLSurfaceBase(int width, int height, Object tag) { EGLSurfaceBase eglSurfaceBase = createEGLSurfaceBase(width, height); eglSurfaceBase.setTag(tag); + addEGLSurfaceBase(width, height, tag, null); + } + + /** + * 描画先の EGLSurfaceBase を追加します. + * + * 引数に指定された width と height でオフスクリーンの EGLSurfaceBase を作成します。 + * + * @param width 横幅 + * @param height 縦幅 + * @param tag タグ + */ + public void addEGLSurfaceBase(int width, int height, Object tag, Rect drawingRange) { + EGLSurfaceBase eglSurfaceBase = createEGLSurfaceBase(width, height); + eglSurfaceBase.setTag(tag); + eglSurfaceBase.setDrawingRange(drawingRange); addEGLSurfaceBase(eglSurfaceBase); } @@ -371,9 +380,6 @@ protected SurfaceTextureManager createStManager() { SurfaceTextureManager manager = new SurfaceTextureManager(); SurfaceTexture st = manager.getSurfaceTexture(); st.setDefaultBufferSize(mWidth, mHeight); - if (mDrawingRange != null) { - manager.setDrawingRange(mDrawingRange, mWidth, mHeight); - } return manager; } @@ -543,6 +549,12 @@ public void run() { synchronized (mEGLSurfaceBases) { for (EGLSurfaceBase eglSurfaceBase : mEGLSurfaceBases) { eglSurfaceBase.makeCurrent(); + Rect drawingRange = eglSurfaceBase.getDrawingRange(); + if (drawingRange != null) { + mStManager.setDrawingRange(drawingRange, mWidth, mHeight); + } else { + mStManager.clearDrawingRange(); + } mStManager.setViewport(0, 0, eglSurfaceBase.getWidth(), eglSurfaceBase.getHeight()); mStManager.drawImage(getDisplayRotation()); eglSurfaceBase.setPresentationTime(st.getTimestamp()); diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java index 3ce60e0855..c3c2000ab7 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java @@ -160,6 +160,13 @@ public void setDrawingRange(Rect rect, int width, int height) { mTextureRenderer.setDrawingRange(rect, width, height); } + /** + * 描画範囲を解除します. + */ + public void clearDrawingRange() { + mTextureRenderer.clearDrawingRange(); + } + /** * Draws the data from SurfaceTexture onto the current EGL surface. */ diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java index 2c97dab7d2..3570714694 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java @@ -220,6 +220,11 @@ public void drawFrame(SurfaceTexture st, int displayRotation) { GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); } + public void clearDrawingRange() { + mTriangleVertices.clear(); + mTriangleVertices.put(TRIANGLE_VERTICES_DATA).position(0); + } + public void setDrawingRange(Rect rect, int width, int height) { setDrawingRange(rect.left, rect.top, rect.right, rect.bottom, width, height); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/MJPEGQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/MJPEGQuality.java index ee6c546062..a5a21d3000 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/MJPEGQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/MJPEGQuality.java @@ -1,5 +1,6 @@ package org.deviceconnect.android.libmedia.streaming.mjpeg; +import android.graphics.Rect; import android.hardware.camera2.CameraCharacteristics; public class MJPEGQuality { @@ -8,6 +9,7 @@ public class MJPEGQuality { private int mHeight = 640; private int mQuality = 60; private int mFrameRate = 30; + private Rect mDrawingRange; /** * Motion JPEG の横幅を取得します. @@ -96,4 +98,12 @@ public int getFacing() { public void setFacing(int facing) { mFacing = facing; } + + public void setDrawingRange(Rect drawingRange) { + mDrawingRange = drawingRange; + } + + public Rect getDrawingRange() { + return mDrawingRange; + } } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java index 866dac64b7..bcdfcf9c25 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java @@ -1,6 +1,7 @@ package org.deviceconnect.android.libmedia.streaming.mjpeg; import android.graphics.Bitmap; +import android.graphics.Rect; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceBase; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; @@ -165,8 +166,6 @@ protected EGLSurfaceDrawingThread createEGLSurfaceDrawingThread() { */ private synchronized void startDrawingThreadInternal() { MJPEGQuality quality = getMJPEGQuality(); - int w = quality.getWidth(); - int h = quality.getHeight(); if (mEncoderThread != null) { mEncoderThread.terminate(); @@ -179,8 +178,16 @@ private synchronized void startDrawingThreadInternal() { if (mInternalCreateSurfaceDrawingThread) { mSurfaceDrawingThread = createEGLSurfaceDrawingThread(); } - mSurfaceDrawingThread.setSize(w, h); - mSurfaceDrawingThread.addEGLSurfaceBase(quality.getWidth(), quality.getHeight(), TAG_SURFACE); + + int w = quality.getWidth(); + int h = quality.getHeight(); + Rect rect = quality.getDrawingRange(); + if (rect != null) { + w = rect.width(); + h = rect.height(); + } + mSurfaceDrawingThread.setSize(quality.getWidth(), quality.getHeight()); + mSurfaceDrawingThread.addEGLSurfaceBase(w, h, TAG_SURFACE, quality.getDrawingRange()); mSurfaceDrawingThread.addOnDrawingEventListener(mOnDrawingEventListener); mSurfaceDrawingThread.start(); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java index 52c88cf742..171081491e 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java @@ -107,7 +107,7 @@ private void startDrawingThreadInternal() { } mSurfaceDrawingThread.setSize(quality.getVideoWidth(), quality.getVideoHeight()); - mSurfaceDrawingThread.addEGLSurfaceBase(mMediaCodecSurface); + mSurfaceDrawingThread.addEGLSurfaceBase(mMediaCodecSurface, quality.getDrawingRange()); mSurfaceDrawingThread.addOnDrawingEventListener(mOnDrawingEventListener); mSurfaceDrawingThread.start(); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java index 6248796024..c66d62e582 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java @@ -1,5 +1,6 @@ package org.deviceconnect.android.libmedia.streaming.video; +import android.graphics.Rect; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecList; @@ -36,6 +37,11 @@ protected void prepare() throws IOException { VideoQuality videoQuality = getVideoQuality(); int w = videoQuality.getVideoWidth(); int h = videoQuality.getVideoHeight(); + Rect drawingRange = videoQuality.getDrawingRange(); + if (drawingRange != null) { + w = drawingRange.width(); + h = drawingRange.height(); + } mMediaCodec = createMediaCodec(getColorFormat(), w, h); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java index e73cf33375..e33dc6e41e 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java @@ -1,5 +1,7 @@ package org.deviceconnect.android.libmedia.streaming.video; +import android.graphics.Rect; + public class VideoQuality { private static final int DEFAULT_FRAME_RATE = 30; private static final int DEFAULT_IFRAME_INTERVAL = 3; @@ -25,6 +27,12 @@ public class VideoQuality { */ private boolean mUseSoftwareEncoder; + /** + * 描画範囲. + * null の場合は範囲してい無し。 + */ + private Rect mDrawingRange; + /** * コンストラクタ. * @param mimeType エンコードのマイムタイプ @@ -50,6 +58,9 @@ public void set(VideoQuality quality) { mUseSoftwareEncoder = quality.isUseSoftwareEncoder(); mProfile = quality.getProfile(); mLevel = quality.getLevel(); + if (quality.mDrawingRange != null) { + mDrawingRange = new Rect(quality.mDrawingRange); + } } /** @@ -283,6 +294,14 @@ public enum BitRateMode { CQ } + public Rect getDrawingRange() { + return mDrawingRange; + } + + public void setDrawingRange(Rect drawingRange) { + mDrawingRange = drawingRange; + } + @Override public String toString() { return "VideoQuality{" + From 410aa4cdc362996f72d6686f3cc28f1de4d3e0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Thu, 19 Aug 2021 19:26:38 +0900 Subject: [PATCH 02/40] =?UTF-8?q?=E5=90=84=E3=83=9E=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=97=E3=81=94=E3=81=A8=E3=81=AB=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E4=BF=9D=E5=AD=98=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deviceplugin/host/HostDevicePlugin.java | 2 +- .../settings/SettingsBroadcastFragment.java | 411 ++++++- .../settings/SettingsMJPEGFragment.java | 292 +++++ .../settings/SettingsMainFragment.java | 4 + .../settings/SettingsRTSPFragment.java | 422 +++++++ .../settings/SettingsSRTFragment.java | 411 ++++++- .../settings/SettingsVideoFragment.java | 255 ---- .../host/recorder/AbstractBroadcaster.java | 40 +- .../recorder/AbstractMJPEGPreviewServer.java | 14 +- .../host/recorder/AbstractPreviewServer.java | 23 +- .../recorder/AbstractSRTPreviewServer.java | 1 - .../host/recorder/HostMediaRecorder.java | 1054 +++++++++++------ .../camera/Camera2MJPEGPreviewServer.java | 4 +- .../camera/Camera2PreviewServerProvider.java | 10 +- .../camera/Camera2RTSPPreviewServer.java | 6 +- .../camera/Camera2SRTPreviewServer.java | 6 +- .../recorder/camera/CameraMJPEGEncoder.java | 19 - .../camera/CameraSurfaceDrawingThread.java | 61 +- .../recorder/camera/CameraVideoEncoder.java | 10 - .../host/recorder/ui/PreviewSurfaceView.java | 152 ++- .../app/src/main/res/drawable/border_red.xml | 9 + .../res/layout/host_preview_surface_view.xml | 9 + .../navigation_recorder_settings.xml | 28 + .../app/src/main/res/values-ja/strings.xml | 6 +- .../app/src/main/res/values/strings.xml | 4 + .../xml/settings_host_recorder_broadcast.xml | 99 ++ .../res/xml/settings_host_recorder_main.xml | 20 +- .../res/xml/settings_host_recorder_mjpeg.xml | 83 ++ .../res/xml/settings_host_recorder_rtsp.xml | 125 ++ .../res/xml/settings_host_recorder_srt.xml | 230 +++- .../res/xml/settings_host_recorder_video.xml | 100 -- .../app/src/main/AndroidManifest.xml | 15 +- .../gles/EGLSurfaceDrawingThread.java | 1 + .../streaming/video/VideoQuality.java | 14 + 34 files changed, 3029 insertions(+), 911 deletions(-) create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/drawable/border_red.xml create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/HostDevicePlugin.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/HostDevicePlugin.java index 6e7f354fdd..1347983a87 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/HostDevicePlugin.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/HostDevicePlugin.java @@ -311,7 +311,7 @@ protected void onKeyStoreUpdated(final KeyStore keyStore, final Certificate cert } mSSLContext = createSSLContext(keyStore, DEFAULT_PASSWORD); } catch (Exception e) { - mLogger.log(Level.SEVERE, "Failed to update keystore", e); + mLogger.log(Level.WARNING, "Failed to update keystore", e); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java index 866c50afe1..2f1f187fa6 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java @@ -1,15 +1,422 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; import android.os.Bundle; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import org.deviceconnect.android.deviceplugin.host.R; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; +import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class SettingsBroadcastFragment extends SettingsParameterFragment { + /** + * マイムタイプを定義します. + */ + private static final String MIME_TYPE = "video/x-rtmp"; -public class SettingsBroadcastFragment extends SettingsBaseFragment { + private HostMediaRecorder mMediaRecorder; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId()); + getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-rtmp"); setPreferencesFromResource(R.xml.settings_host_recorder_broadcast, rootKey); } + @Override + public void onBindService() { + mMediaRecorder = getRecorder(); + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + setPreviewServerPort(); + setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewSizePreference(settings); + setPreviewVideoEncoderPreference(settings); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(MIME_TYPE), false); + + setPreviewCutOutReset(); + + setInputTypeNumber("preview_framerate"); + setInputTypeNumber("preview_bitrate"); + setInputTypeNumber("preview_i_frame_interval"); + setInputTypeNumber("preview_intra_refresh"); + + setPreviewClipPreference("preview_clip_left"); + setPreviewClipPreference("preview_clip_top"); + setPreviewClipPreference("preview_clip_right"); + setPreviewClipPreference("preview_clip_bottom"); + } + + private void setPreviewServerPort() { + setInputTypeNumber("port"); + EditTextPreference pref = findPreference("port"); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + } + } + + private void setPreviewServerUrl(int port) { + EditTextPreference pref = findPreference("url"); + if (pref != null) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + pref.setText("rtsp://" + ipAddress + ":" + port); + } + } + + /** + * 切り抜き範囲の設定にリスナーを設定します. + * + * @param key キー + */ + public void setPreviewClipPreference(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + setInputTypeNumber(key); + } + } + + private void setEmptyText(String key) { + EditTextPreference left = findPreference(key); + if (left != null) { + left.setText(null); + } + } + + /** + * 切り抜き範囲のリセットボタンのリスナーを設定します. + */ + private void setPreviewCutOutReset() { + PreferenceScreen pref = findPreference("preview_clip_reset"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + setEmptyText("preview_clip_left"); + setEmptyText("preview_clip_top"); + setEmptyText("preview_clip_right"); + setEmptyText("preview_clip_bottom"); + mMediaRecorder.getSettings().setDrawingRange(null); + return false; + }); + } + } + + /** + * プレビューの解像度 Preference を作成します. + * + * @param settings レコーダの設定 + */ + private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("camera_preview_size"); + if (pref != null) { + List previewSizes = getSupportedPreviewSizes(settings); + if (!previewSizes.isEmpty()) { + List entryValues = new ArrayList<>(); + for (Size preview : previewSizes) { + entryValues.add(getValueFromSize(preview)); + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + Size previewSize = settings.getPreviewSize(MIME_TYPE); + if (previewSize != null) { + pref.setValue(getValueFromSize(previewSize)); + } + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダの設定を行います. + * + * @param settings レコーダ設定 + */ + private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("preview_encoder"); + if (pref != null) { + List list = settings.getSupportedVideoEncoders(); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(list); + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダのプロファイルとレベルを設定します. + * + * @param settings レコーダ設定 + * @param encoderName エンコーダ + * @param reset リセットフラグ + */ + private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + ListPreference pref = findPreference("preview_profile_level"); + if (pref != null) { + List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(); + entryValues.add("none"); + + for (HostMediaRecorder.ProfileLevel pl : list) { + String value = getProfileLevel(encoderName, pl); + if (value != null) { + entryValues.add(value); + } + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + if (reset) { + pref.setValue("none"); + } else { + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(MIME_TYPE); + if (pl != null) { + pref.setValue(getProfileLevel(encoderName, pl)); + } + } + + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * サイズの小さい方からソートを行うための比較演算子. + */ + private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + }; + + /** + * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. + * + * @param settings レコーダ + * @return サポートしているプレビューサイズのリスト + */ + @NonNull + private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + List previewSizes = new ArrayList<>(); + if (settings != null) { + previewSizes.addAll(settings.getSupportedPreviewSizes()); + Collections.sort(previewSizes, SIZE_COMPARATOR); + } + return previewSizes; + } + + /** + * プレビューのサイズを文字列に変換します. + * + * @param previewSize プレビューサイズ + * @return 文字列 + */ + private String getValueFromSize(Size previewSize) { + return previewSize.getWidth() + " x " + previewSize.getHeight(); + } + + /** + * 文字列を Size に変換します. + * + * Size に変換できなかった場合には null を返却します。 + * + * @param value 文字列のサイズ + * @return サイズ + */ + private Size getSizeFromValue(String value) { + String[] t = value.split("x"); + if (t.length == 2) { + try { + int w = Integer.parseInt(t[0].trim()); + int h = Integer.parseInt(t[1].trim()); + return new Size(w, h); + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * プロファイルとレベルを文字列に変換します. + * + * @param encoderName エンコーダ + * @param pl プロファイルとレベル + * @return 文字列 + */ + private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.valueOf(pl.getProfile()); + H264Level l = H264Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + case H265: { + H265Profile p = H265Profile.valueOf(pl.getProfile()); + H265Level l = H265Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + } + return null; + } + + /** + * 文字列をプロファイルとレベルに変換します. + * + * プロファイルとレベルに変換できなかった場合には、null を返却します。 + * + * @param encoderName エンコーダ + * @param value 変換する文字列 + * @return プロファイルとレベル + */ + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { + String[] t = value.split("-"); + if (t.length == 2) { + try { + String profile = t[0].trim(); + String level = t[1].trim(); + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.nameOf(profile); + H264Level l = H264Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + case H265: { + H265Profile p = H265Profile.nameOf(profile); + H265Level l = H265Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + } + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * 切り抜き範囲の値を取得します. + * + * 未設定の場合には null を返却します。 + * + * @param key キー + * @return 切り抜き範囲 + */ + private Integer getDrawingRange(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + try { + return Integer.parseInt(pref.getText()); + } catch (NumberFormatException e) { + // ignore. + } + } + return null; + } + + /** + * 設定が変更された時に呼び出されるリスナー. + */ + private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { + if (mMediaRecorder == null) { + return false; + } + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + String key = preference.getKey(); + if ("camera_preview_size".equals(key)) { + Size size = getSizeFromValue((String) newValue); + if (size != null) { + settings.setPreviewSize(MIME_TYPE, size); + } + } else if ("port".equalsIgnoreCase(key)) { + setPreviewServerUrl(Integer.parseInt((String) newValue)); + } else if ("preview_encoder".equals(key)) { + // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく + settings.setProfileLevel(MIME_TYPE, null); + HostMediaRecorder.VideoEncoderName encoderName = + HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); + setPreviewProfileLevelPreference(settings, encoderName, true); + } else if ("preview_profile_level".equalsIgnoreCase(key)) { + settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } else if ("preview_clip_left".equalsIgnoreCase(key)) { + try { + int clipLeft = Integer.parseInt((String) newValue); + Integer clipRight = getDrawingRange("preview_clip_right"); + if (clipRight != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_top".equalsIgnoreCase(key)) { + try { + int clipTop = Integer.parseInt((String) newValue); + Integer clipBottom = getDrawingRange("preview_clip_bottom"); + if (clipBottom != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_right".equalsIgnoreCase(key)) { + try { + int clipRight = Integer.parseInt((String) newValue); + Integer clipLeft = getDrawingRange("preview_clip_left"); + if (clipLeft != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { + try { + int clipBottom = Integer.parseInt((String) newValue); + Integer clipTop = getDrawingRange("preview_clip_top"); + if (clipTop != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } + return true; + }; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java new file mode 100644 index 0000000000..cec5e4a22c --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -0,0 +1,292 @@ +package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; + +import android.os.Bundle; +import android.util.Log; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import org.deviceconnect.android.deviceplugin.host.R; +import org.deviceconnect.android.deviceplugin.host.activity.fragment.SeekBarDialogPreference; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class SettingsMJPEGFragment extends SettingsParameterFragment { + + private static final String MIME_TYPE = "video/x-mjpeg"; + + private HostMediaRecorder mMediaRecorder; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-mjpeg"); + setPreferencesFromResource(R.xml.settings_host_recorder_mjpeg, rootKey); + } + + @Override + public void onBindService() { + mMediaRecorder = getRecorder(); + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + setPreviewServerPort(); + setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewSizePreference(settings); + + setPreviewJpegQuality(); + setPreviewCutOutReset(); + + setPreviewClipPreference("preview_clip_left"); + setPreviewClipPreference("preview_clip_top"); + setPreviewClipPreference("preview_clip_right"); + setPreviewClipPreference("preview_clip_bottom"); + } + + private void setPreviewServerPort() { + setInputTypeNumber("port"); + EditTextPreference pref = findPreference("port"); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + } + } + + private void setPreviewServerUrl(int port) { + EditTextPreference pref = findPreference("url"); + if (pref != null) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + pref.setText("http://" + ipAddress + ":" + port + "/mjpeg"); + } + } + + /** + * JPEG クオリティを設定します. + */ + private void setPreviewJpegQuality() { + SeekBarDialogPreference pref = findPreference("preview_jpeg_quality"); + if (pref != null) { + pref.setMinValue(0); + pref.setMaxValue(100); + pref.setEnabled(true); + } + } + + /** + * 切り抜き範囲の設定にリスナーを設定します. + * + * @param key キー + */ + public void setPreviewClipPreference(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + setInputTypeNumber(key); + } + } + + /** + * 切り抜き範囲のリセットボタンのリスナーを設定します. + */ + private void setPreviewCutOutReset() { + PreferenceScreen pref = findPreference("preview_clip_reset"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + EditTextPreference left = findPreference("preview_clip_left"); + if (left != null) { + left.setText(null); + } + EditTextPreference top = findPreference("preview_clip_top"); + if (top != null) { + top.setText(null); + } + EditTextPreference right = findPreference("preview_clip_right"); + if (right != null) { + right.setText(null); + } + EditTextPreference bottom = findPreference("preview_clip_bottom"); + if (bottom != null) { + bottom.setText(null); + } + mMediaRecorder.getSettings().setDrawingRange(null); + return false; + }); + } + } + + /** + * プレビューの解像度 Preference を作成します. + * + * @param settings レコーダの設定 + */ + private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("camera_preview_size"); + if (pref != null) { + List previewSizes = getSupportedPreviewSizes(settings); + if (!previewSizes.isEmpty()) { + List entryValues = new ArrayList<>(); + for (Size preview : previewSizes) { + entryValues.add(getValueFromSize(preview)); + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + Size previewSize = settings.getPreviewSize(MIME_TYPE); + if (previewSize != null) { + pref.setValue(getValueFromSize(previewSize)); + } + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * サイズの小さい方からソートを行うための比較演算子. + */ + private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + }; + + /** + * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. + * + * @param settings レコーダ + * @return サポートしているプレビューサイズのリスト + */ + @NonNull + private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + List previewSizes = new ArrayList<>(); + if (settings != null) { + previewSizes.addAll(settings.getSupportedPreviewSizes()); + Collections.sort(previewSizes, SIZE_COMPARATOR); + } + return previewSizes; + } + + /** + * プレビューのサイズを文字列に変換します. + * + * @param previewSize プレビューサイズ + * @return 文字列 + */ + private String getValueFromSize(Size previewSize) { + return previewSize.getWidth() + " x " + previewSize.getHeight(); + } + + /** + * 文字列を Size に変換します. + * + * Size に変換できなかった場合には null を返却します。 + * + * @param value 文字列のサイズ + * @return サイズ + */ + private Size getSizeFromValue(String value) { + String[] t = value.split("x"); + if (t.length == 2) { + try { + int w = Integer.parseInt(t[0].trim()); + int h = Integer.parseInt(t[1].trim()); + return new Size(w, h); + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * 切り抜き範囲の値を取得します. + * + * 未設定の場合には null を返却します。 + * + * @param key キー + * @return 切り抜き範囲 + */ + private Integer getDrawingRange(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + try { + return Integer.parseInt(pref.getText()); + } catch (NumberFormatException e) { + // ignore. + } + } + return null; + } + + /** + * 設定が変更された時に呼び出されるリスナー. + */ + private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { + if (mMediaRecorder == null) { + return false; + } + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + String key = preference.getKey(); + if ("camera_preview_size".equals(key)) { + Size size = getSizeFromValue((String) newValue); + if (size != null) { + settings.setPreviewSize(MIME_TYPE, size); + } + } else if ("port".equalsIgnoreCase(key)) { + setPreviewServerUrl(Integer.parseInt((String) newValue)); + } else if ("preview_clip_left".equalsIgnoreCase(key)) { + try { + int clipLeft = Integer.parseInt((String) newValue); + Integer clipRight = getDrawingRange("preview_clip_right"); + if (clipRight != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_top".equalsIgnoreCase(key)) { + try { + int clipTop = Integer.parseInt((String) newValue); + Integer clipBottom = getDrawingRange("preview_clip_bottom"); + if (clipBottom != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_right".equalsIgnoreCase(key)) { + try { + int clipRight = Integer.parseInt((String) newValue); + Integer clipLeft = getDrawingRange("preview_clip_left"); + if (clipLeft != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { + try { + int clipBottom = Integer.parseInt((String) newValue); + Integer clipTop = getDrawingRange("preview_clip_top"); + if (clipTop != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } + return true; + }; +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java index 467f66d6df..96183a5a2f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java @@ -26,6 +26,10 @@ public boolean onPreferenceTreeClick(final Preference preference) { findNavController(this).navigate(R.id.action_main_to_video); } else if ("recorder_settings_audio".equals(preference.getKey())) { findNavController(this).navigate(R.id.action_main_to_audio); + } else if ("recorder_settings_mjpeg".equals(preference.getKey())) { + findNavController(this).navigate(R.id.action_main_to_mjpeg); + } else if ("recorder_settings_rtsp".equals(preference.getKey())) { + findNavController(this).navigate(R.id.action_main_to_rtsp); } else if ("recorder_settings_srt".equals(preference.getKey())) { findNavController(this).navigate(R.id.action_main_to_srt); } else if ("recorder_settings_broadcast".equals(preference.getKey())) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java new file mode 100644 index 0000000000..222d5678c6 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -0,0 +1,422 @@ +package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; + +import android.os.Bundle; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import org.deviceconnect.android.deviceplugin.host.R; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; +import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class SettingsRTSPFragment extends SettingsParameterFragment { + /** + * マイムタイプを定義します. + */ + private static final String MIME_TYPE = "video/x-rtp"; + + private HostMediaRecorder mMediaRecorder; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-rtsp"); + setPreferencesFromResource(R.xml.settings_host_recorder_rtsp, rootKey); + } + + @Override + public void onBindService() { + mMediaRecorder = getRecorder(); + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + setPreviewServerPort(); + setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewSizePreference(settings); + setPreviewVideoEncoderPreference(settings); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(MIME_TYPE), false); + + setPreviewCutOutReset(); + + setInputTypeNumber("preview_framerate"); + setInputTypeNumber("preview_bitrate"); + setInputTypeNumber("preview_i_frame_interval"); + setInputTypeNumber("preview_intra_refresh"); + + setPreviewClipPreference("preview_clip_left"); + setPreviewClipPreference("preview_clip_top"); + setPreviewClipPreference("preview_clip_right"); + setPreviewClipPreference("preview_clip_bottom"); + } + + private void setPreviewServerPort() { + setInputTypeNumber("port"); + EditTextPreference pref = findPreference("port"); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + } + } + + private void setPreviewServerUrl(int port) { + EditTextPreference pref = findPreference("url"); + if (pref != null) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + pref.setText("rtsp://" + ipAddress + ":" + port); + } + } + + /** + * 切り抜き範囲の設定にリスナーを設定します. + * + * @param key キー + */ + public void setPreviewClipPreference(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + setInputTypeNumber(key); + } + } + + private void setEmptyText(String key) { + EditTextPreference left = findPreference(key); + if (left != null) { + left.setText(null); + } + } + + /** + * 切り抜き範囲のリセットボタンのリスナーを設定します. + */ + private void setPreviewCutOutReset() { + PreferenceScreen pref = findPreference("preview_clip_reset"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + setEmptyText("preview_clip_left"); + setEmptyText("preview_clip_top"); + setEmptyText("preview_clip_right"); + setEmptyText("preview_clip_bottom"); + mMediaRecorder.getSettings().setDrawingRange(null); + return false; + }); + } + } + + /** + * プレビューの解像度 Preference を作成します. + * + * @param settings レコーダの設定 + */ + private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("camera_preview_size"); + if (pref != null) { + List previewSizes = getSupportedPreviewSizes(settings); + if (!previewSizes.isEmpty()) { + List entryValues = new ArrayList<>(); + for (Size preview : previewSizes) { + entryValues.add(getValueFromSize(preview)); + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + Size previewSize = settings.getPreviewSize(MIME_TYPE); + if (previewSize != null) { + pref.setValue(getValueFromSize(previewSize)); + } + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダの設定を行います. + * + * @param settings レコーダ設定 + */ + private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("preview_encoder"); + if (pref != null) { + List list = settings.getSupportedVideoEncoders(); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(list); + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダのプロファイルとレベルを設定します. + * + * @param settings レコーダ設定 + * @param encoderName エンコーダ + * @param reset リセットフラグ + */ + private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + ListPreference pref = findPreference("preview_profile_level"); + if (pref != null) { + List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(); + entryValues.add("none"); + + for (HostMediaRecorder.ProfileLevel pl : list) { + String value = getProfileLevel(encoderName, pl); + if (value != null) { + entryValues.add(value); + } + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + if (reset) { + pref.setValue("none"); + } else { + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(MIME_TYPE); + if (pl != null) { + pref.setValue(getProfileLevel(encoderName, pl)); + } + } + + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * サイズの小さい方からソートを行うための比較演算子. + */ + private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + }; + + /** + * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. + * + * @param settings レコーダ + * @return サポートしているプレビューサイズのリスト + */ + @NonNull + private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + List previewSizes = new ArrayList<>(); + if (settings != null) { + previewSizes.addAll(settings.getSupportedPreviewSizes()); + Collections.sort(previewSizes, SIZE_COMPARATOR); + } + return previewSizes; + } + + /** + * プレビューのサイズを文字列に変換します. + * + * @param previewSize プレビューサイズ + * @return 文字列 + */ + private String getValueFromSize(Size previewSize) { + return previewSize.getWidth() + " x " + previewSize.getHeight(); + } + + /** + * 文字列を Size に変換します. + * + * Size に変換できなかった場合には null を返却します。 + * + * @param value 文字列のサイズ + * @return サイズ + */ + private Size getSizeFromValue(String value) { + String[] t = value.split("x"); + if (t.length == 2) { + try { + int w = Integer.parseInt(t[0].trim()); + int h = Integer.parseInt(t[1].trim()); + return new Size(w, h); + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * プロファイルとレベルを文字列に変換します. + * + * @param encoderName エンコーダ + * @param pl プロファイルとレベル + * @return 文字列 + */ + private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.valueOf(pl.getProfile()); + H264Level l = H264Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + case H265: { + H265Profile p = H265Profile.valueOf(pl.getProfile()); + H265Level l = H265Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + } + return null; + } + + /** + * 文字列をプロファイルとレベルに変換します. + * + * プロファイルとレベルに変換できなかった場合には、null を返却します。 + * + * @param encoderName エンコーダ + * @param value 変換する文字列 + * @return プロファイルとレベル + */ + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { + String[] t = value.split("-"); + if (t.length == 2) { + try { + String profile = t[0].trim(); + String level = t[1].trim(); + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.nameOf(profile); + H264Level l = H264Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + case H265: { + H265Profile p = H265Profile.nameOf(profile); + H265Level l = H265Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + } + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * 切り抜き範囲の値を取得します. + * + * 未設定の場合には null を返却します。 + * + * @param key キー + * @return 切り抜き範囲 + */ + private Integer getDrawingRange(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + try { + return Integer.parseInt(pref.getText()); + } catch (NumberFormatException e) { + // ignore. + } + } + return null; + } + + /** + * 設定が変更された時に呼び出されるリスナー. + */ + private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { + if (mMediaRecorder == null) { + return false; + } + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + String key = preference.getKey(); + if ("camera_preview_size".equals(key)) { + Size size = getSizeFromValue((String) newValue); + if (size != null) { + settings.setPreviewSize(MIME_TYPE, size); + } + } else if ("port".equalsIgnoreCase(key)) { + setPreviewServerUrl(Integer.parseInt((String) newValue)); + } else if ("preview_encoder".equals(key)) { + // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく + settings.setProfileLevel(MIME_TYPE, null); + HostMediaRecorder.VideoEncoderName encoderName = + HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); + setPreviewProfileLevelPreference(settings, encoderName, true); + } else if ("preview_profile_level".equalsIgnoreCase(key)) { + settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } else if ("preview_clip_left".equalsIgnoreCase(key)) { + try { + int clipLeft = Integer.parseInt((String) newValue); + Integer clipRight = getDrawingRange("preview_clip_right"); + if (clipRight != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_top".equalsIgnoreCase(key)) { + try { + int clipTop = Integer.parseInt((String) newValue); + Integer clipBottom = getDrawingRange("preview_clip_bottom"); + if (clipBottom != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_right".equalsIgnoreCase(key)) { + try { + int clipRight = Integer.parseInt((String) newValue); + Integer clipLeft = getDrawingRange("preview_clip_left"); + if (clipLeft != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { + try { + int clipBottom = Integer.parseInt((String) newValue); + Integer clipTop = getDrawingRange("preview_clip_top"); + if (clipTop != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } + return true; + }; +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index 54a81a4dc8..fb7dfea954 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -2,17 +2,39 @@ import android.os.Bundle; import android.text.TextUtils; +import android.util.Size; +import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import org.deviceconnect.android.deviceplugin.host.R; -import org.deviceconnect.android.deviceplugin.host.recorder.util.SRTSettings; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; +import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class SettingsSRTFragment extends SettingsParameterFragment { + /** + * マイムタイプを定義します. + */ + private static final String MIME_TYPE = "video/MP2T"; + + private HostMediaRecorder mMediaRecorder; -public class SettingsSRTFragment extends SettingsBaseFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(SRTSettings.FILE_NAME); + getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-srt"); setPreferencesFromResource(R.xml.settings_host_recorder_srt, rootKey); setSummaryOptionAuto(getString(R.string.pref_key_settings_srt_inputbw)); @@ -27,6 +49,31 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setInputTypeNumber(getString(R.string.pref_key_settings_srt_packetfilter)); } + @Override + public void onBindService() { + mMediaRecorder = getRecorder(); + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + setPreviewServerPort(); + setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewSizePreference(settings); + setPreviewVideoEncoderPreference(settings); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(MIME_TYPE), false); + + setPreviewCutOutReset(); + + setInputTypeNumber("preview_framerate"); + setInputTypeNumber("preview_bitrate"); + setInputTypeNumber("preview_i_frame_interval"); + setInputTypeNumber("preview_intra_refresh"); + + setPreviewClipPreference("preview_clip_left"); + setPreviewClipPreference("preview_clip_top"); + setPreviewClipPreference("preview_clip_right"); + setPreviewClipPreference("preview_clip_bottom"); + } + private void setSummaryOptionAuto(String name) { EditTextPreference inputBwPref = findPreference(name); if (inputBwPref != null) { @@ -41,4 +88,362 @@ private void setSummaryOptionAuto(String name) { } return value; }; + + private void setPreviewServerPort() { + setInputTypeNumber("port"); + EditTextPreference pref = findPreference("port"); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + } + } + + private void setPreviewServerUrl(int port) { + EditTextPreference pref = findPreference("url"); + if (pref != null) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + pref.setText("srt://" + ipAddress + ":" + port); + } + } + + /** + * 切り抜き範囲の設定にリスナーを設定します. + * + * @param key キー + */ + public void setPreviewClipPreference(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + setInputTypeNumber(key); + } + } + + private void setEmptyText(String key) { + EditTextPreference left = findPreference(key); + if (left != null) { + left.setText(null); + } + } + + /** + * 切り抜き範囲のリセットボタンのリスナーを設定します. + */ + private void setPreviewCutOutReset() { + PreferenceScreen pref = findPreference("preview_clip_reset"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + setEmptyText("preview_clip_left"); + setEmptyText("preview_clip_top"); + setEmptyText("preview_clip_right"); + setEmptyText("preview_clip_bottom"); + mMediaRecorder.getSettings().setDrawingRange(null); + return false; + }); + } + } + + /** + * プレビューの解像度 Preference を作成します. + * + * @param settings レコーダの設定 + */ + private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("camera_preview_size"); + if (pref != null) { + List previewSizes = getSupportedPreviewSizes(settings); + if (!previewSizes.isEmpty()) { + List entryValues = new ArrayList<>(); + for (Size preview : previewSizes) { + entryValues.add(getValueFromSize(preview)); + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + Size previewSize = settings.getPreviewSize(MIME_TYPE); + if (previewSize != null) { + pref.setValue(getValueFromSize(previewSize)); + } + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダの設定を行います. + * + * @param settings レコーダ設定 + */ + private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("preview_encoder"); + if (pref != null) { + List list = settings.getSupportedVideoEncoders(); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(list); + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダのプロファイルとレベルを設定します. + * + * @param settings レコーダ設定 + * @param encoderName エンコーダ + * @param reset リセットフラグ + */ + private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + ListPreference pref = findPreference("preview_profile_level"); + if (pref != null) { + List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(); + entryValues.add("none"); + + for (HostMediaRecorder.ProfileLevel pl : list) { + String value = getProfileLevel(encoderName, pl); + if (value != null) { + entryValues.add(value); + } + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + if (reset) { + pref.setValue("none"); + } else { + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(MIME_TYPE); + if (pl != null) { + pref.setValue(getProfileLevel(encoderName, pl)); + } + } + + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * サイズの小さい方からソートを行うための比較演算子. + */ + private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + }; + + /** + * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. + * + * @param settings レコーダ + * @return サポートしているプレビューサイズのリスト + */ + @NonNull + private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + List previewSizes = new ArrayList<>(); + if (settings != null) { + previewSizes.addAll(settings.getSupportedPreviewSizes()); + Collections.sort(previewSizes, SIZE_COMPARATOR); + } + return previewSizes; + } + + /** + * プレビューのサイズを文字列に変換します. + * + * @param previewSize プレビューサイズ + * @return 文字列 + */ + private String getValueFromSize(Size previewSize) { + return previewSize.getWidth() + " x " + previewSize.getHeight(); + } + + /** + * 文字列を Size に変換します. + * + * Size に変換できなかった場合には null を返却します。 + * + * @param value 文字列のサイズ + * @return サイズ + */ + private Size getSizeFromValue(String value) { + String[] t = value.split("x"); + if (t.length == 2) { + try { + int w = Integer.parseInt(t[0].trim()); + int h = Integer.parseInt(t[1].trim()); + return new Size(w, h); + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * プロファイルとレベルを文字列に変換します. + * + * @param encoderName エンコーダ + * @param pl プロファイルとレベル + * @return 文字列 + */ + private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.valueOf(pl.getProfile()); + H264Level l = H264Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + case H265: { + H265Profile p = H265Profile.valueOf(pl.getProfile()); + H265Level l = H265Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + } + return null; + } + + /** + * 文字列をプロファイルとレベルに変換します. + * + * プロファイルとレベルに変換できなかった場合には、null を返却します。 + * + * @param encoderName エンコーダ + * @param value 変換する文字列 + * @return プロファイルとレベル + */ + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { + String[] t = value.split("-"); + if (t.length == 2) { + try { + String profile = t[0].trim(); + String level = t[1].trim(); + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.nameOf(profile); + H264Level l = H264Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + case H265: { + H265Profile p = H265Profile.nameOf(profile); + H265Level l = H265Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + } + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * 切り抜き範囲の値を取得します. + * + * 未設定の場合には null を返却します。 + * + * @param key キー + * @return 切り抜き範囲 + */ + private Integer getDrawingRange(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + try { + return Integer.parseInt(pref.getText()); + } catch (NumberFormatException e) { + // ignore. + } + } + return null; + } + + /** + * 設定が変更された時に呼び出されるリスナー. + */ + private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { + if (mMediaRecorder == null) { + return false; + } + + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + + String key = preference.getKey(); + if ("camera_preview_size".equals(key)) { + Size size = getSizeFromValue((String) newValue); + if (size != null) { + settings.setPreviewSize(MIME_TYPE, size); + } + } else if ("port".equalsIgnoreCase(key)) { + setPreviewServerUrl(Integer.parseInt((String) newValue)); + } else if ("preview_encoder".equals(key)) { + // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく + settings.setProfileLevel(MIME_TYPE, null); + HostMediaRecorder.VideoEncoderName encoderName = + HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); + setPreviewProfileLevelPreference(settings, encoderName, true); + } else if ("preview_profile_level".equalsIgnoreCase(key)) { + settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } else if ("preview_clip_left".equalsIgnoreCase(key)) { + try { + int clipLeft = Integer.parseInt((String) newValue); + Integer clipRight = getDrawingRange("preview_clip_right"); + if (clipRight != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_top".equalsIgnoreCase(key)) { + try { + int clipTop = Integer.parseInt((String) newValue); + Integer clipBottom = getDrawingRange("preview_clip_bottom"); + if (clipBottom != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_right".equalsIgnoreCase(key)) { + try { + int clipRight = Integer.parseInt((String) newValue); + Integer clipLeft = getDrawingRange("preview_clip_left"); + if (clipLeft != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { + try { + int clipBottom = Integer.parseInt((String) newValue); + Integer clipTop = getDrawingRange("preview_clip_top"); + if (clipTop != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } + return true; + }; } \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java index 5ae82f2e26..32e80fce43 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java @@ -5,25 +5,18 @@ import android.util.Size; import androidx.annotation.NonNull; -import androidx.preference.EditTextPreference; import androidx.preference.ListPreference; import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; import org.deviceconnect.android.deviceplugin.host.R; import org.deviceconnect.android.deviceplugin.host.activity.fragment.SeekBarDialogPreference; import org.deviceconnect.android.deviceplugin.host.profile.utils.AutoExposure; import org.deviceconnect.android.deviceplugin.host.profile.utils.AutoFocus; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; import org.deviceconnect.android.deviceplugin.host.profile.utils.NoiseReduction; import org.deviceconnect.android.deviceplugin.host.profile.utils.OpticalStabilization; import org.deviceconnect.android.deviceplugin.host.profile.utils.Stabilization; import org.deviceconnect.android.deviceplugin.host.profile.utils.WhiteBalance; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; -import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import java.util.ArrayList; import java.util.Collections; @@ -47,8 +40,6 @@ public void onBindService() { setPictureSizePreference(settings); setPreviewSizePreference(settings); - setPreviewVideoEncoderPreference(settings); - setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); setPreviewAutoFocusPreference(settings); setPreviewWhiteBalancePreference(settings); setPreviewWhiteBalanceTemperaturePreference(settings); @@ -61,52 +52,10 @@ public void onBindService() { setPreviewNoiseReduction(settings); setPreviewFocalLength(settings); - setPreviewJpegQuality(); - setPreviewCutOutReset(); - setInputTypeNumber("preview_framerate"); setInputTypeNumber("preview_bitrate"); setInputTypeNumber("preview_i_frame_interval"); setInputTypeNumber("preview_intra_refresh"); - - setPreviewClipPreference("preview_clip_left"); - setPreviewClipPreference("preview_clip_top"); - setPreviewClipPreference("preview_clip_right"); - setPreviewClipPreference("preview_clip_bottom"); - } - - /** - * 切り抜き範囲の設定にリスナーを設定します. - * - * @param key キー - */ - public void setPreviewClipPreference(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - setInputTypeNumber(key); - } - } - - /** - * 切り抜き範囲のリセットボタンのリスナーを設定します. - */ - private void setPreviewCutOutReset() { - PreferenceScreen pref = findPreference("preview_clip_reset"); - if (pref != null) { - pref.setOnPreferenceClickListener(preference -> { - EditTextPreference left = findPreference("preview_clip_left"); - left.setText(null); - EditTextPreference top = findPreference("preview_clip_top"); - top.setText(null); - EditTextPreference right = findPreference("preview_clip_right"); - right.setText(null); - EditTextPreference bottom = findPreference("preview_clip_bottom"); - bottom.setText(null); - mMediaRecorder.getSettings().setDrawingRange(null); - return false; - }); - } } /** @@ -168,69 +117,6 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { } } - /** - * エンコーダの設定を行います. - * - * @param settings レコーダ設定 - */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { - ListPreference pref = findPreference("preview_encoder"); - if (pref != null) { - List list = settings.getSupportedVideoEncoders(); - if (!list.isEmpty()) { - List entryValues = new ArrayList<>(list); - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * エンコーダのプロファイルとレベルを設定します. - * - * @param settings レコーダ設定 - * @param encoderName エンコーダ - * @param reset リセットフラグ - */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { - ListPreference pref = findPreference("preview_profile_level"); - if (pref != null) { - List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); - if (!list.isEmpty()) { - List entryValues = new ArrayList<>(); - entryValues.add("none"); - - for (HostMediaRecorder.ProfileLevel pl : list) { - String value = getProfileLevel(encoderName, pl); - if (value != null) { - entryValues.add(value); - } - } - - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - - if (reset) { - pref.setValue("none"); - } else { - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); - if (pl != null) { - pref.setValue(getProfileLevel(encoderName, pl)); - } - } - - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - /** * 自動フォーカスモードの設定を行います. * @@ -521,18 +407,6 @@ private void setPreviewFocalLength(HostMediaRecorder.Settings settings) { } } - /** - * JPEG クオリティを設定します. - */ - private void setPreviewJpegQuality() { - SeekBarDialogPreference pref = findPreference("preview_jpeg_quality"); - if (pref != null) { - pref.setMinValue(0); - pref.setMaxValue(100); - pref.setEnabled(true); - } - } - /** * サイズの小さい方からソートを行うための比較演算子. */ @@ -606,87 +480,6 @@ private Size getSizeFromValue(String value) { return null; } - /** - * プロファイルとレベルを文字列に変換します. - * - * @param encoderName エンコーダ - * @param pl プロファイルとレベル - * @return 文字列 - */ - private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { - switch (encoderName) { - case H264: { - H264Profile p = H264Profile.valueOf(pl.getProfile()); - H264Level l = H264Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - return p.getName() + " - " + l.getName(); - } - } - case H265: { - H265Profile p = H265Profile.valueOf(pl.getProfile()); - H265Level l = H265Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - return p.getName() + " - " + l.getName(); - } - } - } - return null; - } - - /** - * 文字列をプロファイルとレベルに変換します. - * - * プロファイルとレベルに変換できなかった場合には、null を返却します。 - * - * @param encoderName エンコーダ - * @param value 変換する文字列 - * @return プロファイルとレベル - */ - private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { - String[] t = value.split("-"); - if (t.length == 2) { - try { - String profile = t[0].trim(); - String level = t[1].trim(); - switch (encoderName) { - case H264: { - H264Profile p = H264Profile.nameOf(profile); - H264Level l = H264Level.nameOf(level); - return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } - case H265: { - H265Profile p = H265Profile.nameOf(profile); - H265Level l = H265Level.nameOf(level); - return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } - } - } catch (Exception e) { - return null; - } - } - return null; - } - - /** - * 切り抜き範囲の値を取得します. - * - * 未設定の場合には null を返却します。 - * - * @param key キー - * @return 切り抜き範囲 - */ - private Integer getDrawingRange(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - try { - return Integer.parseInt(pref.getText()); - } catch (NumberFormatException e) { - // ignore. - } - } - return null; - } - /** * 設定が変更された時に呼び出されるリスナー. */ @@ -708,54 +501,6 @@ private Integer getDrawingRange(String key) { if (size != null) { settings.setPreviewSize(size); } - } else if ("preview_encoder".equals(key)) { - // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく - settings.setProfileLevel(null); - HostMediaRecorder.VideoEncoderName encoderName = - HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); - setPreviewProfileLevelPreference(settings, encoderName, true); - } else if ("preview_profile_level".equalsIgnoreCase(key)) { - settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); - } else if ("preview_clip_left".equalsIgnoreCase(key)) { - try { - int clipLeft = Integer.parseInt((String) newValue); - Integer clipRight = getDrawingRange("preview_clip_right"); - if (clipRight != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_top".equalsIgnoreCase(key)) { - try { - int clipTop = Integer.parseInt((String) newValue); - Integer clipBottom = getDrawingRange("preview_clip_bottom"); - if (clipBottom != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_right".equalsIgnoreCase(key)) { - try { - int clipRight = Integer.parseInt((String) newValue); - Integer clipLeft = getDrawingRange("preview_clip_left"); - if (clipLeft != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { - try { - int clipBottom = Integer.parseInt((String) newValue); - Integer clipTop = getDrawingRange("preview_clip_top"); - if (clipTop != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } } return true; }; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java index 9428381c08..56969828d8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java @@ -34,7 +34,7 @@ public AbstractBroadcaster(HostMediaRecorder recorder, String broadcastURI) { @Override public String getMimeType() { - return ""; + return "video/x-rtmp"; } @Override @@ -98,27 +98,25 @@ public void setVideoQuality(VideoQuality videoQuality) { HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - Rect rect = settings.getDrawingRange(); - if (rect != null) { - videoQuality.setVideoWidth(rect.width()); - videoQuality.setVideoHeight(rect.height()); - } else { - EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); - int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); - int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); - videoQuality.setVideoWidth(w); - videoQuality.setVideoHeight(h); + EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); + Size previewSize = settings.getPreviewSize(getMimeType()); + if (previewSize == null) { + previewSize = settings.getPreviewSize(); } - videoQuality.setBitRate(settings.getPreviewBitRate()); - videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); - videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); - videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); - videoQuality.setIntraRefresh(settings.getIntraRefresh()); - videoQuality.setProfile(settings.getProfile()); - videoQuality.setLevel(settings.getLevel()); - if (settings.getPreviewBitRateMode() != null) { - switch (settings.getPreviewBitRateMode()) { + int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); + int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); + videoQuality.setVideoWidth(w); + videoQuality.setVideoHeight(h); + videoQuality.setDrawingRange(settings.getDrawingRange(getMimeType())); + videoQuality.setBitRate(settings.getPreviewBitRate(getMimeType())); + videoQuality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); + videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval(getMimeType())); + videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder(getMimeType())); + videoQuality.setIntraRefresh(settings.getIntraRefresh(getMimeType())); + videoQuality.setProfile(settings.getProfile(getMimeType())); + videoQuality.setLevel(settings.getLevel(getMimeType())); + if (settings.getPreviewBitRateMode(getMimeType()) != null) { + switch (settings.getPreviewBitRateMode(getMimeType())) { case VBR: videoQuality.setBitRateMode(VideoQuality.BitRateMode.VBR); break; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index feeadc7adb..16f5fb6db3 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -1,7 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.recorder; import android.content.Context; -import android.graphics.Rect; import android.util.Log; import android.util.Size; @@ -10,7 +9,6 @@ import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGQuality; import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGServer; -import java.io.IOException; import java.net.Socket; import javax.net.ssl.SSLContext; @@ -19,7 +17,7 @@ public abstract class AbstractMJPEGPreviewServer extends AbstractPreviewServer { /** * Motion JPEG のマイムタイプを定義します. */ - protected static final String MIME_TYPE = "video/x-mjpeg"; + public static final String MIME_TYPE = "video/x-mjpeg"; /** * サーバー名を定義します. @@ -133,14 +131,16 @@ private void setMJPEGQuality(MJPEGQuality quality) { HostMediaRecorder.Settings settings = recorder.getSettings(); EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); + Size previewSize = settings.getPreviewSize(getMimeType()); + if (previewSize == null) { + previewSize = settings.getPreviewSize(); + } int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); quality.setWidth(w); quality.setHeight(h); - quality.setFrameRate(settings.getPreviewMaxFrameRate()); - quality.setQuality(settings.getPreviewQuality()); - quality.setDrawingRange(settings.getDrawingRange()); + quality.setQuality(settings.getPreviewQuality(getMimeType())); + quality.setDrawingRange(settings.getDrawingRange(getMimeType())); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java index 7f9fd3a872..549246ba0a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java @@ -192,20 +192,23 @@ public void setVideoQuality(VideoQuality videoQuality) { HostMediaRecorder.Settings settings = recorder.getSettings(); EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); + Size previewSize = settings.getPreviewSize(getMimeType()); + if (previewSize == null) { + previewSize = settings.getPreviewSize(); + } int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); videoQuality.setVideoWidth(w); videoQuality.setVideoHeight(h); - videoQuality.setDrawingRange(settings.getDrawingRange()); - videoQuality.setBitRate(settings.getPreviewBitRate()); - videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); - videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); - videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); - videoQuality.setIntraRefresh(settings.getIntraRefresh()); - videoQuality.setProfile(settings.getProfile()); - videoQuality.setLevel(settings.getLevel()); - if (settings.getPreviewBitRateMode() != null) { + videoQuality.setDrawingRange(settings.getDrawingRange(getMimeType())); + videoQuality.setBitRate(settings.getPreviewBitRate(getMimeType())); + videoQuality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); + videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval(getMimeType())); + videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder(getMimeType())); + videoQuality.setIntraRefresh(settings.getIntraRefresh(getMimeType())); + videoQuality.setProfile(settings.getProfile(getMimeType())); + videoQuality.setLevel(settings.getLevel(getMimeType())); + if (settings.getPreviewBitRateMode(getMimeType()) != null) { switch (settings.getPreviewBitRateMode()) { default: case VBR: diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index 9aeea91a44..795b470ae2 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -41,7 +41,6 @@ public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder, boo mSettings = new SRTSettings(context); } - @Override public String getUri() { return "srt://localhost:" + getPort(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index 38d38de1bc..d03d74f182 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -19,7 +19,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.net.ssl.SSLContext; @@ -460,10 +462,15 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host * HostMediaRecorder の設定を保持するクラス. */ abstract class Settings { - private final PropertyUtil mPref; + private final PropertyUtil mProperty; + private final Map mPropertyMap = new HashMap<>(); public Settings(Context context, HostMediaRecorder recorder) { - mPref = new PropertyUtil(context, recorder.getId()); + mProperty = new PropertyUtil(context, recorder.getId()); + mPropertyMap.put("video/x-mjpeg", new PropertyUtil(context, recorder.getId() + "-mjpeg")); + mPropertyMap.put("video/x-rtp", new PropertyUtil(context, recorder.getId() + "-rtsp")); + mPropertyMap.put("video/MP2T", new PropertyUtil(context, recorder.getId() + "-srt")); + mPropertyMap.put("video/x-rtmp", new PropertyUtil(context, recorder.getId() + "-rtmp")); } /** @@ -472,21 +479,78 @@ public Settings(Context context, HostMediaRecorder recorder) { * @return 初期化されている場合はtrue、それ以外はfalse */ public boolean isInitialized() { - return mPref.getString("test", null) != null; + return mProperty.getString("initialization", null) != null; } /** * 初期化完了を書き込みます. */ public void finishInitialization() { - mPref.put("test", "test"); + mProperty.put("initialization", "completion"); } /** * 保存データを初期化します. */ public void clear() { - mPref.clear(); + mProperty.clear(); + for (String key : mPropertyMap.keySet()) { + PropertyUtil prof = mPropertyMap.get(key); + if (prof != null) { + prof.clear(); + } + } + } + + // カメラ設定 + + /** + * 写真サイズを取得します. + * + * @return 写真サイズ + */ + public Size getPictureSize() { + return mProperty.getSize("picture_size_width", "picture_size_height"); + } + + /** + * 写真サイズを設定します. + * + * サポートされていない写真サイズの場合は IllegalArgumentException を発生させます。 + * + * @param pictureSize 写真サイズ + */ + public void setPictureSize(Size pictureSize) { + if (!isSupportedPictureSize(pictureSize)) { + throw new IllegalArgumentException("pictureSize is not supported."); + } + mProperty.put( + "picture_size_width", + "picture_size_height", + pictureSize); + } + + /** + * プレビューサイズを取得します. + * + * @return プレビューサイズ + */ + public Size getPreviewSize() { + return mProperty.getSize("preview_size_width", "preview_size_height"); + } + + /** + * プレビューサイズを設定します. + * + * サポートされていないプレビューサイズの場合は IllegalArgumentException を発生させます。 + * + * @param previewSize プレビューサイズ + */ + public void setPreviewSize(Size previewSize) { + if (!isSupportedPreviewSize(previewSize)) { + throw new IllegalArgumentException("previewSize is not supported."); + } + mProperty.put("preview_size_width", "preview_size_height", previewSize); } /** @@ -497,7 +561,7 @@ public void clear() { * @return 自動フォーカスモード */ public Integer getPreviewAutoFocusMode() { - return mPref.getInteger("preview_auto_focus", null); + return mProperty.getInteger("preview_auto_focus", null); } /** @@ -511,525 +575,711 @@ public Integer getPreviewAutoFocusMode() { */ public void setPreviewAutoFocusMode(Integer mode) { if (mode == null) { - mPref.remove("preview_auto_focus"); + mProperty.remove("preview_auto_focus"); } else { if (!isSupportedAutoFocusMode(mode)) { throw new IllegalArgumentException("focus mode is not supported."); } - mPref.put("preview_auto_focus", mode); + mProperty.put("preview_auto_focus", mode); } } /** - * プレビュー配信エンコード名を取得します. + * ホワイトバランスの設定を取得します. * - * @return エンコード名 + * @return ホワイトバランス */ - public VideoEncoderName getPreviewEncoderName() { - return VideoEncoderName.nameOf(getPreviewEncoder()); + public Integer getPreviewWhiteBalance() { + return mProperty.getInteger("preview_white_balance", null); } /** - * プレビューの配信エンコードの名前を取得します. - * - * 未設定の場合は h264 を返却します。 + * ホワイトバランスを設定します. * - * @return プレビューの配信エンコードの名前 + * @param whiteBalance ホワイトバランス */ - public String getPreviewEncoder() { - return mPref.getString("preview_encoder", "h264"); + public void setPreviewWhiteBalance(Integer whiteBalance) { + if (whiteBalance == null) { + mProperty.remove("preview_white_balance"); + } else { + if (!isSupportedWhiteBalanceMode(whiteBalance)) { + throw new IllegalArgumentException("WhiteBalance is unsupported value."); + } + mProperty.put("preview_white_balance", whiteBalance); + } } - /** - * プレビューの配信エンコードの名前を設定します. - * - * @param encoder プレビューの配信エンコードの名前 - */ - public void setPreviewEncoder(String encoder) { - if (encoder == null) { - mPref.remove("preview_encoder"); + public Integer getPreviewAutoExposureMode() { + return mProperty.getInteger("preview_auto_exposure_mode", null); + } + + public void setPreviewAutoExposureMode(Integer mode) { + if (mode == null) { + mProperty.remove("preview_auto_exposure_mode"); } else { - if (!isSupportedVideoEncoder(encoder)) { - throw new IllegalArgumentException("encoder is not supported."); + if (!isSupportedAutoExposureMode(mode)) { + throw new IllegalArgumentException("Exposure mode is unsupported value."); } - mPref.put("preview_encoder", encoder); + mProperty.put("preview_auto_exposure_mode", mode); } } /** - * プロファイルとレベルを取得します. - * - * 未設定の場合には、null を返却します。 + * 手ぶれ補正モードを取得します. * - * @return プロファイルとレベル + * @return 手ぶれ補正モード */ - public ProfileLevel getProfileLevel() { - Integer profile = mPref.getInteger("preview_profile", null); - Integer level = mPref.getInteger("preview_level", null); - if (profile != null && level != null) { - return new ProfileLevel(profile, level); - } - return null; + public Integer getStabilizationMode() { + return mProperty.getInteger("preview_stabilization_mode", null); } /** - * プロファイルとレベルを設定します. - * - * null が設定された場合には、未設定にします。 - * - * サポートされていないプロファイルとレベルが設定された場合には例外を発生します。 + * 手ぶれ補正モードを設定します. * - * @param pl プロファイルとレベル + * @param mode 手ぶれ補正モード */ - public void setProfileLevel(ProfileLevel pl) { - if (pl == null) { - mPref.remove("preview_profile"); - mPref.remove("preview_level"); + public void setStabilizationMode(Integer mode) { + if (mode == null) { + mProperty.remove("preview_stabilization_mode"); } else { - if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) { - throw new IllegalArgumentException("profile and level are not supported."); + if (!isSupportedStabilization(mode)) { + throw new IllegalArgumentException("Stabilization Mode is unsupported value."); } - mPref.put("preview_profile", pl.getProfile()); - mPref.put("preview_level", pl.getLevel()); + mProperty.put("preview_stabilization_mode", mode); } } /** - * 設定されているプロファイルを取得します. + * 光学手ぶれ補正モードを取得します. * - * @return プロファイル + * @return 光学手ぶれ補正モード */ - public Integer getProfile() { - return mPref.getInteger("preview_profile", 0); + public Integer getOpticalStabilizationMode() { + return mProperty.getInteger("preview_optical_stabilization_mode", null); } /** - * 設定されているレベルを取得します. + * 光学手ぶれ補正モードを設定します. * - * @return レベル + * @param mode 光学手ぶれ補正モード */ - public Integer getLevel() { - return mPref.getInteger("preview_level", 0); + public void setOpticalStabilizationMode(Integer mode) { + if (mode == null) { + mProperty.remove("preview_optical_stabilization_mode"); + } else { + if (!isSupportedOpticalStabilization(mode)) { + throw new IllegalArgumentException("Optical Stabilization Mode is unsupported value."); + } + mProperty.put("preview_optical_stabilization_mode", mode); + } } /** - * 写真サイズを取得します. + * デジタルズームを取得します. * - * @return 写真サイズ + * @return デジタルズーム */ - public Size getPictureSize() { - return mPref.getSize("picture_size_width", "picture_size_height"); + public Float getDigitalZoom() { + return mProperty.getFloat("preview_digital_zoom", null); } /** - * 写真サイズを設定します. - * - * サポートされていない写真サイズの場合は IllegalArgumentException を発生させます。 + * デジタルズームを設定します. * - * @param pictureSize 写真サイズ + * @param zoom デジタルズーム */ - public void setPictureSize(Size pictureSize) { - if (!isSupportedPictureSize(pictureSize)) { - throw new IllegalArgumentException("pictureSize is not supported."); + public void setDigitalZoom(Float zoom) { + if (zoom == null) { + mProperty.remove("preview_digital_zoom"); + } else { + if (!isSupportedDigitalZoom(zoom)) { + throw new IllegalArgumentException("Digital zoom is unsupported value."); + } + mProperty.put("preview_digital_zoom", zoom); } - mPref.put( - "picture_size_width", - "picture_size_height", - pictureSize); } - /** - * プレビューサイズを取得します. - * - * @return プレビューサイズ - */ - public Size getPreviewSize() { - return mPref.getSize("preview_size_width", "preview_size_height"); + public Float getFocalLength() { + Float value = mProperty.getFloat("preview_focal_length", null); + if (value == null) { + return null; + } + List focalLengthList = getSupportedFocalLengthList(); + for (Float focalLength : focalLengthList) { + if (Math.abs(focalLength - value) < 0.01f) { + return focalLength; + } + } + return null; } - /** - * プレビューサイズを設定します. - * - * サポートされていないプレビューサイズの場合は IllegalArgumentException を発生させます。 - * - * @param previewSize プレビューサイズ - */ - public void setPreviewSize(Size previewSize) { - if (!isSupportedPreviewSize(previewSize)) { - throw new IllegalArgumentException("previewSize is not supported."); + public void setFocalLength(Float focalLength) { + if (focalLength == null) { + mProperty.remove("preview_focal_length"); + } else { + if (!isSupportedFocalLength(focalLength)) { + throw new IllegalArgumentException("focalLength cannot set."); + } + mProperty.put("preview_focal_length", focalLength); } - mPref.put("preview_size_width", "preview_size_height", previewSize); } - /** - * フレームレートを取得します. - * - * @return フレームレート - */ - public int getPreviewMaxFrameRate() { - return mPref.getInteger("preview_framerate", 30); + public Integer getNoiseReduction() { + return mProperty.getInteger("preview_reduction_noise", null); } - /** - * フレームレートを設定します. - * - * @param previewMaxFrameRate フレームレート - */ - public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { - if (previewMaxFrameRate <= 0) { - throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); + public void setNoiseReduction(Integer mode) { + if (mode == null) { + mProperty.remove("preview_reduction_noise"); + } else { + if (!isSupportedNoiseReduction(mode)) { + throw new IllegalArgumentException("mode cannot set."); + } + mProperty.put("preview_reduction_noise", mode); } - mPref.put("preview_framerate", previewMaxFrameRate); } - /** - * ビットレートを取得します. - * - * @return ビットレート(byte) - */ - public int getPreviewBitRate() { - return mPref.getInteger("preview_bitrate", 2 * 1024 * 1024); + public Integer getAutoExposureMode() { + return mProperty.getInteger("preview_auto_exposure_mode", null); } - /** - * ビットレートを設定します. - * - * @param previewBitRate ビットレート(byte) - */ - public void setPreviewBitRate(int previewBitRate) { - if (previewBitRate <= 0) { - throw new IllegalArgumentException("previewBitRate is zero or negative."); + public void setAutoExposureMode(Integer mode) { + if (mode == null) { + mProperty.remove("preview_auto_exposure_mode"); + } else { + if (!isSupportedAutoExposureMode(mode)) { + throw new IllegalArgumentException("mode cannot set."); + } + mProperty.put("preview_auto_exposure_mode", mode); } - mPref.put("preview_bitrate", String.valueOf(previewBitRate)); } - /** - * キーフレームインターバルを取得します. - * - * @return キーフレームを発行する間隔(ミリ秒) - */ - public int getPreviewKeyFrameInterval() { - return mPref.getInteger("preview_i_frame_interval", 1); + public Long getSensorExposureTime() { + return mProperty.getLong("preview_sensor_exposure_time", null); + } + + public void setSensorExposureTime(Long exposureTime) { + if (exposureTime == null) { + mProperty.remove("preview_sensor_exposure_time"); + } else { + if (!isSupportedSensorExposureTime(exposureTime)) { + throw new IllegalArgumentException("exposureTime cannot set."); + } + mProperty.put("preview_sensor_exposure_time", exposureTime); + } + } + + public Integer getSensorSensitivity() { + return mProperty.getInteger("preview_sensor_sensitivity", null); + } + + public void setSensorSensitivity(Integer sensitivity) { + if (sensitivity == null) { + mProperty.remove("preview_sensor_sensitivity"); + } else { + if (!isSupportedSensorSensorSensitivity(sensitivity)) { + throw new IllegalArgumentException("sensitivity cannot set."); + } + mProperty.put("preview_sensor_sensitivity", sensitivity); + } + } + + public Long getSensorFrameDuration() { + return mProperty.getLong("preview_sensor_frame_duration", null); + } + + public void setSensorFrameDuration(Long frameDuration) { + if (frameDuration == null) { + mProperty.remove("preview_sensor_frame_duration"); + } else { + if (!isSupportedSensorFrameDuration(frameDuration)) { + throw new IllegalArgumentException("frameDuration cannot set."); + } + mProperty.put("preview_sensor_frame_duration", frameDuration); + } } + public Integer getPreviewWhiteBalanceTemperature() { + return mProperty.getInteger("preview_sensor_white_balance_temperature", null); + } + + public void setPreviewWhiteBalanceTemperature(Integer temperature) { + if (temperature == null) { + mProperty.remove("preview_sensor_white_balance_temperature"); + } else { + if (!isSupportedWhiteBalanceTemperature(temperature)) { + throw new IllegalArgumentException("whiteBalanceTemperature cannot set."); + } + mProperty.put("preview_sensor_white_balance_temperature", temperature); + } + } + + + /// ポート番号 + /** - * キーフレームインターバルを設定します. + * サーバ用のポート番号を取得します. * - * @param previewKeyFrameInterval キーフレームを発行する間隔(ミリ秒) + * @return サーバ用のポート番号 */ - public void setPreviewKeyFrameInterval(int previewKeyFrameInterval) { - if (previewKeyFrameInterval <= 0) { - throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); + public Integer getPort(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("port", 0); } - mPref.put("preview_i_frame_interval", previewKeyFrameInterval); + return 0; } /** - * プレビューの品質を取得します. + * サーバ用のポート番号を設定します. * - * @return プレビューの品質 + * @param mimeType マイムタイプ + * @param port サーバ用のポート番号 */ - public int getPreviewQuality() { - return mPref.getInteger("preview_jpeg_quality", 80); + public void setPort(String mimeType, int port) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.put("port", port); + } + } + + //// MediaCodec + + public Size getPreviewSize(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getSize("preview_size_width", "preview_size_height"); + } + return null; + } + + public void setPreviewSize(String mimeType, Size previewSize) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.put("preview_size_width", "preview_size_height", previewSize); + } + } + + private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; + + public VideoEncoderName getPreviewEncoderName(String mimeType) { + return VideoEncoderName.nameOf(getPreviewEncoder(mimeType)); + } + + public String getPreviewEncoder(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getString("preview_encoder", DEFAULT_PREVIEW_ENCODER); + } + return DEFAULT_PREVIEW_ENCODER; + } + + public void setPreviewEncoder(String mimeType, String encoder) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + if (encoder == null) { + prof.remove("preview_encoder"); + } else { + if (!isSupportedVideoEncoder(encoder)) { + throw new IllegalArgumentException("encoder is not supported."); + } + prof.put("preview_encoder", encoder); + } + } } /** - * プレビューの品質を設定します. - * - * 0 から 100 の間で設定することができます。 - * それ以外は例外が発生します。 + * プレビュー配信エンコード名を取得します. * - * @param quality プレビューの品質 + * @return エンコード名 */ - public void setPreviewQuality(int quality) { - if (quality < 0) { - throw new IllegalArgumentException("quality is negative value."); - } - if (quality > 100) { - throw new IllegalArgumentException("quality is over 100."); - } - mPref.put("preview_jpeg_quality", quality); + public VideoEncoderName getPreviewEncoderName() { + return VideoEncoderName.nameOf(getPreviewEncoder()); } /** - * ホワイトバランスの設定を取得します. + * プレビューの配信エンコードの名前を取得します. * - * @return ホワイトバランス + * 未設定の場合は h264 を返却します。 + * + * @return プレビューの配信エンコードの名前 */ - public Integer getPreviewWhiteBalance() { - return mPref.getInteger("preview_white_balance", null); + public String getPreviewEncoder() { + return mProperty.getString("preview_encoder", DEFAULT_PREVIEW_ENCODER); } /** - * ホワイトバランスを設定します. + * プレビューの配信エンコードの名前を設定します. * - * @param whiteBalance ホワイトバランス + * @param encoder プレビューの配信エンコードの名前 */ - public void setPreviewWhiteBalance(Integer whiteBalance) { - if (whiteBalance == null) { - mPref.remove("preview_white_balance"); + public void setPreviewEncoder(String encoder) { + if (encoder == null) { + mProperty.remove("preview_encoder"); } else { - if (!isSupportedWhiteBalanceMode(whiteBalance)) { - throw new IllegalArgumentException("WhiteBalance is unsupported value."); + if (!isSupportedVideoEncoder(encoder)) { + throw new IllegalArgumentException("encoder is not supported."); } - mPref.put("preview_white_balance", whiteBalance); + mProperty.put("preview_encoder", encoder); } } - public Integer getPreviewAutoExposureMode() { - return mPref.getInteger("preview_auto_exposure_mode", null); + public ProfileLevel getProfileLevel(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + Integer profile = prof.getInteger("preview_profile", null); + Integer level = prof.getInteger("preview_level", null); + if (profile != null && level != null) { + return new ProfileLevel(profile, level); + } + } + return null; } - public void setPreviewAutoExposureMode(Integer mode) { - if (mode == null) { - mPref.remove("preview_auto_exposure_mode"); - } else { - if (!isSupportedAutoExposureMode(mode)) { - throw new IllegalArgumentException("Exposure mode is unsupported value."); + public void setProfileLevel(String mimeType, ProfileLevel pl) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + if (pl == null) { + prof.remove("preview_profile"); + prof.remove("preview_level"); + } else { + if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) { + throw new IllegalArgumentException("profile and level are not supported."); + } + prof.put("preview_profile", pl.getProfile()); + prof.put("preview_level", pl.getLevel()); } - mPref.put("preview_auto_exposure_mode", mode); } } /** - * ソフトウェアエンコーダを優先的に使用するフラグを確認します. + * プロファイルとレベルを取得します. * - * @return ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false + * 未設定の場合には、null を返却します。 + * + * @return プロファイルとレベル */ - public boolean isUseSoftwareEncoder() { - return mPref.getBoolean("preview_use_software_encoder", false); + public ProfileLevel getProfileLevel() { + Integer profile = mProperty.getInteger("preview_profile", null); + Integer level = mProperty.getInteger("preview_level", null); + if (profile != null && level != null) { + return new ProfileLevel(profile, level); + } + return null; + } + + /** + * プロファイルとレベルを設定します. + * + * null が設定された場合には、値を削除して未設定にします。 + * + * サポートされていないプロファイルとレベルが設定された場合には例外を発生します。 + * + * @param pl プロファイルとレベル + */ + public void setProfileLevel(ProfileLevel pl) { + if (pl == null) { + mProperty.remove("preview_profile"); + mProperty.remove("preview_level"); + } else { + if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) { + throw new IllegalArgumentException("profile and level are not supported."); + } + mProperty.put("preview_profile", pl.getProfile()); + mProperty.put("preview_level", pl.getLevel()); + } + } + + public Integer getProfile(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("preview_profile", 0); + } + return 0; } /** - * ソフトウェアエンコーダを優先的に使用するフラグを設定します. + * 設定されているプロファイルを取得します. * - * @param used ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false + * @return プロファイル */ - public void setUseSoftwareEncoder(boolean used) { - mPref.put("preview_use_software_encoder", used); + public Integer getProfile() { + return mProperty.getInteger("preview_profile", 0); } - /** - * イントラリフレッシュのフレーム数を取得します. - * - * @return イントラリフレッシュのフレーム数 - */ - public Integer getIntraRefresh() { - return mPref.getInteger("preview_intra_refresh", 0); + public Integer getLevel(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.getInteger("preview_level", 0); + } + return 0; } /** - * イントラリフレッシュのフレーム数を設定します. + * 設定されているレベルを取得します. * - * @param refresh イントラリフレッシュのフレーム数 + * @return レベル */ - public void setIntraRefresh(Integer refresh) { - if (refresh == null) { - mPref.remove("preview_intra_refresh"); - } else { - mPref.put("preview_intra_refresh", refresh); + public Integer getLevel() { + return mProperty.getInteger("preview_level", 0); + } + + private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; + + public int getPreviewMaxFrameRate(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); + } + return DEFAULT_PREVIEW_MAX_FRAME_RATE; + } + + public void setPreviewMaxFrameRate(String mimeType, Integer previewMaxFrameRate) { + if (previewMaxFrameRate <= 0) { + throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); + } + + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + mProperty.put("preview_framerate", previewMaxFrameRate); } } /** - * 手ぶれ補正モードを取得します. + * フレームレートを取得します. * - * @return 手ぶれ補正モード + * @return フレームレート */ - public Integer getStabilizationMode() { - return mPref.getInteger("preview_stabilization_mode", null); + public int getPreviewMaxFrameRate() { + return mProperty.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); } /** - * 手ぶれ補正モードを設定します. + * フレームレートを設定します. * - * @param mode 手ぶれ補正モード + * @param previewMaxFrameRate フレームレート */ - public void setStabilizationMode(Integer mode) { - if (mode == null) { - mPref.remove("preview_stabilization_mode"); - } else { - if (!isSupportedStabilization(mode)) { - throw new IllegalArgumentException("Stabilization Mode is unsupported value."); - } - mPref.put("preview_stabilization_mode", mode); + public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { + if (previewMaxFrameRate <= 0) { + throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); } + mProperty.put("preview_framerate", previewMaxFrameRate); } - /** - * 光学手ぶれ補正モードを取得します. - * - * @return 光学手ぶれ補正モード - */ - public Integer getOpticalStabilizationMode() { - return mPref.getInteger("preview_optical_stabilization_mode", null); + private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; + + public int getPreviewBitRate(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("preview_bitrate", DEFAULT_PREVIEW_BITRATE); + } + return DEFAULT_PREVIEW_BITRATE; } - /** - * 光学手ぶれ補正モードを設定します. - * - * @param mode 光学手ぶれ補正モード - */ - public void setOpticalStabilizationMode(Integer mode) { - if (mode == null) { - mPref.remove("preview_optical_stabilization_mode"); - } else { - if (!isSupportedOpticalStabilization(mode)) { - throw new IllegalArgumentException("Optical Stabilization Mode is unsupported value."); - } - mPref.put("preview_optical_stabilization_mode", mode); + public void setPreviewBitRate(String mimeType, int previewBitRate) { + if (previewBitRate <= 0) { + throw new IllegalArgumentException("previewBitRate is zero or negative."); + } + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.put("preview_bitrate", String.valueOf(previewBitRate)); } } /** - * デジタルズームを取得します. + * ビットレートを取得します. * - * @return デジタルズーム + * @return ビットレート(byte) */ - public Float getDigitalZoom() { - return mPref.getFloat("preview_digital_zoom", null); + public int getPreviewBitRate() { + return mProperty.getInteger("preview_bitrate", DEFAULT_PREVIEW_BITRATE); } /** - * デジタルズームを設定します. + * ビットレートを設定します. * - * @param zoom デジタルズーム + * @param previewBitRate ビットレート(byte) */ - public void setDigitalZoom(Float zoom) { - if (zoom == null) { - mPref.remove("preview_digital_zoom"); - } else { - if (!isSupportedDigitalZoom(zoom)) { - throw new IllegalArgumentException("Digital zoom is unsupported value."); - } - mPref.put("preview_digital_zoom", zoom); + public void setPreviewBitRate(int previewBitRate) { + if (previewBitRate <= 0) { + throw new IllegalArgumentException("previewBitRate is zero or negative."); } + mProperty.put("preview_bitrate", String.valueOf(previewBitRate)); } - public Float getFocalLength() { - Float value = mPref.getFloat("preview_focal_length", null); - if (value == null) { - return null; - } - List focalLengthList = getSupportedFocalLengthList(); - for (Float focalLength : focalLengthList) { - if (Math.abs(focalLength - value) < 0.01f) { - return focalLength; - } + public BitRateMode getPreviewBitRateMode(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return BitRateMode.nameOf(prof.getString("preview_bitrate_mode", null)); } return null; } - public void setFocalLength(Float focalLength) { - if (focalLength == null) { - mPref.remove("preview_focal_length"); - } else { - if (!isSupportedFocalLength(focalLength)) { - throw new IllegalArgumentException("focalLength cannot set."); + public void setPreviewBitRateMode(String mimeType, BitRateMode mode) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + if (mode == null) { + prof.remove("preview_bitrate_mode"); + } else { + prof.put("preview_bitrate_mode", mode.getName()); } - mPref.put("preview_focal_length", focalLength); } } - public Integer getNoiseReduction() { - return mPref.getInteger("preview_reduction_noise", null); + public BitRateMode getPreviewBitRateMode() { + return BitRateMode.nameOf(mProperty.getString("preview_bitrate_mode", null)); } - public void setNoiseReduction(Integer mode) { + public void setPreviewBitRateMode(BitRateMode mode) { if (mode == null) { - mPref.remove("preview_reduction_noise"); + mProperty.remove("preview_bitrate_mode"); } else { - if (!isSupportedNoiseReduction(mode)) { - throw new IllegalArgumentException("mode cannot set."); - } - mPref.put("preview_reduction_noise", mode); + mProperty.put("preview_bitrate_mode", mode.getName()); } } - public BitRateMode getPreviewBitRateMode() { - return BitRateMode.nameOf(mPref.getString("preview_bitrate_mode", null)); + private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; + + public int getPreviewKeyFrameInterval(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("preview_i_frame_interval", DEFAULT_PREVIEW_KEY_FRAME_INTERVAL); + } + return DEFAULT_PREVIEW_KEY_FRAME_INTERVAL; } - public void setPreviewBitRateMode(BitRateMode mode) { - if (mode == null) { - mPref.remove("preview_bitrate_mode"); - } else { - mPref.put("preview_bitrate_mode", mode.getName()); + public void setPreviewKeyFrameInterval(String mimeType, int previewKeyFrameInterval) { + if (previewKeyFrameInterval <= 0) { + throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); + } + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.put("preview_i_frame_interval", previewKeyFrameInterval); } } - public Integer getAutoExposureMode() { - return mPref.getInteger("preview_auto_exposure_mode", null); + /** + * キーフレームインターバルを取得します. + * + * @return キーフレームを発行する間隔(ミリ秒) + */ + public int getPreviewKeyFrameInterval() { + return mProperty.getInteger("preview_i_frame_interval", DEFAULT_PREVIEW_KEY_FRAME_INTERVAL); } - public void setAutoExposureMode(Integer mode) { - if (mode == null) { - mPref.remove("preview_auto_exposure_mode"); - } else { - if (!isSupportedAutoExposureMode(mode)) { - throw new IllegalArgumentException("mode cannot set."); - } - mPref.put("preview_auto_exposure_mode", mode); + /** + * キーフレームインターバルを設定します. + * + * @param previewKeyFrameInterval キーフレームを発行する間隔(ミリ秒) + */ + public void setPreviewKeyFrameInterval(int previewKeyFrameInterval) { + if (previewKeyFrameInterval <= 0) { + throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); } + mProperty.put("preview_i_frame_interval", previewKeyFrameInterval); } - public Long getSensorExposureTime() { - return mPref.getLong("preview_sensor_exposure_time", null); + public boolean isUseSoftwareEncoder(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getBoolean("preview_use_software_encoder", false); + } + return false; } - public void setSensorExposureTime(Long exposureTime) { - if (exposureTime == null) { - mPref.remove("preview_sensor_exposure_time"); - } else { - if (!isSupportedSensorExposureTime(exposureTime)) { - throw new IllegalArgumentException("exposureTime cannot set."); - } - mPref.put("preview_sensor_exposure_time", exposureTime); + public void setUseSoftwareEncoder(String mimeType, boolean used) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.put("preview_use_software_encoder", used); } } - public Integer getSensorSensitivity() { - return mPref.getInteger("preview_sensor_sensitivity", null); + /** + * ソフトウェアエンコーダを優先的に使用するフラグを確認します. + * + * @return ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false + */ + public boolean isUseSoftwareEncoder() { + return mProperty.getBoolean("preview_use_software_encoder", false); } - public void setSensorSensitivity(Integer sensitivity) { - if (sensitivity == null) { - mPref.remove("preview_sensor_sensitivity"); - } else { - if (!isSupportedSensorSensorSensitivity(sensitivity)) { - throw new IllegalArgumentException("sensitivity cannot set."); + /** + * ソフトウェアエンコーダを優先的に使用するフラグを設定します. + * + * @param used ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false + */ + public void setUseSoftwareEncoder(boolean used) { + mProperty.put("preview_use_software_encoder", used); + } + + public Integer getIntraRefresh(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("preview_intra_refresh", 0); + } + return 0; + } + + public void setIntraRefresh(String mimeType, Integer refresh) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + if (refresh == null) { + prof.remove("preview_intra_refresh"); + } else { + prof.put("preview_intra_refresh", refresh); } - mPref.put("preview_sensor_sensitivity", sensitivity); } } - public Long getSensorFrameDuration() { - return mPref.getLong("preview_sensor_frame_duration", null); + /** + * イントラリフレッシュのフレーム数を取得します. + * + * @return イントラリフレッシュのフレーム数 + */ + public Integer getIntraRefresh() { + return mProperty.getInteger("preview_intra_refresh", 0); } - public void setSensorFrameDuration(Long frameDuration) { - if (frameDuration == null) { - mPref.remove("preview_sensor_frame_duration"); + /** + * イントラリフレッシュのフレーム数を設定します. + * + * @param refresh イントラリフレッシュのフレーム数 + */ + public void setIntraRefresh(Integer refresh) { + if (refresh == null) { + mProperty.remove("preview_intra_refresh"); } else { - if (!isSupportedSensorFrameDuration(frameDuration)) { - throw new IllegalArgumentException("frameDuration cannot set."); - } - mPref.put("preview_sensor_frame_duration", frameDuration); + mProperty.put("preview_intra_refresh", refresh); } } - public Integer getPreviewWhiteBalanceTemperature() { - return mPref.getInteger("preview_sensor_white_balance_temperature", null); + public Rect getDrawingRange(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getRect("preview_clip_left", + "preview_clip_top", + "preview_clip_right", + "preview_clip_bottom"); + } + return null; } - public void setPreviewWhiteBalanceTemperature(Integer temperature) { - if (temperature == null) { - mPref.remove("preview_sensor_white_balance_temperature"); - } else { - if (!isSupportedWhiteBalanceTemperature(temperature)) { - throw new IllegalArgumentException("whiteBalanceTemperature cannot set."); + public void setDrawingRange(String mimeType, Rect rect) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + if (rect == null) { + prof.remove("preview_clip_left"); + prof.remove("preview_clip_top"); + prof.remove("preview_clip_right"); + prof.remove("preview_clip_bottom"); + } else { + prof.put( + "preview_clip_left", + "preview_clip_top", + "preview_clip_right", + "preview_clip_bottom", + rect); } - mPref.put("preview_sensor_white_balance_temperature", temperature); } } @@ -1041,7 +1291,7 @@ public void setPreviewWhiteBalanceTemperature(Integer temperature) { * @return 切り抜き範囲 */ public Rect getDrawingRange() { - return mPref.getRect("preview_clip_left", + return mProperty.getRect("preview_clip_left", "preview_clip_top", "preview_clip_right", "preview_clip_bottom"); @@ -1056,12 +1306,12 @@ public Rect getDrawingRange() { */ public void setDrawingRange(Rect rect) { if (rect == null) { - mPref.remove("preview_clip_left"); - mPref.remove("preview_clip_top"); - mPref.remove("preview_clip_right"); - mPref.remove("preview_clip_bottom"); + mProperty.remove("preview_clip_left"); + mProperty.remove("preview_clip_top"); + mProperty.remove("preview_clip_right"); + mProperty.remove("preview_clip_bottom"); } else { - mPref.put( + mProperty.put( "preview_clip_left", "preview_clip_top", "preview_clip_right", @@ -1070,6 +1320,56 @@ public void setDrawingRange(Rect rect) { } } + public int getPreviewQuality(String mimeType) { + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + return prof.getInteger("preview_jpeg_quality", 80); + } + return 80; + } + + public void setPreviewQuality(String mimeType, int quality) { + if (quality < 0) { + throw new IllegalArgumentException("quality is negative value."); + } + if (quality > 100) { + throw new IllegalArgumentException("quality is over 100."); + } + PropertyUtil prof = mPropertyMap.get(mimeType); + if (prof != null) { + prof.put("preview_jpeg_quality", quality); + } + } + + /** + * プレビューの品質を取得します. + * + * @return プレビューの品質 + */ + public int getPreviewQuality() { + return mProperty.getInteger("preview_jpeg_quality", 80); + } + + /** + * プレビューの品質を設定します. + * + * 0 から 100 の間で設定することができます。 + * それ以外は例外が発生します。 + * + * @param quality プレビューの品質 + */ + public void setPreviewQuality(int quality) { + if (quality < 0) { + throw new IllegalArgumentException("quality is negative value."); + } + if (quality > 100) { + throw new IllegalArgumentException("quality is over 100."); + } + mProperty.put("preview_jpeg_quality", quality); + } + + /// サポートしているデータサイズ + /** * サポートしている写真サイズを取得します. * @@ -1180,6 +1480,13 @@ public Long getMaxSensorFrameDuration() { return null; } + /** + * サポートしている色温度の範囲を取得します. + * + * サポートしていない場合は null を返却します。 + * + * @return 色温度の範囲 + */ public Range getSupportedWhiteBalanceTemperature() { return null; } @@ -1200,6 +1507,11 @@ public List getSupportedVideoEncoders() { return list; } + /** + * サポートしているプロファイル・レベルの一覧を取得します. + * + * @return サポートしているプロファイル・レベルの一覧 + */ public List getSupportedProfileLevel() { VideoEncoderName encoderName = getPreviewEncoderName(); return CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); @@ -1302,6 +1614,12 @@ public boolean isSupportedFps(Range fps) { return false; } + /** + * 指定されたフォーカスモードがサポートされているか確認します. + * + * @param mode 確認するフォーカスモード + * @return サポートされている場合はtrue、それ以外はfalse + */ public boolean isSupportedAutoFocusMode(int mode) { List modeList = getSupportedAutoFocusModeList(); if (modeList != null) { @@ -1513,7 +1831,7 @@ public boolean isAudioEnabled() { * @return 音声タイプ */ public AudioSource getPreviewAudioSource() { - return AudioSource.typeOf(mPref.getString("preview_audio_source", "none")); + return AudioSource.typeOf(mProperty.getString("preview_audio_source", "none")); } /** @@ -1523,9 +1841,9 @@ public AudioSource getPreviewAudioSource() { */ public void setPreviewAudioSource(AudioSource audioSource) { if (audioSource == null) { - mPref.put("preview_audio_source", "none"); + mProperty.put("preview_audio_source", "none"); } else { - mPref.put("preview_audio_source", audioSource.mSource); + mProperty.put("preview_audio_source", audioSource.mSource); } } @@ -1535,7 +1853,7 @@ public void setPreviewAudioSource(AudioSource audioSource) { * @return プレビュー音声のビットレート */ public int getPreviewAudioBitRate() { - return mPref.getInteger("preview_audio_bitrate", 64 * 1024); + return mProperty.getInteger("preview_audio_bitrate", 64 * 1024); } /** @@ -1547,7 +1865,7 @@ public void setPreviewAudioBitRate(int bitRate) { if (bitRate <= 0) { throw new IllegalArgumentException("previewAudioBitRate is zero or negative value."); } - mPref.put("preview_audio_bitrate", bitRate); + mProperty.put("preview_audio_bitrate", bitRate); } /** @@ -1556,7 +1874,7 @@ public void setPreviewAudioBitRate(int bitRate) { * @return プレビュー音声のサンプルレート */ public int getPreviewSampleRate() { - return mPref.getInteger("preview_audio_sample_rate", 16000); + return mProperty.getInteger("preview_audio_sample_rate", 16000); } /** @@ -1566,12 +1884,12 @@ public int getPreviewSampleRate() { */ public void setPreviewSampleRate(Integer sampleRate) { if (sampleRate == null) { - mPref.remove("preview_audio_sample_rate"); + mProperty.remove("preview_audio_sample_rate"); } else { if (!isSupportedSampleRate(sampleRate)) { throw new IllegalArgumentException("preivewSampleRate is invalid."); } - mPref.put("preview_audio_sample_rate", sampleRate); + mProperty.put("preview_audio_sample_rate", sampleRate); } } @@ -1581,7 +1899,7 @@ public void setPreviewSampleRate(Integer sampleRate) { * @return プレビュー音声のチャンネル数 */ public int getPreviewChannel() { - return mPref.getInteger("preview_audio_channel", 1); + return mProperty.getInteger("preview_audio_channel", 1); } /** @@ -1590,7 +1908,7 @@ public int getPreviewChannel() { * @param channel プレビュー音声のチャンネル数 */ public void setPreviewChannel(int channel) { - mPref.put("preview_audio_channel", channel); + mProperty.put("preview_audio_channel", channel); } /** @@ -1599,7 +1917,7 @@ public void setPreviewChannel(int channel) { * @return プレビュー配信のエコーキャンセラー */ public boolean isUseAEC() { - return mPref.getBoolean("preview_audio_aec", true); + return mProperty.getBoolean("preview_audio_aec", true); } /** @@ -1608,7 +1926,7 @@ public boolean isUseAEC() { * @param used プレビュー配信のエコーキャンセラー */ public void setUseAEC(boolean used) { - mPref.put("preview_audio_aec", used); + mProperty.put("preview_audio_aec", used); } /** @@ -1617,7 +1935,7 @@ public void setUseAEC(boolean used) { * @return ミュートの場合はtrue、それ以外の場合はfalse */ public boolean isMute() { - return mPref.getBoolean("preview_audio_mute", false); + return mProperty.getBoolean("preview_audio_mute", false); } /** @@ -1626,27 +1944,27 @@ public boolean isMute() { * @param mute ミュートにする場合はtrue、それ以外はfalse */ public void setMute(boolean mute) { - mPref.put("preview_audio_mute", mute); + mProperty.put("preview_audio_mute", mute); } public AudioFilter getAudioFilter() { - return AudioFilter.nameOf(mPref.getString("preview_audio_filter", "none")); + return AudioFilter.nameOf(mProperty.getString("preview_audio_filter", "none")); } public void setAudioFilter(AudioFilter filter) { if (filter == null) { - mPref.remove("preview_audio_filter"); + mProperty.remove("preview_audio_filter"); } else { - mPref.put("preview_audio_filter", filter.mName); + mProperty.put("preview_audio_filter", filter.mName); } } public float getAudioCoefficient() { - return mPref.getInteger("preview_audio_coefficient", 10) / 100.0f; + return mProperty.getInteger("preview_audio_coefficient", 10) / 100.0f; } public void setAudioCoefficient(float coefficient) { - mPref.put("preview_audio_coefficient", (int) (coefficient * 100)); + mProperty.put("preview_audio_coefficient", (int) (coefficient * 100)); } public boolean isSupportedAudioSource(AudioSource source) { @@ -1717,7 +2035,7 @@ public boolean isSupportedSampleRate(int sampleRate) { * @return 配信先の URI */ public String getBroadcastURI() { - return mPref.getString("broadcast_uri", null); + return mProperty.getString("broadcast_uri", null); } /** @@ -1726,7 +2044,7 @@ public String getBroadcastURI() { * @param broadcastURI 配信先の URI */ public void setBroadcastURI(String broadcastURI) { - mPref.put("broadcast_uri", broadcastURI); + mProperty.put("broadcast_uri", broadcastURI); } /** @@ -1735,7 +2053,7 @@ public void setBroadcastURI(String broadcastURI) { * @return リトライ回数 */ public int getRetryCount() { - return mPref.getInteger("broadcast_retry_count", 0); + return mProperty.getInteger("broadcast_retry_count", 0); } /** @@ -1745,9 +2063,9 @@ public int getRetryCount() { */ public void setRetryCount(int count) { if (count < 0) { - mPref.remove("broadcast_retry_count"); + mProperty.remove("broadcast_retry_count"); } else { - mPref.put("broadcast_retry_count", count); + mProperty.put("broadcast_retry_count", count); } } @@ -1757,7 +2075,7 @@ public void setRetryCount(int count) { * @return リトライのインターバル */ public int getRetryInterval() { - return mPref.getInteger("broadcast_retry_interval", 3000); + return mProperty.getInteger("broadcast_retry_interval", 3000); } /** @@ -1767,9 +2085,9 @@ public int getRetryInterval() { */ public void setRetryInterval(int interval) { if (interval < 0) { - mPref.remove("broadcast_retry_interval"); + mProperty.remove("broadcast_retry_interval"); } else { - mPref.put("broadcast_retry_interval", interval); + mProperty.put("broadcast_retry_interval", interval); } } @@ -1781,7 +2099,7 @@ public void setRetryInterval(int interval) { * @return Motion JPEG サーバ用のポート番号 */ public Integer getMjpegPort() { - return mPref.getInteger("mjpeg_port", 0); + return mProperty.getInteger("mjpeg_port", 0); } /** @@ -1790,7 +2108,7 @@ public Integer getMjpegPort() { * @param port Motion JPEG サーバ用のポート番号 */ public void setMjpegPort(int port) { - mPref.put("mjpeg_port", port); + mProperty.put("mjpeg_port", port); } /** @@ -1799,7 +2117,7 @@ public void setMjpegPort(int port) { * @return Motion JPEG サーバ用のポート番号 */ public Integer getMjpegSSLPort() { - return mPref.getInteger("mjpeg_ssl_port", 0); + return mProperty.getInteger("mjpeg_ssl_port", 0); } /** @@ -1808,7 +2126,7 @@ public Integer getMjpegSSLPort() { * @param port Motion JPEG サーバ用のポート番号 */ public void setMjpegSSLPort(int port) { - mPref.put("mjpeg_ssl_port", port); + mProperty.put("mjpeg_ssl_port", port); } /** @@ -1817,7 +2135,7 @@ public void setMjpegSSLPort(int port) { * @return RTSP サーバ用のポート番号 */ public Integer getRtspPort() { - return mPref.getInteger("rtsp_port", 0); + return mProperty.getInteger("rtsp_port", 0); } /** @@ -1826,7 +2144,7 @@ public Integer getRtspPort() { * @param port RTSP サーバ用のポート番号 */ public void setRtspPort(int port) { - mPref.put("rtsp_port", port); + mProperty.put("rtsp_port", port); } /** @@ -1835,7 +2153,7 @@ public void setRtspPort(int port) { * @return SRT サーバ用のポート番号 */ public Integer getSrtPort() { - return mPref.getInteger("srt_port", 0); + return mProperty.getInteger("srt_port", 0); } /** @@ -1844,7 +2162,7 @@ public Integer getSrtPort() { * @param port SRT サーバ用のポート番号 */ public void setSrtPort(int port) { - mPref.put("srt_port", port); + mProperty.put("srt_port", port); } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java index a124fae9e7..4340f54d68 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java @@ -18,9 +18,9 @@ * {@link SurfaceTexture} をもとに実装. */ class Camera2MJPEGPreviewServer extends AbstractMJPEGPreviewServer { - Camera2MJPEGPreviewServer(Context context, Camera2Recorder recorder, int port, boolean useSSL) { + Camera2MJPEGPreviewServer(Context context, Camera2Recorder recorder, boolean useSSL) { super(context, recorder, useSSL); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType()) + (useSSL ? 1 : 0)); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java index a54dae45a4..daff366917 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java @@ -37,12 +37,10 @@ class Camera2PreviewServerProvider extends AbstractPreviewServerProvider { mOverlayManager = new OverlayManager(context, recorder); - HostMediaRecorder.Settings settings = recorder.getSettings(); - - addServer(new Camera2MJPEGPreviewServer(context, recorder, settings.getMjpegPort(), false)); - addServer(new Camera2MJPEGPreviewServer(context, recorder, settings.getMjpegSSLPort(), true)); - addServer(new Camera2RTSPPreviewServer(context, recorder, settings.getRtspPort())); - addServer(new Camera2SRTPreviewServer(context, recorder, settings.getSrtPort())); + addServer(new Camera2MJPEGPreviewServer(context, recorder, false)); + addServer(new Camera2MJPEGPreviewServer(context, recorder, true)); + addServer(new Camera2RTSPPreviewServer(context, recorder)); + addServer(new Camera2SRTPreviewServer(context, recorder)); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java index 5b8f101439..300a4b01c7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java @@ -12,16 +12,16 @@ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) class Camera2RTSPPreviewServer extends AbstractRTSPPreviewServer { - Camera2RTSPPreviewServer(Context context, Camera2Recorder recorder, int port) { + Camera2RTSPPreviewServer(Context context, Camera2Recorder recorder) { super(context, recorder); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType())); } @Override protected VideoStream createVideoStream() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName()) { + switch (settings.getPreviewEncoderName(getMimeType())) { case H264: default: return new CameraH264VideoStream(recorder, 5006); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java index 126d6f4037..3195ed0a11 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java @@ -7,16 +7,16 @@ import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class Camera2SRTPreviewServer extends AbstractSRTPreviewServer { - Camera2SRTPreviewServer(final Context context, final Camera2Recorder recorder, final int port) { + Camera2SRTPreviewServer(final Context context, final Camera2Recorder recorder) { super(context, recorder); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType())); } @Override protected VideoEncoder createVideoEncoder() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName()) { + switch (settings.getPreviewEncoderName(getMimeType())) { case H264: default: return new CameraVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraMJPEGEncoder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraMJPEGEncoder.java index 37678b9441..7a7adf0059 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraMJPEGEncoder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraMJPEGEncoder.java @@ -3,26 +3,7 @@ import org.deviceconnect.android.libmedia.streaming.mjpeg.SurfaceMJPEGEncoder; public class CameraMJPEGEncoder extends SurfaceMJPEGEncoder { - CameraMJPEGEncoder(Camera2Recorder recorder) { super(recorder.getSurfaceDrawingThread()); } - - // SurfaceMJPEGEncoder - - @Override - protected void prepare() { - } - - @Override - protected void startRecording() { - } - - @Override - protected void stopRecording() { - } - - @Override - protected void release() { - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java index 41fbcc5f03..e5e76aff12 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java @@ -7,6 +7,7 @@ import android.view.WindowManager; import org.deviceconnect.android.deviceplugin.host.camera.CameraWrapper; +import org.deviceconnect.android.deviceplugin.host.camera.CameraWrapperException; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; import org.deviceconnect.android.libmedia.streaming.gles.SurfaceTextureManager; @@ -59,12 +60,20 @@ public boolean isSwappedDimensions() { @Override protected void onStarted() { - startCamera(getSurfaceTexture()); + try { + startCamera(getSurfaceTexture()); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Override protected void onStopped() { - stopCamera(); + try { + stopCamera(); + } catch (Exception e) { + // ignore. + } } @Override @@ -87,36 +96,28 @@ protected SurfaceTextureManager createStManager() { return manager; } - private void startCamera(SurfaceTexture surfaceTexture) { - try { - HostMediaRecorder.Settings settings = mRecorder.getSettings(); - CameraWrapper cameraWrapper = mRecorder.getCameraWrapper(); - cameraWrapper.getOptions().setPictureSize(settings.getPictureSize()); - cameraWrapper.getOptions().setPreviewSize(settings.getPreviewSize()); - cameraWrapper.getOptions().setFps(settings.getPreviewMaxFrameRate()); - cameraWrapper.getOptions().setAutoFocusMode(settings.getPreviewAutoFocusMode()); - cameraWrapper.getOptions().setAutoWhiteBalanceMode(settings.getPreviewWhiteBalance()); - cameraWrapper.getOptions().setWhiteBalanceTemperature(settings.getPreviewWhiteBalanceTemperature()); - cameraWrapper.getOptions().setAutoExposureMode(settings.getAutoExposureMode()); - cameraWrapper.getOptions().setSensorExposureTime(settings.getSensorExposureTime()); - cameraWrapper.getOptions().setSensorSensitivity(settings.getSensorSensitivity()); - cameraWrapper.getOptions().setSensorFrameDuration(settings.getSensorFrameDuration()); - cameraWrapper.getOptions().setStabilizationMode(settings.getStabilizationMode()); - cameraWrapper.getOptions().setOpticalStabilizationMode(settings.getOpticalStabilizationMode()); - cameraWrapper.getOptions().setDigitalZoom(settings.getDigitalZoom()); - cameraWrapper.getOptions().setNoiseReductionMode(settings.getNoiseReduction()); - cameraWrapper.getOptions().setFocalLength(settings.getFocalLength()); - cameraWrapper.startPreview(new Surface(surfaceTexture)); - } catch (Exception e) { - throw new RuntimeException(e); - } + private void startCamera(SurfaceTexture surfaceTexture) throws CameraWrapperException { + HostMediaRecorder.Settings settings = mRecorder.getSettings(); + CameraWrapper cameraWrapper = mRecorder.getCameraWrapper(); + cameraWrapper.getOptions().setPictureSize(settings.getPictureSize()); + cameraWrapper.getOptions().setPreviewSize(settings.getPreviewSize()); + cameraWrapper.getOptions().setFps(settings.getPreviewMaxFrameRate()); + cameraWrapper.getOptions().setAutoFocusMode(settings.getPreviewAutoFocusMode()); + cameraWrapper.getOptions().setAutoWhiteBalanceMode(settings.getPreviewWhiteBalance()); + cameraWrapper.getOptions().setWhiteBalanceTemperature(settings.getPreviewWhiteBalanceTemperature()); + cameraWrapper.getOptions().setAutoExposureMode(settings.getAutoExposureMode()); + cameraWrapper.getOptions().setSensorExposureTime(settings.getSensorExposureTime()); + cameraWrapper.getOptions().setSensorSensitivity(settings.getSensorSensitivity()); + cameraWrapper.getOptions().setSensorFrameDuration(settings.getSensorFrameDuration()); + cameraWrapper.getOptions().setStabilizationMode(settings.getStabilizationMode()); + cameraWrapper.getOptions().setOpticalStabilizationMode(settings.getOpticalStabilizationMode()); + cameraWrapper.getOptions().setDigitalZoom(settings.getDigitalZoom()); + cameraWrapper.getOptions().setNoiseReductionMode(settings.getNoiseReduction()); + cameraWrapper.getOptions().setFocalLength(settings.getFocalLength()); + cameraWrapper.startPreview(new Surface(surfaceTexture)); } private void stopCamera() { - try { - mRecorder.getCameraWrapper().stopPreview(); - } catch (Exception e) { - // ignore. - } + mRecorder.getCameraWrapper().stopPreview(); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java index c39c186efd..cf4ca573d0 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java @@ -28,14 +28,4 @@ public CameraVideoEncoder(Camera2Recorder recorder, String mimeType) { public VideoQuality getVideoQuality() { return mVideoQuality; } - - // SurfaceVideoEncoder - - @Override - protected void onStartSurfaceDrawing() { - } - - @Override - protected void onStopSurfaceDrawing() { - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java index 0d454615dc..7855cc5816 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java @@ -1,9 +1,12 @@ package org.deviceconnect.android.deviceplugin.host.recorder.ui; +import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.Size; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; @@ -12,6 +15,13 @@ import org.deviceconnect.android.deviceplugin.host.R; public class PreviewSurfaceView extends FrameLayout { + private Rect mDrawingRect; + private int mPreviewWidth; + private int mPreviewHeight; + private int mSurfaceWidth; + private int mSurfaceHeight; + private int mDragStartX; + private int mDragStartY; public PreviewSurfaceView(Context context) { super(context); @@ -27,21 +37,130 @@ public PreviewSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) initView(context, attrs, defStyleAttr); } + @SuppressLint("ClickableViewAccessibility") private void initView(Context context, AttributeSet attrs, int defStyle) { LayoutInflater.from(context).inflate(R.layout.host_preview_surface_view, this); + + SurfaceView surfaceView = getSurfaceView(); + surfaceView.setOnTouchListener((v, event) -> { + Rect drawingRect = getDrawingRect(); + if (drawingRect == null) { + return false; + } + + float scale = getScale(); + int x = (int) event.getRawX(); + int y = (int) event.getRawY(); + int orgX = (int) (x / scale); + int orgY = (int) (y / scale); + + switch(event.getAction()) { + case MotionEvent.ACTION_DOWN: + if (drawingRect.contains(orgX, orgY)) { + mDragStartX = x; + mDragStartY = y; + } else { + return false; + } + break; + + case MotionEvent.ACTION_MOVE: + int diffX = (int) ((x - mDragStartX) / scale); + int diffY = (int) ((y - mDragStartY) / scale); + + int newLeft = drawingRect.left + diffX; + int newRight = drawingRect.right + diffX; + int newTop = drawingRect.top + diffY; + int newBottom = drawingRect.bottom + diffY; + + if (newLeft < 0) { + diffX = -drawingRect.left; + } + + if (newRight >= mPreviewWidth) { + diffX = mPreviewWidth - drawingRect.right; + } + + if (newTop < 0) { + diffY = -drawingRect.top; + } + + if (newBottom >= mPreviewHeight) { + diffY = mPreviewHeight - drawingRect.bottom; + } + + drawingRect.offset(diffX, diffY); + mDragStartX = x; + mDragStartY = y; + setDrawingRange(drawingRect); + break; + + case MotionEvent.ACTION_UP: + break; + } + return true; + }); + } + + public float getScale() { + if (mPreviewHeight == 0) { + return 1.0f; + } else { + return mSurfaceHeight / (float) mPreviewHeight; + } + } + + public SurfaceView getSurfaceView() { + View root = findViewById(R.id.preview_root); + if (root != null) { + return root.findViewById(R.id.preview_surface_view); + } + return null; + } + + public void setDrawingRange(Rect drawingRange) { + mDrawingRect = drawingRange; + setDrawingRangeView(drawingRange); + } + + public Rect getDrawingRect() { + return mDrawingRect; + } + + private void setDrawingRangeView(Rect drawingRange) { + View root = findViewById(R.id.preview_root); + View frameView = root.findViewById(R.id.preview_drawing_range); + if (drawingRange == null || mPreviewHeight == 0) { + frameView.setVisibility(GONE); + } else { + float scale = getScale(); + int left = (int) (drawingRange.left * scale); + int top = (int) (drawingRange.top * scale); + ViewGroup.LayoutParams layoutParams = frameView.getLayoutParams(); + layoutParams.width = (int) (drawingRange.width() * scale); + layoutParams.height = (int) (drawingRange.height() * scale); + MarginLayoutParams mlp = (MarginLayoutParams) layoutParams; + mlp.setMargins(left, top, 0, 0); + frameView.setLayoutParams(layoutParams); + frameView.setVisibility(VISIBLE); + } } + /** + * アスペクトを保持したまま画面全体にプレビューを表示するように調整します. + * + * @param previewWidth プレビューの横幅 + * @param previewHeight プレビューの縦幅 + */ public void fullSurfaceView(int previewWidth, int previewHeight) { post(() -> { int gcd = calculatedGcd(previewWidth, previewHeight); View root = findViewById(R.id.preview_root); SurfaceView surfaceView = root.findViewById(R.id.preview_surface_view); - int cameraWidth = previewWidth; - int cameraHeight = previewHeight; - int widthRatio = cameraWidth / gcd; - int heightRatio = cameraHeight / gcd; + int widthRatio = previewWidth / gcd; + int heightRatio = previewHeight / gcd; int largeRate = widthRatio; if (widthRatio < heightRatio) { @@ -55,14 +174,18 @@ public void fullSurfaceView(int previewWidth, int previewHeight) { int previewGcd = (int) Math.ceil(largeSize / (double) largeRate); - int width = widthRatio * previewGcd; - int height = heightRatio * previewGcd; + mPreviewWidth = previewWidth; + mPreviewHeight = previewHeight; + mSurfaceWidth = widthRatio * previewGcd; + mSurfaceHeight = heightRatio * previewGcd; ViewGroup.LayoutParams layoutParams = surfaceView.getLayoutParams(); - layoutParams.width = width; - layoutParams.height = height; + layoutParams.width = mSurfaceWidth; + layoutParams.height = mSurfaceHeight; surfaceView.setLayoutParams(layoutParams); surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); + + setDrawingRangeView(mDrawingRect); }); } @@ -85,9 +208,22 @@ public void adjustSurfaceView(int previewWidth, int previewHeight) { layoutParams.height = changeSize.getHeight(); surfaceView.setLayoutParams(layoutParams); surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); + + mPreviewWidth = previewWidth; + mPreviewHeight = previewHeight; + mSurfaceWidth = changeSize.getWidth(); + mSurfaceHeight = changeSize.getHeight(); + + setDrawingRangeView(mDrawingRect); }); } + /** + * a と b の最大公約数を計算します. + * @param a 値1 + * @param b 値2 + * @return a と b の最大公約数 + */ private int calculatedGcd(int a, int b) { if (b == 0) { return a; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/drawable/border_red.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/drawable/border_red.xml new file mode 100644 index 0000000000..3ca4e29a7a --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/drawable/border_red.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml index 65fab07454..89b048aa11 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml @@ -1,6 +1,7 @@ + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml index 8b939a19b3..cd4a992175 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml @@ -24,6 +24,22 @@ app:popEnterAnim="@anim/nav_slide_pop_enter_anim" app:popExitAnim="@anim/nav_slide_pop_exit_anim"/> + + + + + + + + + + + + diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml index c622b32663..c1c9799050 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml @@ -79,8 +79,11 @@ 映像設定 音声設定 配信設定 - SRT 設定 + MJPEG + RTSP + SRT ポート設定 + サーバ設定 静止画解像度 @@ -142,6 +145,7 @@ RTSP 設定 SRT 設定 ポート番号 + URL 自動 diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml index 8526b2814f..69f7b85bc8 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml @@ -78,8 +78,11 @@ Video Settings Audio Settings Broadcast Settings + MJPEG Settings + RTSP Settings SRT Settings Port Settings + Server Settings Still Image Resolution Preview Resolution @@ -136,6 +139,7 @@ RTSP Settings SRT Settings Port + URL SRTO_PEERLATENCY SRTO_LOSSMAXTTL diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml index 3534a97906..81bc1c0651 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml @@ -29,6 +29,105 @@ app:iconSpaceReserved="false" app:key="broadcast_retry_interval" app:useSimpleSummaryProvider="true" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml index ee025e7077..efe7409c32 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml @@ -21,15 +21,27 @@ app:key="recorder_settings_broadcast" app:title="@string/host_recorder_settings_broadcast" /> + + + + + app:key="recorder_settings_mjpeg" + app:title="@string/host_recorder_settings_mjpeg" /> + app:key="recorder_settings_rtsp" + app:title="@string/host_recorder_settings_rtsp" /> + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml new file mode 100644 index 0000000000..77d7f0fed8 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml new file mode 100644 index 0000000000..3466103dea --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml index d4c8e7d198..7d20c726cb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml @@ -2,57 +2,183 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml index 40d901bffd..b0a8c4fa25 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml @@ -18,57 +18,6 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml index 289e4e954d..8706604fb7 100644 --- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml +++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml @@ -8,11 +8,7 @@ - + + + + + + diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java index 3d2c898935..c739640f72 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java @@ -2,6 +2,7 @@ import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.util.Log; import android.view.Surface; import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java index e33dc6e41e..1066f92b68 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java @@ -294,10 +294,24 @@ public enum BitRateMode { CQ } + /** + * 描画範囲を取得します. + * + * 描画範囲指定されていない場合は null を返却します。 + * + * @return 描画範囲 + */ public Rect getDrawingRange() { return mDrawingRange; } + /** + * 描画範囲を設定します. + * + * null が指定された場合には、描画範囲を削除します。 + * + * @param drawingRange 描画範囲 + */ public void setDrawingRange(Rect drawingRange) { mDrawingRange = drawingRange; } From db7197b69c701bd44e7bb6406b623e0580826449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Fri, 20 Aug 2021 17:41:31 +0900 Subject: [PATCH 03/40] =?UTF-8?q?=E3=83=97=E3=83=A9=E3=82=B0=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E6=8E=A5=E7=B6=9A=E3=82=BF=E3=82=A4=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/manager/core/DConnectCore.java | 4 - .../manager/core/plugin/BinderConnection.java | 29 ++--- .../manager/core/plugin/DevicePlugin.java | 9 +- .../core/plugin/DevicePluginManager.java | 104 +++++++++--------- 4 files changed, 63 insertions(+), 83 deletions(-) diff --git a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/DConnectCore.java b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/DConnectCore.java index 6e671d8f9a..e55192ce54 100644 --- a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/DConnectCore.java +++ b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/DConnectCore.java @@ -181,10 +181,6 @@ public void sendMessage(final Intent message) { mPluginManager.addEventListener(new DevicePluginManager.DevicePluginEventListener() { @Override public void onDeviceFound(final DevicePlugin plugin) { - if (mSettings.isRegisterNetworkServiceDiscovery()) { - // 見つけたプラグインを有効にする - plugin.apply(); - } getServiceProvider().addService(plugin); } diff --git a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/BinderConnection.java b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/BinderConnection.java index 6d094a7310..c2a58e2119 100755 --- a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/BinderConnection.java +++ b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/BinderConnection.java @@ -41,9 +41,9 @@ public class BinderConnection extends AbstractConnection { private Future mRunningTask; - private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - private Logger mLogger = Logger.getLogger("dconnect.manager"); + private final Logger mLogger = Logger.getLogger("dconnect.manager"); public BinderConnection(final Context context, final String pluginId, @@ -61,10 +61,10 @@ public ConnectionType getType() { @Override public synchronized void connect() throws ConnectingException { - if (BuildConfig.DEBUG) { - mLogger.info("BinderConnection.connect: " + mPluginName.getPackageName()); - } if (!(ConnectionState.DISCONNECTED == getState() || ConnectionState.SUSPENDED == getState())) { + if (BuildConfig.DEBUG) { + mLogger.info("BinderConnection.connect: state is already connected. plugin=" + mPluginName); + } return; } setConnectingState(); @@ -110,9 +110,6 @@ public void disconnect() { @Override public void send(final Intent message) throws MessagingException { - if (BuildConfig.DEBUG) { - mLogger.info("BinderConnection.send: sending: target = " + mPluginName.getPackageName()); - } synchronized (this) { if (ConnectionState.SUSPENDED == getState()) { throw new MessagingException(MessagingException.Reason.CONNECTION_SUSPENDED); @@ -123,22 +120,14 @@ public void send(final Intent message) throws MessagingException { } try { mPlugin.sendMessage(message); - if (BuildConfig.DEBUG) { - mLogger.info("BinderConnection.send: sent: target = " + mPluginName.getPackageName()); - } } catch (RemoteException e) { throw new MessagingException(e, MessagingException.Reason.NOT_CONNECTED); } } private class ConnectingTask implements Callable { - @Override public ConnectingResult call() throws Exception { - if (BuildConfig.DEBUG) { - mLogger.info("ConnectingTask.call: " + mPluginName); - } - final Object lockObj = new Object(); final ConnectingResult result = new ConnectingResult(); @@ -148,7 +137,9 @@ public ConnectingResult call() throws Exception { @Override public void onServiceConnected(final ComponentName componentName, final IBinder binder) { if (BuildConfig.DEBUG) { - mLogger.info("onServiceConnected: componentName = " + componentName + ", binder = " + binder); + mLogger.info("BinderConnection.onServiceConnected: \n" + + " componentName = " + componentName + "\n" + + " binder = " + binder); } try { IDConnectPlugin plugin = IDConnectPlugin.Stub.asInterface(binder); @@ -160,7 +151,6 @@ public void onServiceConnected(final ComponentName componentName, final IBinder result.mServiceConnection = this; lockObj.notifyAll(); } - } catch (RemoteException e) { e.printStackTrace(); // TODO エラーハンドリング } @@ -169,7 +159,8 @@ public void onServiceConnected(final ComponentName componentName, final IBinder @Override public void onServiceDisconnected(final ComponentName componentName) { if (BuildConfig.DEBUG) { - mLogger.info("onServiceDisconnected: componentName = " + componentName); + mLogger.info("BinderConnection.onServiceDisconnected: " + + " componentName = " + componentName); } synchronized (BinderConnection.this) { mServiceConnection = null; diff --git a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePlugin.java b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePlugin.java index b51af2f1c2..4bb11c967e 100755 --- a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePlugin.java +++ b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePlugin.java @@ -191,9 +191,9 @@ public void reportRoundTrip(final Intent request, final long start, final long e // 統計を取る. if (calculatesStats()) { if (BuildConfig.DEBUG) { - mLogger.info("Plug-in PackageName: " + getPackageName()); - mLogger.info("Request: " + DConnectUtil.convertRequestToString(request)); - mLogger.info("ResponseTime: " + (end - start)); + mLogger.info("Plug-in PackageName: " + getPackageName() + "\n" + + " Request: " + DConnectUtil.convertRequestToString(request) + "\n" + + " ResponseTime: " + (end - start)); } long baudRate = info.getRoundTripTime(); @@ -448,9 +448,6 @@ private boolean tryConnection() { for (int cnt = 0; cnt < MAX_CONNECTION_TRY; cnt++) { try { mConnection.connect(); - if (BuildConfig.DEBUG) { - mLogger.info("Connected to the plug-in: " + getPackageName()); - } return true; } catch (ConnectingException e) { mLogger.log(Level.WARNING, "Failed to connect to the plug-in: " + getPackageName(), e); diff --git a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePluginManager.java b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePluginManager.java index b266ebd7cc..c195ee6fe9 100755 --- a/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePluginManager.java +++ b/dConnectManager/dConnectManager/dconnect-manager-core/src/main/java/org/deviceconnect/android/manager/core/plugin/DevicePluginManager.java @@ -94,10 +94,8 @@ public class DevicePluginManager { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - mLogger.info("PluginManager: Received: action=" + action); if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { String packageName = getPackageName(intent); - mLogger.info("PluginManager: added package: name=" + packageName); if (packageName != null) { checkAndAddDevicePlugin(packageName); } @@ -112,9 +110,11 @@ public void onReceive(Context context, Intent intent) { private String getPackageName(final Intent intent) { String pkgName = intent.getDataString(); - int idx = pkgName.indexOf(":"); - if (idx != -1) { - pkgName = pkgName.substring(idx + 1); + if (pkgName != null) { + int idx = pkgName.indexOf(":"); + if (idx != -1) { + pkgName = pkgName.substring(idx + 1); + } } return pkgName; } @@ -137,7 +137,7 @@ private String getPackageName(final Intent intent) { /** * イベントを通知するスレッド. */ - private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); /** * コンテキスト. @@ -152,13 +152,10 @@ private String getPackageName(final Intent intent) { /** * 接続管理用インスタンスのイベントリスナー. */ - private final ConnectionStateListener mStateListener = new ConnectionStateListener() { - @Override - public void onConnectionStateChanged(final String pluginId, final ConnectionState state) { - DevicePlugin plugin = mPlugins.get(pluginId); - if (plugin != null) { - notifyStateChange(plugin, state); - } + private final ConnectionStateListener mStateListener = (pluginId, state) -> { + DevicePlugin plugin = mPlugins.get(pluginId); + if (plugin != null) { + notifyStateChange(plugin, state); } }; @@ -185,10 +182,10 @@ public void startMonitoring() { packageFilter.addDataScheme("package"); try { mContext.registerReceiver(mPackageReceiver, packageFilter); - mLogger.info("PluginManager: Started plugin monitoring."); } catch (Exception e) { - // ignore. - mLogger.severe("PluginManager: Failed to start plugin monitoring: " + e.getMessage()); + if (BuildConfig.DEBUG) { + mLogger.severe("PluginManager: Failed to start plugin monitoring: " + e.getMessage()); + } } } @@ -198,12 +195,11 @@ public void startMonitoring() { public void stopMonitoring() { try { mContext.unregisterReceiver(mPackageReceiver); - mLogger.info("PluginManager: Stopped plugin monitoring."); } catch (Exception e) { - // ignore. - mLogger.severe("PluginManager: Failed to stop plugin monitoring: " + e.getMessage()); + if (BuildConfig.DEBUG) { + mLogger.severe("PluginManager: Failed to stop plugin monitoring: " + e.getMessage()); + } } - } /** @@ -244,12 +240,13 @@ public void addDevicePlugin(final DevicePlugin plugin) { if (mConnectionFactory != null) { plugin.setConnection(mConnectionFactory.createConnectionForPlugin(plugin)); plugin.addConnectionStateListener(mStateListener); - mLogger.info("PluginManager: created connection to plugin: package=" + plugin.getPackageName()); } else { - mLogger.info("PluginManager: No connection factory: package=" + plugin.getPackageName()); + if (BuildConfig.DEBUG) { + mLogger.info("PluginManager: No connection factory: package=" + plugin.getPackageName()); + } } mPlugins.put(plugin.getPluginId(), plugin); - mLogger.info("PluginManager: added to plugin list: package=" + plugin.getPackageName() + ", ID=" + plugin.getPluginId()); + plugin.apply(); notifyFound(plugin); } @@ -266,7 +263,7 @@ public void createDevicePluginList() throws PluginDetectionException { allPlugins = getInstalledPlugins(pkgMgr); } catch (Exception e) { PluginDetectionException.Reason reason; - if (Build.VERSION.SDK_INT >= 15) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { if (e.getClass() == TransactionTooLargeException.class) { reason = PluginDetectionException.Reason.TOO_MANY_PACKAGES; } else { @@ -425,10 +422,8 @@ private void checkAndAddDevicePlugin(final String packageName) { try { int flag = PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS; PackageInfo pkg = pkgMgr.getPackageInfo(packageName, flag); - mLogger.info("PluginManager: get package info: " + pkg); if (pkg != null) { List plugins = getInstalledPluginsForPackage(pkgMgr, pkg); - mLogger.info("PluginManager: installed plugins: size=" + plugins.size()); for (DevicePlugin plugin : filterPlugin(plugins)) { addDevicePlugin(plugin); } @@ -461,25 +456,24 @@ private List filterPlugin(final List plugins) { for (int index = 0; index < array.size(); index++) { List list = array.valueAt(index); if (list != null && list.size() > 0) { - Collections.sort(list, new Comparator() { - @Override - public int compare(final DevicePlugin p1, final DevicePlugin p2) { - // BroadcastよりもBinderを優先する. - if (p1.getConnectionType() == p2.getConnectionType()) { - return 0; - } else if (p1.getConnectionType() == ConnectionType.BINDER) { - return -1; - } else { - return 1; - } - } - }); + Collections.sort(list, COMPARATOR); result.add(list.get(0)); } } return result; } + private static final Comparator COMPARATOR = (Comparator) (p1, p2) -> { + // BroadcastよりもBinderを優先する. + if (p1.getConnectionType() == p2.getConnectionType()) { + return 0; + } else if (p1.getConnectionType() == ConnectionType.BINDER) { + return -1; + } else { + return 1; + } + }; + /** * 指定されたコンポーネントの ServiceInfo を取得します. *

@@ -567,17 +561,17 @@ private DevicePlugin parsePlugin(final PackageInfo pkgInfo, final ComponentInfo Integer iconId = (Integer) metaData.get(PLUGIN_META_PLUGIN_ICON); if (BuildConfig.DEBUG) { - mLogger.info("Added DevicePlugin: [" + hash + "]"); - mLogger.info(" PackageName: " + packageName); - mLogger.info(" className: " + className); - mLogger.info(" versionName: " + versionName); - mLogger.info(" sdkVersionName: " + sdkVersionName); - } - - // MEMO 既に同じ名前のデバイスプラグインが存在した場合の処理 - // 現在は警告を表示し、上書きする. - if (mPlugins.containsKey(hash)) { - mLogger.warning("DevicePlugin[" + hash + "] already exists."); + mLogger.info("Added DevicePlugin: [" + hash + "]\n" + + " PackageName: " + packageName + "\n" + + " className: " + className + "\n" + + " versionName: " + versionName + "\n" + + " sdkVersionName: " + sdkVersionName); + + // MEMO 既に同じ名前のデバイスプラグインが存在した場合の処理 + // 現在は警告を表示し、上書きする. + if (mPlugins.containsKey(hash)) { + mLogger.warning("DevicePlugin[" + hash + "] already exists."); + } } ConnectionType type; @@ -779,10 +773,12 @@ public void appendPluginIdToSessionKey(final Intent request, final DevicePlugin */ public void splitPluginIdToServiceId(final Intent request) { String serviceId = request.getStringExtra(DConnectMessage.EXTRA_SERVICE_ID); - List plugins = getDevicePlugins(serviceId); - // 各デバイスプラグインへ渡すサービスIDを作成 - String id = DevicePluginManager.splitServiceId(plugins.get(0), serviceId); - request.putExtra(IntentDConnectMessage.EXTRA_SERVICE_ID, id); + if (serviceId != null) { + List plugins = getDevicePlugins(serviceId); + // 各デバイスプラグインへ渡すサービスIDを作成 + String id = DevicePluginManager.splitServiceId(plugins.get(0), serviceId); + request.putExtra(IntentDConnectMessage.EXTRA_SERVICE_ID, id); + } } /** From a15f79b5fff23f3ee8ba8d15af88ed891f328f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Fri, 20 Aug 2021 17:41:57 +0900 Subject: [PATCH 04/40] =?UTF-8?q?MJPEG=E3=81=AE=E9=80=81=E4=BF=A1=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=9F=E3=83=B3=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recorder/AbstractMJPEGPreviewServer.java | 1 + .../res/xml/settings_host_recorder_mjpeg.xml | 6 ++++ .../streaming/mjpeg/MJPEGQuality.java | 31 +++++++++++++++++++ .../streaming/mjpeg/SurfaceMJPEGEncoder.java | 11 +++++++ 4 files changed, 49 insertions(+) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index 16f5fb6db3..d45025f430 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -140,6 +140,7 @@ private void setMJPEGQuality(MJPEGQuality quality) { quality.setWidth(w); quality.setHeight(h); quality.setQuality(settings.getPreviewQuality(getMimeType())); + quality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); quality.setDrawingRange(settings.getDrawingRange(getMimeType())); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml index 77d7f0fed8..153d924049 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml @@ -35,6 +35,12 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + Date: Mon, 23 Aug 2021 17:22:25 +0900 Subject: [PATCH 05/40] =?UTF-8?q?MJPEG=20=E3=81=AE=E3=83=95=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=83=AC=E3=83=BC=E3=83=88=E3=81=AE=E5=80=A4?= =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=82=92=E6=95=B0=E5=80=A4=E3=81=AE=E3=81=BF?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/activity/recorder/settings/SettingsMJPEGFragment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java index cec5e4a22c..dc540f266d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -45,6 +45,8 @@ public void onBindService() { setPreviewJpegQuality(); setPreviewCutOutReset(); + setInputTypeNumber("preview_framerate"); + setPreviewClipPreference("preview_clip_left"); setPreviewClipPreference("preview_clip_top"); setPreviewClipPreference("preview_clip_right"); From 024ae88f48ff422fc86b8ebdb08d1b4babf5b4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 23 Aug 2021 17:23:53 +0900 Subject: [PATCH 06/40] =?UTF-8?q?=E9=9F=B3=E5=A3=B0=E7=94=A8=E3=81=AE?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=83=AA=E3=83=BC=E3=83=9F=E3=83=B3=E3=82=B0?= =?UTF-8?q?=E7=94=A8=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/recorder/audio/AudioPreviewServerProvider.java | 6 ++---- .../host/recorder/audio/AudioRTSPPreviewServer.java | 4 ++-- .../host/recorder/audio/AudioSRTPreviewServer.java | 5 +++-- .../host/recorder/audio/HostAudioRecorder.java | 8 +++----- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java index 5c39dc7213..1a9c7a5207 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java @@ -15,9 +15,7 @@ public class AudioPreviewServerProvider extends AbstractPreviewServerProvider { public AudioPreviewServerProvider(Context context, HostMediaRecorder recorder) { super(context, recorder); - HostMediaRecorder.Settings settings = recorder.getSettings(); - - addServer(new AudioRTSPPreviewServer(context, recorder, settings.getRtspPort())); - addServer(new AudioSRTPreviewServer(context, recorder, settings.getSrtPort())); + addServer(new AudioRTSPPreviewServer(context, recorder)); + addServer(new AudioSRTPreviewServer(context, recorder)); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java index 60d3b6880f..47a269b5d4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java @@ -6,8 +6,8 @@ import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class AudioRTSPPreviewServer extends AbstractRTSPPreviewServer { - AudioRTSPPreviewServer(Context context, HostMediaRecorder recorder, int port) { + AudioRTSPPreviewServer(Context context, HostMediaRecorder recorder) { super(context, recorder); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType())); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java index eecc2911ef..5c62af7c73 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java @@ -1,13 +1,14 @@ package org.deviceconnect.android.deviceplugin.host.recorder.audio; import android.content.Context; +import android.util.Log; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class AudioSRTPreviewServer extends AbstractSRTPreviewServer { - AudioSRTPreviewServer(final Context context, final HostMediaRecorder recorder, final int port) { + AudioSRTPreviewServer(final Context context, final HostMediaRecorder recorder) { super(context, recorder); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType())); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java index 0127b62c19..a97d1ab3da 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java @@ -9,6 +9,7 @@ import android.Manifest; import android.content.Context; import android.os.Handler; +import android.util.Log; import android.util.Size; import androidx.annotation.NonNull; @@ -79,14 +80,11 @@ private void initSettings() { mSettings.setPreviewKeyFrameInterval(1); mSettings.setPreviewAudioSource(AudioSource.DEFAULT); - mSettings.setPreviewAudioBitRate(64 * 1024); - mSettings.setPreviewSampleRate(16000); + mSettings.setPreviewAudioBitRate(128 * 1024); + mSettings.setPreviewSampleRate(48000); mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); - mSettings.setRtspPort(32000); - mSettings.setSrtPort(33000); - mSettings.finishInitialization(); } } From b1cb435b070fe3e5efc6aed49eadf01e20d1af68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 23 Aug 2021 17:24:12 +0900 Subject: [PATCH 07/40] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=92=E5=87=BA=E5=8A=9B=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/libmedia/streaming/muxer/RtmpMuxer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/muxer/RtmpMuxer.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/muxer/RtmpMuxer.java index 24ae37cb43..352b53b681 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/muxer/RtmpMuxer.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/muxer/RtmpMuxer.java @@ -113,6 +113,7 @@ public void onAuthErrorRtmp() { }; mSrsFlvMuxer = new SrsFlvMuxer(rtmpAdapter); + mSrsFlvMuxer.setLogs(false); if (videoQuality != null) { mSrsFlvMuxer.setVideoResolution(videoQuality.getVideoWidth(), videoQuality.getVideoHeight()); } From d9508aa82abfc3e40c35457b422e78e85b638a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 23 Aug 2021 17:26:09 +0900 Subject: [PATCH 08/40] =?UTF-8?q?=E3=83=9E=E3=82=A4=E3=83=A0=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=97=E3=81=94=E3=81=A8=E3=81=AB=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/recorder/AbstractBroadcaster.java | 1 + .../host/recorder/AbstractMediaRecorder.java | 18 +++ .../host/recorder/HostMediaRecorder.java | 103 ++++-------------- .../host/recorder/camera/Camera2Recorder.java | 29 ++++- .../screen/ScreenCastMJPEGEncoder.java | 17 --- .../screen/ScreenCastMJPEGPreviewServer.java | 4 +- .../ScreenCastPreviewServerProvider.java | 11 +- .../screen/ScreenCastRTSPPreviewServer.java | 6 +- .../recorder/screen/ScreenCastRecorder.java | 30 ++++- .../screen/ScreenCastSRTBroadcaster.java | 2 +- .../screen/ScreenCastSRTPreviewServer.java | 6 +- .../screen/ScreenCastVideoEncoder.java | 10 -- .../app/src/main/res/values-ja/strings.xml | 1 + .../app/src/main/res/values/strings.xml | 7 +- .../res/xml/settings_host_recorder_main.xml | 16 ++- 15 files changed, 116 insertions(+), 145 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java index 56969828d8..4c84cf06a4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java @@ -117,6 +117,7 @@ public void setVideoQuality(VideoQuality videoQuality) { videoQuality.setLevel(settings.getLevel(getMimeType())); if (settings.getPreviewBitRateMode(getMimeType()) != null) { switch (settings.getPreviewBitRateMode(getMimeType())) { + default: case VBR: videoQuality.setBitRateMode(VideoQuality.BitRateMode.VBR); break; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java index dcc562618b..def4ecb0bf 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java @@ -25,6 +25,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaSharing; import org.deviceconnect.android.deviceplugin.host.recorder.util.SurfaceMP4Recorder; +import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; import org.deviceconnect.android.provider.FileManager; import java.io.File; @@ -44,6 +45,11 @@ public abstract class AbstractMediaRecorder implements HostMediaRecorder { */ private static final boolean DEBUG = BuildConfig.DEBUG; + public static final String MIME_TYPE_MJPEG = "video/x-mjpeg"; + public static final String MIME_TYPE_RTSP = "video/x-rtp"; + public static final String MIME_TYPE_SRT = "video/MP2T"; + public static final String MIME_TYPE_RTMP = "video/x-rtmp"; + /** * コンテキスト. */ @@ -165,6 +171,12 @@ public boolean isPreviewRunning() { @Override public void onDisplayRotation(int rotation) { + // 画面が回転した場合には、一度描画用のスレッドを停止しておく + EGLSurfaceDrawingThread drawingThread = getSurfaceDrawingThread(); + if (drawingThread != null && drawingThread.isRunning()) { + drawingThread.stop(true); + } + PreviewServerProvider previewServerProvider = getServerProvider(); if (previewServerProvider != null) { previewServerProvider.onConfigChange(); @@ -178,6 +190,12 @@ public void onDisplayRotation(int rotation) { @Override public void onConfigChange() { + // 設定が変更された場合には、一度描画用のスレッドを停止しておく + EGLSurfaceDrawingThread drawingThread = getSurfaceDrawingThread(); + if (drawingThread != null && drawingThread.isRunning()) { + drawingThread.stop(true); + } + PreviewServerProvider previewServerProvider = getServerProvider(); if (previewServerProvider != null) { previewServerProvider.onConfigChange(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index d03d74f182..c1c2132d29 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -465,6 +465,10 @@ abstract class Settings { private final PropertyUtil mProperty; private final Map mPropertyMap = new HashMap<>(); + private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; + private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; + private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; + public Settings(Context context, HostMediaRecorder recorder) { mProperty = new PropertyUtil(context, recorder.getId()); mPropertyMap.put("video/x-mjpeg", new PropertyUtil(context, recorder.getId() + "-mjpeg")); @@ -609,10 +613,22 @@ public void setPreviewWhiteBalance(Integer whiteBalance) { } } + /** + * 自動露出モードを取得します. + * + * @return 自動露出モード + */ public Integer getPreviewAutoExposureMode() { return mProperty.getInteger("preview_auto_exposure_mode", null); } + /** + * 自動露出モードを設定します. + * + * mode に null が指定された場合は設定を削除します。 + * + * @param mode 自動露出モード + */ public void setPreviewAutoExposureMode(Integer mode) { if (mode == null) { mProperty.remove("preview_auto_exposure_mode"); @@ -814,7 +830,6 @@ public void setPreviewWhiteBalanceTemperature(Integer temperature) { } } - /// ポート番号 /** @@ -860,8 +875,6 @@ public void setPreviewSize(String mimeType, Size previewSize) { } } - private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; - public VideoEncoderName getPreviewEncoderName(String mimeType) { return VideoEncoderName.nameOf(getPreviewEncoder(mimeType)); } @@ -1024,8 +1037,6 @@ public Integer getLevel() { return mProperty.getInteger("preview_level", 0); } - private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; - public int getPreviewMaxFrameRate(String mimeType) { PropertyUtil prof = mPropertyMap.get(mimeType); if (prof != null) { @@ -1041,7 +1052,7 @@ public void setPreviewMaxFrameRate(String mimeType, Integer previewMaxFrameRate) PropertyUtil prof = mPropertyMap.get(mimeType); if (prof != null) { - mProperty.put("preview_framerate", previewMaxFrameRate); + prof.put("preview_framerate", previewMaxFrameRate); } } @@ -1066,8 +1077,6 @@ public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { mProperty.put("preview_framerate", previewMaxFrameRate); } - private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; - public int getPreviewBitRate(String mimeType) { PropertyUtil prof = mPropertyMap.get(mimeType); if (prof != null) { @@ -2035,6 +2044,10 @@ public boolean isSupportedSampleRate(int sampleRate) { * @return 配信先の URI */ public String getBroadcastURI() { + PropertyUtil prof = mPropertyMap.get("video/x-rtmp"); + if (prof != null) { + return prof.getString("broadcast_uri", null); + } return mProperty.getString("broadcast_uri", null); } @@ -2090,79 +2103,5 @@ public void setRetryInterval(int interval) { mProperty.put("broadcast_retry_interval", interval); } } - - // ポート番号 - - /** - * Motion JPEG サーバ用のポート番号を取得します. - * - * @return Motion JPEG サーバ用のポート番号 - */ - public Integer getMjpegPort() { - return mProperty.getInteger("mjpeg_port", 0); - } - - /** - * Motion JPEG サーバ用のポート番号を設定します. - * - * @param port Motion JPEG サーバ用のポート番号 - */ - public void setMjpegPort(int port) { - mProperty.put("mjpeg_port", port); - } - - /** - * SSL で暗号化された Motion JPEG サーバ用のポート番号を取得します. - * - * @return Motion JPEG サーバ用のポート番号 - */ - public Integer getMjpegSSLPort() { - return mProperty.getInteger("mjpeg_ssl_port", 0); - } - - /** - * SSL で暗号化された Motion JPEG サーバ用のポート番号を取得します. - * - * @param port Motion JPEG サーバ用のポート番号 - */ - public void setMjpegSSLPort(int port) { - mProperty.put("mjpeg_ssl_port", port); - } - - /** - * RTSP サーバ用のポート番号を取得します. - * - * @return RTSP サーバ用のポート番号 - */ - public Integer getRtspPort() { - return mProperty.getInteger("rtsp_port", 0); - } - - /** - * RTSP サーバ用のポート番号を設定します. - * - * @param port RTSP サーバ用のポート番号 - */ - public void setRtspPort(int port) { - mProperty.put("rtsp_port", port); - } - - /** - * SRT サーバ用のポート番号を取得します. - * - * @return SRT サーバ用のポート番号 - */ - public Integer getSrtPort() { - return mProperty.getInteger("srt_port", 0); - } - - /** - * SRT サーバ用のポート番号を設定します. - * - * @param port SRT サーバ用のポート番号 - */ - public void setSrtPort(int port) { - mProperty.put("srt_port", port); - } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index 4ea30ff09c..c60d343494 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -184,15 +184,32 @@ private void initSupportedSettings() { mSettings.setPreviewWhiteBalanceTemperature(5600); mSettings.setPreviewAudioSource(null); - mSettings.setPreviewAudioBitRate(64 * 1024); - mSettings.setPreviewSampleRate(16000); + mSettings.setPreviewAudioBitRate(128 * 1024); + mSettings.setPreviewSampleRate(48000); mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); - mSettings.setMjpegPort(11000 + mFacing.mValue); - mSettings.setMjpegSSLPort(11100 + mFacing.mValue); - mSettings.setRtspPort(12000 + mFacing.mValue); - mSettings.setSrtPort(13000 + mFacing.mValue); + mSettings.setPort(MIME_TYPE_MJPEG, 11000 + mFacing.mValue); + mSettings.setPreviewSize(MIME_TYPE_MJPEG, options.getDefaultPreviewSize()); + mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); + + mSettings.setPort(MIME_TYPE_RTSP, 12000 + mFacing.mValue); + mSettings.setPreviewSize(MIME_TYPE_RTSP, options.getDefaultPreviewSize()); + mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); + mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); + + mSettings.setPort(MIME_TYPE_SRT, 13000 + mFacing.mValue); + mSettings.setPreviewSize(MIME_TYPE_SRT, options.getDefaultPreviewSize()); + mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); + mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); + + mSettings.setPreviewSize(MIME_TYPE_RTMP, options.getDefaultPreviewSize()); + mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); + mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); mSettings.finishInitialization(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGEncoder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGEncoder.java index 964a39c031..8c3e2c59b8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGEncoder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGEncoder.java @@ -3,24 +3,7 @@ import org.deviceconnect.android.libmedia.streaming.mjpeg.SurfaceMJPEGEncoder; public class ScreenCastMJPEGEncoder extends SurfaceMJPEGEncoder { - ScreenCastMJPEGEncoder(ScreenCastRecorder recorder) { super(recorder.getSurfaceDrawingThread()); } - - @Override - protected void prepare() { - } - - @Override - protected void startRecording() { - } - - @Override - protected void stopRecording() { - } - - @Override - protected void release() { - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java index 049af34fb6..70ee087991 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java @@ -10,9 +10,9 @@ @TargetApi(Build.VERSION_CODES.LOLLIPOP) class ScreenCastMJPEGPreviewServer extends AbstractMJPEGPreviewServer { - ScreenCastMJPEGPreviewServer(Context context, ScreenCastRecorder recorder, int port, boolean useSSL) { + ScreenCastMJPEGPreviewServer(Context context, ScreenCastRecorder recorder, boolean useSSL) { super(context, recorder, useSSL); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType()) + (useSSL ? 1 : 0)); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java index 30f1143ef4..8a2b443015 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java @@ -3,7 +3,6 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServerProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; /** * スクリーンキャストのプレビューを配信するサーバを管理するクラス. @@ -12,11 +11,9 @@ class ScreenCastPreviewServerProvider extends AbstractPreviewServerProvider { ScreenCastPreviewServerProvider(Context context, ScreenCastRecorder recorder) { super(context, recorder); - HostMediaRecorder.Settings settings = recorder.getSettings(); - - addServer(new ScreenCastMJPEGPreviewServer(context, recorder, settings.getMjpegPort(), false)); - addServer(new ScreenCastMJPEGPreviewServer(context, recorder, settings.getMjpegSSLPort(), true)); - addServer(new ScreenCastRTSPPreviewServer(context, recorder, settings.getRtspPort())); - addServer(new ScreenCastSRTPreviewServer(context, recorder, settings.getSrtPort())); + addServer(new ScreenCastMJPEGPreviewServer(context, recorder, false)); + addServer(new ScreenCastMJPEGPreviewServer(context, recorder, true)); + addServer(new ScreenCastRTSPPreviewServer(context, recorder)); + addServer(new ScreenCastSRTPreviewServer(context, recorder)); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java index 0645c27c4e..03311486b7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java @@ -11,16 +11,16 @@ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) class ScreenCastRTSPPreviewServer extends AbstractRTSPPreviewServer { - ScreenCastRTSPPreviewServer(Context context, ScreenCastRecorder recorder, int port) { + ScreenCastRTSPPreviewServer(Context context, ScreenCastRecorder recorder) { super(context, recorder); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType())); } @Override protected VideoStream createVideoStream() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName()) { + switch (settings.getPreviewEncoderName(getMimeType())) { case H264: default: return new ScreenCastH264VideoStream(recorder, 5006); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index 3b5d50a354..f855ce67b4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -134,15 +134,33 @@ private void initSupportedSettings() { mSettings.setPreviewKeyFrameInterval(1); mSettings.setPreviewQuality(80); - mSettings.setPreviewAudioBitRate(64 * 1024); - mSettings.setPreviewSampleRate(16000); + mSettings.setPreviewAudioSource(null); + mSettings.setPreviewAudioBitRate(128 * 1024); + mSettings.setPreviewSampleRate(48000); mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); - mSettings.setMjpegPort(21000); - mSettings.setMjpegSSLPort(21100); - mSettings.setRtspPort(22000); - mSettings.setSrtPort(23000); + mSettings.setPort(MIME_TYPE_MJPEG, 21000); + mSettings.setPreviewSize(MIME_TYPE_MJPEG, mSettings.getSupportedPreviewSizes().get(0)); + mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); + + mSettings.setPort(MIME_TYPE_RTSP, 22000); + mSettings.setPreviewSize(MIME_TYPE_RTSP, mSettings.getSupportedPreviewSizes().get(0)); + mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); + mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); + + mSettings.setPort(MIME_TYPE_SRT, 23000); + mSettings.setPreviewSize(MIME_TYPE_SRT, mSettings.getSupportedPreviewSizes().get(0)); + mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); + mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); + + mSettings.setPreviewSize(MIME_TYPE_RTMP, mSettings.getSupportedPreviewSizes().get(0)); + mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); + mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); + mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); mSettings.finishInitialization(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java index b7a72b20fa..316ff11461 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java @@ -14,7 +14,7 @@ public ScreenCastSRTBroadcaster(ScreenCastRecorder recorder, String broadcastURI protected VideoEncoder createVideoEncoder() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName()) { + switch (settings.getPreviewEncoderName(getMimeType())) { case H264: default: return new ScreenCastVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java index e52cf97800..d7d4a45c1b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java @@ -18,16 +18,16 @@ * @author NTT DOCOMO, INC. */ class ScreenCastSRTPreviewServer extends AbstractSRTPreviewServer { - ScreenCastSRTPreviewServer(final Context context, final ScreenCastRecorder recorder, final int port) { + ScreenCastSRTPreviewServer(final Context context, final ScreenCastRecorder recorder) { super(context, recorder); - setPort(port); + setPort(recorder.getSettings().getPort(getMimeType())); } @Override protected VideoEncoder createVideoEncoder() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName()) { + switch (settings.getPreviewEncoderName(getMimeType())) { case H264: default: return new ScreenCastVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastVideoEncoder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastVideoEncoder.java index a8c6262f41..d5b0e88029 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastVideoEncoder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastVideoEncoder.java @@ -25,14 +25,4 @@ public ScreenCastVideoEncoder(ScreenCastRecorder recorder, String mimeType) { public VideoQuality getVideoQuality() { return mVideoQuality; } - - // SurfaceVideoEncoder - - @Override - protected void onStartSurfaceDrawing() { - } - - @Override - protected void onStopSurfaceDrawing() { - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml index c1c9799050..7959b237da 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml @@ -82,6 +82,7 @@ MJPEG RTSP SRT + RTMP ポート設定 サーバ設定 diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml index 69f7b85bc8..b493d0ddec 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml @@ -78,9 +78,10 @@ Video Settings Audio Settings Broadcast Settings - MJPEG Settings - RTSP Settings - SRT Settings + MJPEG + RTSP + SRT + RTMP Port Settings Server Settings diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml index efe7409c32..ec7e4bfc80 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml @@ -16,11 +16,6 @@ app:key="recorder_settings_audio" app:title="@string/host_recorder_settings_audio" /> - - + + + + + + \ No newline at end of file From 4970e1e09f6770a6098c4e00d50cc7585e739071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 25 Aug 2021 19:26:23 +0900 Subject: [PATCH 09/40] =?UTF-8?q?Connection=E3=83=97=E3=83=AD=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE=20network=20api=20?= =?UTF-8?q?=E3=81=AB=20upstream=20=E3=81=A8=20downstream=20=E3=81=AE?= =?UTF-8?q?=E5=B8=AF=E5=9F=9F=E5=B9=85=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connection/HostConnectionManager.java | 228 +++++++++++++----- .../host/profile/HostConnectionProfile.java | 25 +- 2 files changed, 189 insertions(+), 64 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java index 7b7d0841a4..23b216dd8d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java @@ -16,6 +16,7 @@ import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.Uri; +import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; @@ -39,6 +40,7 @@ import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; import org.deviceconnect.android.message.DevicePluginContext; import org.deviceconnect.android.util.NotificationUtils; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -52,6 +54,7 @@ public class HostConnectionManager { private final ConnectivityManager mConnectivityManager; private TelephonyManager mTelephonyManager; private NetworkType mMobileNetworkType = NetworkType.TYPE_NONE; + private int mStrengthLevel = 0; private HostTrafficMonitor mTrafficMonitor; private final Handler mCallbackHandler = new Handler(Looper.getMainLooper()); private final WeakReferenceList mConnectionEventListeners = new WeakReferenceList<>(); @@ -118,8 +121,8 @@ public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo info) { public void onSignalStrengthsChanged(SignalStrength signalStrength) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { for (CellSignalStrength strength : signalStrength.getCellSignalStrengths()) { - int level = strength.getLevel(); - switch (level) { + mStrengthLevel = strength.getLevel(); + switch (mStrengthLevel) { case CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN: case CellSignalStrength.SIGNAL_STRENGTH_POOR: case CellSignalStrength.SIGNAL_STRENGTH_MODERATE: @@ -128,7 +131,10 @@ public void onSignalStrengthsChanged(SignalStrength signalStrength) { break; } } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mStrengthLevel = signalStrength.getLevel(); } + postOnChangeNetwork(); } }; @@ -185,9 +191,9 @@ public void removeConnectionEventListener(ConnectionEventListener listener) { * * @return 有効になっているネットワーク名 */ - public String getActivityNetworkString() { + public String getActivityNetworkString(NetworkType networkType) { Context context = mPluginContext.getContext(); - switch (getActivityNetwork()) { + switch (networkType) { case TYPE_MOBILE: return context.getString(R.string.host_connection_network_type_mobile); case TYPE_WIFI: @@ -213,30 +219,11 @@ public String getActivityNetworkString() { /** * 有効になっているネットワークタイプを取得します. * - * @return ネットワークタイプ + * @return ネットワークの情報 */ - public NetworkType getActivityNetwork() { - NetworkType networkType = NetworkType.TYPE_NONE; - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo(); - if (networkInfo != null) { - switch (networkInfo.getType()) { - case ConnectivityManager.TYPE_MOBILE: - networkType = NetworkType.TYPE_MOBILE; - break; - case ConnectivityManager.TYPE_WIFI: - networkType = NetworkType.TYPE_WIFI; - break; - case ConnectivityManager.TYPE_ETHERNET: - networkType = NetworkType.TYPE_ETHERNET; - break; - case ConnectivityManager.TYPE_BLUETOOTH: - networkType = NetworkType.TYPE_BLUETOOTH; - break; - } - } - } else { + public NetworkCaps getNetworkCaps() { + NetworkCaps caps = new NetworkCaps(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Network n = mConnectivityManager.getActiveNetwork(); NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(n); if (capabilities != null) { @@ -245,26 +232,119 @@ public NetworkType getActivityNetwork() { boolean isBluetooth = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH); boolean isEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET); if (isWifi) { - networkType = NetworkType.TYPE_WIFI; + caps.mStrengthLevel = getWifiStrengthLevel(); + caps.mType = NetworkType.TYPE_WIFI; } else if (isEthernet) { - networkType = NetworkType.TYPE_ETHERNET; + caps.mType = NetworkType.TYPE_ETHERNET; } else if (isBluetooth) { - networkType = NetworkType.TYPE_BLUETOOTH; + caps.mType = NetworkType.TYPE_BLUETOOTH; } else if (isMobile) { - networkType = NetworkType.TYPE_MOBILE; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + caps.mStrengthLevel = capabilities.getSignalStrength(); + } else { + caps.mStrengthLevel = mStrengthLevel; + } + caps.mType = NetworkType.TYPE_MOBILE; if (mMobileNetworkType != null && mMobileNetworkType != NetworkType.TYPE_NONE) { - networkType = mMobileNetworkType; + caps.mType = mMobileNetworkType; } } else { - networkType = NetworkType.TYPE_NONE; + caps.mType = NetworkType.TYPE_NONE; if (mMobileNetworkType != null && mMobileNetworkType != NetworkType.TYPE_NONE) { - networkType = mMobileNetworkType; + caps.mType = mMobileNetworkType; } } + caps.mDownstreamBW = capabilities.getLinkDownstreamBandwidthKbps(); + caps.mUpstreamBW = capabilities.getLinkUpstreamBandwidthKbps(); + } + } else { + NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo(); + if (networkInfo != null) { + switch (networkInfo.getType()) { + case ConnectivityManager.TYPE_MOBILE: + caps.mStrengthLevel = mStrengthLevel; + caps.mType = NetworkType.TYPE_MOBILE; + break; + case ConnectivityManager.TYPE_WIFI: + caps.mStrengthLevel = getWifiStrengthLevel(); + caps.mType = NetworkType.TYPE_WIFI; + break; + case ConnectivityManager.TYPE_ETHERNET: + caps.mType = NetworkType.TYPE_ETHERNET; + break; + case ConnectivityManager.TYPE_BLUETOOTH: + caps.mType = NetworkType.TYPE_BLUETOOTH; + break; + } } } + return caps; + } + + /** + * ネットワークの情報を格納するクラス. + */ + public class NetworkCaps { + private NetworkType mType; + private int mUpstreamBW; + private int mDownstreamBW; + private int mStrengthLevel; - return networkType; + /** + * ネットワークタイプを取得します. + * + * @return ネットワークタイプ + */ + public NetworkType getType() { + return mType; + } + + /** + * ネットワークタイプの文字列を取得します. + * + * @return ネットワークタイプ + */ + public String getTypeString() { + return getActivityNetworkString(mType); + } + + /** + * upstream の帯域幅を取得します. + * + * @return upstream の帯域幅 + */ + public int getUpstreamBW() { + return mUpstreamBW; + } + + /** + * downstream の帯域幅を取得します. + * + * @return downstream の帯域幅 + */ + public int getDownstreamBW() { + return mDownstreamBW; + } + + /** + * 電波強度を取得します. + * + * @return 電波強度 + */ + public int getStrengthLevel() { + return mStrengthLevel; + } + + @NotNull + @Override + public String toString() { + return "NetworkCaps{" + + "mType=" + mType + + ", mUpstreamBW=" + mUpstreamBW + + ", mDownstreamBW=" + mDownstreamBW + + ", mStrengthLevel=" + mStrengthLevel + + '}'; + } } public enum NetworkType { @@ -282,23 +362,21 @@ public enum NetworkType { private boolean mRegisterTelephonyManager; private synchronized void registerTelephony() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - if (mRegisterTelephonyManager) { - return; - } + if (mRegisterTelephonyManager) { + return; + } - try { - mTelephonyManager = (TelephonyManager) mPluginContext.getContext() - .getSystemService(Context.TELEPHONY_SERVICE); - if (mTelephonyManager != null) { - int events = PhoneStateListener.LISTEN_SIGNAL_STRENGTHS; - events |= PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; - mTelephonyManager.listen(mPhoneStateListener, events); - mRegisterTelephonyManager = true; - } - } catch (Exception e) { - // ignore. + try { + mTelephonyManager = (TelephonyManager) mPluginContext.getContext() + .getSystemService(Context.TELEPHONY_SERVICE); + if (mTelephonyManager != null) { + int events = PhoneStateListener.LISTEN_SIGNAL_STRENGTHS; + events |= PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; + mTelephonyManager.listen(mPhoneStateListener, events); + mRegisterTelephonyManager = true; } + } catch (Exception e) { + // ignore. } } @@ -337,14 +415,14 @@ private void registerNetworkCallback() { * ネットワークの接続イベントを受信するための Receiver を解除します. */ private void unregisterNetworkCallback() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - try { + try { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { mPluginContext.getContext().unregisterReceiver(mHostConnectionReceiver); - } catch (Exception e) { - // ignore. + } else { + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); } - } else { - mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + } catch (Exception e) { + // ignore. } } @@ -357,6 +435,34 @@ public boolean isWifiEnabled() { return mWifiManager != null && mWifiManager.isWifiEnabled(); } + /** + * 接続している WiFi の電波強度を取得します. + * + * WiFi の RSSI から下記の範囲で値を返却します。 + * 優れた> -50 dBm + * 良好-50〜-60 dBm + * 普通-60〜-70 dBm + * 弱い<-70dBm + * + * @return WiFi の電波強度 + */ + public int getWifiStrengthLevel() { + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + if (wifiInfo != null) { + int rssi = wifiInfo.getRssi(); + if (rssi > -50) { + return 4; + } else if (rssi > -60) { + return 3; + } else if (rssi > -70) { + return 2; + } else { + return 1; + } + } + return 0; + } + /** * WiFi接続の状態を設定する. * @@ -663,8 +769,18 @@ public interface PermissionCallback { void onDisallowed(); } + /** + * 処理結果を通知するコールバック. + */ public interface Callback { + /** + * 処理に成功した場合に呼び出します. + */ void onSuccess(); + + /** + * 処理に失敗した場合に呼び出します. + */ void onFailure(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java index 242200ded4..07fe8c03e7 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java @@ -277,7 +277,6 @@ public boolean onRequest(final Intent request, final Intent response) { // GET /gotapi/connection/network private final DConnectApi mGetNetworkApi = new GetApi() { - @Override public String getAttribute() { return "network"; @@ -289,7 +288,8 @@ public boolean onRequest(final Intent request, final Intent response) { @Override public void onAllowed() { setResult(response, DConnectMessage.RESULT_OK); - response.putExtra("network", mHostConnectionManager.getActivityNetworkString()); + + response.putExtra("network", createNetworkCaps()); sendResponse(response); } @@ -396,14 +396,13 @@ public boolean onRequest(final Intent request, final Intent response) { traffic.getNetworkType()), createNetworkBitrate(traffic)); } setResult(response, DConnectMessage.RESULT_OK); - return true; } else { HostConnectionManager.openUsageAccessSettings(getContext()); // 使用履歴が有効になるのをポーリングしながら待機 - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 30; i++) { try { - Thread.sleep(3000); + Thread.sleep(1000); } catch (InterruptedException e) { break; } @@ -422,9 +421,8 @@ public boolean onRequest(final Intent request, final Intent response) { } else { MessageUtils.setIllegalServerStateError(response, "Failed to start collecting a traffic."); } - - return true; } + return true; } }; @@ -585,6 +583,16 @@ private Bundle createNetworkBitrate(HostTraffic traffic) { return data; } + private Bundle createNetworkCaps() { + HostConnectionManager.NetworkCaps networkCaps = mHostConnectionManager.getNetworkCaps(); + Bundle network = new Bundle(); + network.putString("network", networkCaps.getTypeString()); + network.putInt("strengthLevel", networkCaps.getStrengthLevel()); + network.putInt("upstream", networkCaps.getUpstreamBW()); + network.putInt("downstream", networkCaps.getDownstreamBW()); + return network; + } + /** * ネットワークが変更されたことを通知します. */ @@ -592,10 +600,11 @@ private void postOnChangeNetwork() { List events = EventManager.INSTANCE.getEventList(HostDevicePlugin.SERVICE_ID, HostConnectionProfile.PROFILE_NAME, "network", "onChange"); + Bundle network = createNetworkCaps(); for (int i = 0; i < events.size(); i++) { Event event = events.get(i); Intent intent = EventManager.createEventMessage(event); - intent.putExtra("network", mHostConnectionManager.getActivityNetworkString()); + intent.putExtra("network", network); sendEvent(intent, event.getAccessToken()); } } From 9aded16d04dfddd87d72544324db0f0d130ef788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 25 Aug 2021 19:27:07 +0900 Subject: [PATCH 10/40] =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=83=97=E3=83=AD=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=83=BB=E3=83=AC=E3=83=99=E3=83=AB=E3=81=8C=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=81=95=E3=82=8C=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AB?= =?UTF-8?q?=E4=BE=8B=E5=A4=96=E3=81=8C=E7=99=BA=E7=94=9F=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E3=80=81=20try-catch=20=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=81=97=E3=81=A6=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recorder/settings/SettingsBroadcastFragment.java | 12 ++++++++++-- .../recorder/settings/SettingsRTSPFragment.java | 12 ++++++++++-- .../recorder/settings/SettingsSRTFragment.java | 12 ++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java index 2f1f187fa6..a7b12905e8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java @@ -370,12 +370,20 @@ private Integer getDrawingRange(String key) { setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく - settings.setProfileLevel(MIME_TYPE, null); + try { + settings.setProfileLevel(MIME_TYPE, null); + } catch (Exception e) { + return false; + } HostMediaRecorder.VideoEncoderName encoderName = HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { - settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + try { + settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } catch (Exception e) { + return false; + } } else if ("preview_clip_left".equalsIgnoreCase(key)) { try { int clipLeft = Integer.parseInt((String) newValue); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java index 222d5678c6..99350de387 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -370,12 +370,20 @@ private Integer getDrawingRange(String key) { setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく - settings.setProfileLevel(MIME_TYPE, null); + try { + settings.setProfileLevel(MIME_TYPE, null); + } catch (Exception e) { + return false; + } HostMediaRecorder.VideoEncoderName encoderName = HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { - settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + try { + settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } catch (Exception e) { + return false; + } } else if ("preview_clip_left".equalsIgnoreCase(key)) { try { int clipLeft = Integer.parseInt((String) newValue); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index fb7dfea954..59d0e8b753 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -397,12 +397,20 @@ private Integer getDrawingRange(String key) { setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく - settings.setProfileLevel(MIME_TYPE, null); + try { + settings.setProfileLevel(MIME_TYPE, null); + } catch (Exception e) { + return false; + } HostMediaRecorder.VideoEncoderName encoderName = HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { - settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + try { + settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } catch (Exception e) { + return false; + } } else if ("preview_clip_left".equalsIgnoreCase(key)) { try { int clipLeft = Integer.parseInt((String) newValue); From 1992fd7e83bfb3b87a1e48fc30c7b87c873dfc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 25 Aug 2021 19:27:39 +0900 Subject: [PATCH 11/40] =?UTF-8?q?=E3=83=93=E3=83=83=E3=83=88=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=A8=E3=82=AD=E3=83=BC=E3=83=95=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=81=AE=E8=A6=81=E6=B1=82=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../streaming/video/VideoEncoder.java | 67 +++++-------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java index c66d62e582..cd7ff97349 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.libmedia.streaming.video; -import android.graphics.Rect; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecList; @@ -20,16 +19,6 @@ public abstract class VideoEncoder extends MediaEncoder { private static final boolean DEBUG = BuildConfig.DEBUG; private static final String TAG = "VIDEO-ENCODER"; - /** - * キーフレームの同期フラグ. - */ - private boolean mSyncKeyFrame; - - /** - * 映像のビットレート変更要求フラグ. - */ - private boolean mRequestChangeBitRate; - // MediaEncoder @Override @@ -37,11 +26,6 @@ protected void prepare() throws IOException { VideoQuality videoQuality = getVideoQuality(); int w = videoQuality.getVideoWidth(); int h = videoQuality.getVideoHeight(); - Rect drawingRange = videoQuality.getDrawingRange(); - if (drawingRange != null) { - w = drawingRange.width(); - h = drawingRange.height(); - } mMediaCodec = createMediaCodec(getColorFormat(), w, h); } @@ -81,7 +65,15 @@ protected void release() { * キーフレームを要求します. */ public void requestSyncKeyFrame() { - mSyncKeyFrame = true; + if (mMediaCodec != null) { + Bundle b = new Bundle(); + b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); + try { + mMediaCodec.setParameters(b); + } catch (Exception e) { + // ignore. + } + } } /** @@ -93,39 +85,14 @@ public void requestSyncKeyFrame() { *

*/ public void requestBitRate() { - mRequestChangeBitRate = true; - } - - /** - * MediaCodec にキーフレームの作成を行います. - */ - private void syncKeyFrame() { - Bundle b = new Bundle(); - b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); - mMediaCodec.setParameters(b); - mSyncKeyFrame = false; - } - - /** - * MediaCodec にビットレートの変更を行います. - */ - private void changeBitRate() { - Bundle b = new Bundle(); - b.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, getVideoQuality().getBitRate()); - mMediaCodec.setParameters(b); - mRequestChangeBitRate = false; - } - - /** - * MediaCodec へのリクエスト処理を行います. - */ - protected void executeRequest() { - if (mSyncKeyFrame) { - syncKeyFrame(); - } - - if (mRequestChangeBitRate) { - changeBitRate(); + if (mMediaCodec != null) { + Bundle b = new Bundle(); + b.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, getVideoQuality().getBitRate()); + try { + mMediaCodec.setParameters(b); + } catch (Exception e) { + // ignore. + } } } From 9b051ae3e49925cd482a9fccec7e2f66646fb75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 25 Aug 2021 19:28:15 +0900 Subject: [PATCH 12/40] =?UTF-8?q?=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B=E6=99=82?= =?UTF-8?q?=E3=81=AB=E3=82=A2=E3=83=8B=E3=83=A1=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/recorder/util/OverlayPermissionActivity.java | 7 +++++++ .../host/recorder/util/PermissionReceiverActivity.java | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/OverlayPermissionActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/OverlayPermissionActivity.java index 4fb2fc86f0..3ad4594342 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/OverlayPermissionActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/OverlayPermissionActivity.java @@ -44,6 +44,13 @@ public void onCreate(final Bundle savedInstanceState) { showOverlayDisabled(); } + @Override + public void finish() { + super.finish(); + // Activity の遷移アニメーションを削除 + overridePendingTransition(0, 0); + } + @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PermissionReceiverActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PermissionReceiverActivity.java index 8709784160..cf07d2b199 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PermissionReceiverActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PermissionReceiverActivity.java @@ -68,6 +68,13 @@ protected void onResume() { startActivityForResult(intent, REQUEST_CODE); } + @Override + public void finish() { + super.finish(); + // Activity の遷移アニメーションを削除 + overridePendingTransition(0, 0); + } + @Override protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); From 35be91ef25e9c84cf91ee015704530fef99b161b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 25 Aug 2021 19:29:09 +0900 Subject: [PATCH 13/40] =?UTF-8?q?SRT=20=E3=81=AE=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=20HostMediaRecorder#Settings=20=E3=81=AB=E7=B5=B1?= =?UTF-8?q?=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recorder/AbstractSRTPreviewServer.java | 10 +-- .../host/recorder/HostMediaRecorder.java | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index 795b470ae2..e04b069a3a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -4,7 +4,6 @@ import android.util.Log; import org.deviceconnect.android.deviceplugin.host.BuildConfig; -import org.deviceconnect.android.deviceplugin.host.recorder.util.SRTSettings; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder; @@ -22,11 +21,6 @@ public abstract class AbstractSRTPreviewServer extends AbstractPreviewServer { */ private static final String MIME_TYPE = "video/MP2T"; - /** - * SRTの設定. - */ - private final SRTSettings mSettings; - /** * SRT サーバ. */ @@ -38,7 +32,6 @@ public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder) { public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { super(context, recorder, useSSL); - mSettings = new SRTSettings(context); } @Override @@ -55,11 +48,12 @@ public String getMimeType() { public void startWebServer(final OnWebServerStartCallback callback) { if (mSRTServer == null) { try { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); mSRTServer = new SRTServer(getPort()); mSRTServer.setStatsInterval(BuildConfig.STATS_INTERVAL); mSRTServer.setShowStats(DEBUG); mSRTServer.setCallback(mCallback); - mSRTServer.setSocketOptions(mSettings.loadSRTSocketOptions()); + mSRTServer.setSocketOptions(settings.getSRTSocketOptions()); mSRTServer.start(); } catch (Exception e) { callback.onFail(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index c1c2132d29..96e1bd5013 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -12,10 +12,12 @@ import android.util.Range; import android.util.Size; +import org.deviceconnect.android.deviceplugin.host.R; import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; import org.deviceconnect.android.deviceplugin.host.recorder.util.PropertyUtil; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; +import org.deviceconnect.android.libsrt.SRT; import java.util.ArrayList; import java.util.Arrays; @@ -464,12 +466,14 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host abstract class Settings { private final PropertyUtil mProperty; private final Map mPropertyMap = new HashMap<>(); + private final Context mContext; private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; public Settings(Context context, HostMediaRecorder recorder) { + mContext = context; mProperty = new PropertyUtil(context, recorder.getId()); mPropertyMap.put("video/x-mjpeg", new PropertyUtil(context, recorder.getId() + "-mjpeg")); mPropertyMap.put("video/x-rtp", new PropertyUtil(context, recorder.getId() + "-rtsp")); @@ -2103,5 +2107,79 @@ public void setRetryInterval(int interval) { mProperty.put("broadcast_retry_interval", interval); } } + + // SRT + + /** + * SRT サーバに対して設定するオプションの一覧を作成します. + * + * @return オプションの一覧 + */ + public Map getSRTSocketOptions() { + PropertyUtil prof = mPropertyMap.get("video/MP2T"); + if (prof != null) { + Map options = new HashMap<>(); + for (SRTOptionItem item : SRT_OPTION_ITEMS) { + String key = mContext.getString(item.getPrefKey()); + String value = prof.getString(key, null); + if (value == null || "".equals(value)) { + continue; + } + + try { + if (item.getValueClass() == Long.class) { + options.put(item.getOptionEnum(), Long.parseLong(value)); + } else if (item.getValueClass() == Integer.class) { + options.put(item.getOptionEnum(), Integer.parseInt(value)); + } else { + options.put(item.getOptionEnum(), value); + } + } catch (Exception ignored) {} + } + return options; + } + return null; + } + + /** + * 設定画面でサポートする SRT オプションの定義. + */ + private static final List SRT_OPTION_ITEMS = Arrays.asList( + new SRTOptionItem(SRT.SRTO_PEERLATENCY, Integer.class, R.string.pref_key_settings_srt_peerlatency), + new SRTOptionItem(SRT.SRTO_LOSSMAXTTL, Integer.class, R.string.pref_key_settings_srt_lossmaxttl), + new SRTOptionItem(SRT.SRTO_INPUTBW, Long.class, R.string.pref_key_settings_srt_inputbw), + new SRTOptionItem(SRT.SRTO_OHEADBW, Integer.class, R.string.pref_key_settings_srt_oheadbw), + new SRTOptionItem(SRT.SRTO_CONNTIMEO, Integer.class, R.string.pref_key_settings_srt_conntimeo), + new SRTOptionItem(SRT.SRTO_PEERIDLETIMEO, Integer.class, R.string.pref_key_settings_srt_peeridletimeo), + new SRTOptionItem(SRT.SRTO_PACKETFILTER, String.class, R.string.pref_key_settings_srt_packetfilter)); + + /** + * SRT オプション設定項目の定義. + * + * SRT オプションの列挙子 ({@link SRT} で定義されているもの) に対して、値の型とプリファレンスキーを対応づける. + */ + private static class SRTOptionItem { + final int mOptionEnum; + final Class mValueClass; + final int mPrefKey; + + SRTOptionItem(int optionEnum, Class valueClass, int prefKey) { + mOptionEnum = optionEnum; + mValueClass = valueClass; + mPrefKey = prefKey; + } + + int getOptionEnum() { + return mOptionEnum; + } + + int getPrefKey() { + return mPrefKey; + } + + Class getValueClass() { + return mValueClass; + } + } } } From 697325322f61036f340f94e65ab0f8cb3eb2ee00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Thu, 26 Aug 2021 12:48:07 +0900 Subject: [PATCH 14/40] =?UTF-8?q?=E9=80=9A=E4=BF=A1=E3=83=93=E3=83=83?= =?UTF-8?q?=E3=83=88=E3=83=AC=E3=83=BC=E3=83=88=E3=81=AE=E5=B1=A5=E6=AD=B4?= =?UTF-8?q?=E3=82=92=E5=8F=96=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connection/HostConnectionManager.java | 27 +++++- .../host/connection/HostTraffic.java | 32 +++++-- .../host/connection/HostTrafficMonitor.java | 87 ++++++++++++++----- .../host/profile/HostConnectionProfile.java | 87 +++++++++++++++---- 4 files changed, 188 insertions(+), 45 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java index 23b216dd8d..052a750bb0 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java @@ -193,6 +193,14 @@ public void removeConnectionEventListener(ConnectionEventListener listener) { */ public String getActivityNetworkString(NetworkType networkType) { Context context = mPluginContext.getContext(); + if (context == null) { + return "No Connect"; + } + + if (networkType == null) { + return context.getString(R.string.host_connection_network_type_no_connect); + } + switch (networkType) { case TYPE_MOBILE: return context.getString(R.string.host_connection_network_type_mobile); @@ -574,7 +582,20 @@ public synchronized void stopTrafficMonitor() { } /** - * ネットワーク通信量のリストを取得します. + * 指定されたネットワークの通信量履歴を取得します. + * + * @param networkType ネットワークタイプ + * @return ネットワークの通信量履歴 + */ + public synchronized List getTrafficList(int networkType) { + if (mTrafficMonitor != null) { + return mTrafficMonitor.getTrafficList(networkType); + } + return new ArrayList<>(); + } + + /** + * ネットワーク毎の最新の通信量をリストにして取得します. * * リストには、各ネットワークの通信量が格納されています。 * @@ -582,8 +603,8 @@ public synchronized void stopTrafficMonitor() { * * @return ネットワーク通信量のリスト */ - public synchronized List getTrafficList() { - return mTrafficMonitor != null ? mTrafficMonitor.getTrafficList() : new ArrayList<>(); + public synchronized List getLastTrafficForAllNetwork() { + return mTrafficMonitor != null ? mTrafficMonitor.getLastTrafficForAllNetwork() : new ArrayList<>(); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTraffic.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTraffic.java index 84cccdfb26..b028db8d01 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTraffic.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTraffic.java @@ -6,6 +6,8 @@ public class HostTraffic { long mTx; long mBitrateRx; long mBitrateTx; + long mStartTime; + long mEndTime; /** * ネットワークタイプを取得します. @@ -52,12 +54,32 @@ public long getBitrateTx() { return mBitrateTx; } + /** + * 計測開始時間を取得します. + * + * @return 計測開始時間 + */ + public long getStartTime() { + return mStartTime; + } + + /** + * 計測終了時間を取得します. + * + * @return 計測終了時間 + */ + public long getEndTime() { + return mEndTime; + } + @Override public String toString() { - return "networkType: " + mNetworkType + "\n" - + "rx: " + mRx + "\n" - + "tx: " + mTx + "\n" - + "BitrateRx: " + mBitrateRx + "\n" - + "BitrateTx: " + mBitrateTx + "\n"; + return "{\n" + + " networkType: " + mNetworkType + "\n" + + " rx: " + mRx + "\n" + + " tx: " + mTx + "\n" + + " BitrateRx: " + mBitrateRx + "\n" + + " BitrateTx: " + mBitrateTx + "\n" + + "}"; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTrafficMonitor.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTrafficMonitor.java index 6c0e56a4c3..087cbba15d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTrafficMonitor.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostTrafficMonitor.java @@ -29,6 +29,7 @@ class HostTrafficMonitor { private final Map> mStatsMap = new HashMap<>(); private final long mInterval; private Timer mTimer; + private int mCacheSize = 50; private NetworkStatsManager mNetworkStatsManager; private OnTrafficListener mOnTrafficListener; @@ -50,6 +51,15 @@ class HostTrafficMonitor { } } + /** + * 通信量の情報をキャッシュするサイズを設定します. + * + * @param cacheSize キャッシュサイズ + */ + void setCacheSize(int cacheSize) { + mCacheSize = cacheSize; + } + /** * 通信量を通知するリスナーを設定します. * @@ -85,40 +95,71 @@ void stopTimer() { } /** - * ネットワークごとの通信量を取得します. + * Stats の情報を HostTraffic に変換します. * * @param networkType ネットワークタイプ - * @return ネットワークごとの通信量 + * @param stats 通信情報 + * @param pre 通信情報 + * @return HostTraffic */ - private HostTraffic getTraffic(int networkType) { + private HostTraffic conv(int networkType, Stats stats, Stats pre) { + long rx = (stats.getTotalRxBytes() - pre.getTotalRxBytes()); + long tx = (stats.getTotalTxBytes() - pre.getTotalTxBytes()); + long bitrateRx = 8 * rx / ((stats.getEndTime() - pre.getEndTime()) / 1000); + long bitrateTx = 8 * tx / ((stats.getEndTime() - pre.getEndTime()) / 1000); + HostTraffic traffic = new HostTraffic(); + traffic.mNetworkType = networkType; + traffic.mRx = rx; + traffic.mTx = tx; + traffic.mBitrateRx = bitrateRx; + traffic.mBitrateTx = bitrateTx; + traffic.mStartTime = stats.mEndTime - mInterval; + traffic.mEndTime = stats.mEndTime; + return traffic; + } + + /** + * 指定されたネットワークの最新の通信量を取得します. + * + * @param networkType ネットワークタイプ + * @return 指定されたネットワークの最新の通信量 + */ + private HostTraffic getLastTraffic(int networkType) { List statsList = mStatsMap.get(networkType); if (statsList != null && statsList.size() > 1) { Stats pre = statsList.get(statsList.size() - 2); Stats stats = statsList.get(statsList.size() - 1); - long rx = (stats.getTotalRxBytes() - pre.getTotalRxBytes()); - long tx = (stats.getTotalTxBytes() - pre.getTotalTxBytes()); - long bitrateRx = 8 * rx / ((stats.getEndTime() - pre.getEndTime()) / 1000); - long bitrateTx = 8 * tx / ((stats.getEndTime() - pre.getEndTime()) / 1000); - HostTraffic traffic = new HostTraffic(); - traffic.mNetworkType = networkType; - traffic.mRx = rx; - traffic.mTx = tx; - traffic.mBitrateRx = bitrateRx; - traffic.mBitrateTx = bitrateTx; - return traffic; + return conv(networkType, stats, pre); } return null; } /** - * ネットワークの通信量を取得します. + * 指定されたネットワークの通信量のリストを取得します. + * + * @param networkType ネットワークタイプ + * @return 指定されたネットワークの通信量のリスト + */ + List getTrafficList(int networkType) { + List trafficList = new ArrayList<>(); + List statsList = mStatsMap.get(networkType); + for (int i = 1; i < statsList.size(); i++) { + Stats pre = statsList.get(i - 1); + Stats stats = statsList.get(i); + trafficList.add(conv(networkType, stats, pre)); + } + return trafficList; + } + + /** + * 各ネットワーク毎の最新の通信量をリストに格納して返却します. * - * @return ネットワークの通信量 + * @return 各ネットワーク毎の最新の通信量 */ - List getTrafficList() { + List getLastTrafficForAllNetwork() { List trafficList = new ArrayList<>(); for (int networkType : NETWORK_TYPE_LIST) { - HostTraffic traffic = getTraffic(networkType); + HostTraffic traffic = getLastTraffic(networkType); if (traffic != null) { trafficList.add(traffic); } @@ -144,6 +185,12 @@ private void monitoring() { } } + /** + * 指定されたネットワークタイプの通信情報を取得します. + * + * @param networkType ネットワークタイプ + * @return ネットワーク情報 + */ private HostTraffic getNetworkStats(int networkType) { HostTraffic traffic = null; @@ -163,12 +210,12 @@ private HostTraffic getNetworkStats(int networkType) { statsList.add(stats); - if (statsList.size() > 100) { + if (statsList.size() > mCacheSize) { statsList.remove(0); } if (statsList.size() > 1) { - traffic = getTraffic(networkType); + traffic = getLastTraffic(networkType); } return traffic; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java index 07fe8c03e7..2e3f5196f4 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java @@ -28,7 +28,12 @@ import org.deviceconnect.message.DConnectMessage; import org.deviceconnect.message.intent.message.IntentDConnectMessage; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Locale; /** * Connection プロファイル. @@ -389,39 +394,42 @@ public String getAttribute() { @Override public boolean onRequest(final Intent request, final Intent response) { - if (HostConnectionManager.checkUsageAccessSettings(getContext())) { - List trafficList = mHostConnectionManager.getTrafficList(); - for (HostTraffic traffic : trafficList) { - response.putExtra(convertNetworkTypeToString( - traffic.getNetworkType()), createNetworkBitrate(traffic)); - } - setResult(response, DConnectMessage.RESULT_OK); - } else { + if (!HostConnectionManager.checkUsageAccessSettings(getContext())) { HostConnectionManager.openUsageAccessSettings(getContext()); - // 使用履歴が有効になるのをポーリングしながら待機 + // 使用履歴が有効になるのをポーリングしながら待機します。 for (int i = 0; i < 30; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { break; } + if (HostConnectionManager.checkUsageAccessSettings(getContext())) { break; } } + } - if (HostConnectionManager.checkUsageAccessSettings(getContext())) { - List trafficList = mHostConnectionManager.getTrafficList(); + if (HostConnectionManager.checkUsageAccessSettings(getContext())) { + final int[] networkTypeList = { + ConnectivityManager.TYPE_MOBILE, + ConnectivityManager.TYPE_WIFI + }; + + for (int networkType : networkTypeList) { + List trafficList = mHostConnectionManager.getTrafficList(networkType); + ArrayList trafficArray = new ArrayList<>(); for (HostTraffic traffic : trafficList) { - response.putExtra(convertNetworkTypeToString( - traffic.getNetworkType()), createNetworkBitrate(traffic)); + trafficArray.add(createNetworkBitrate(traffic)); } - setResult(response, DConnectMessage.RESULT_OK); - } else { - MessageUtils.setIllegalServerStateError(response, "Failed to start collecting a traffic."); + response.putExtra(convertNetworkTypeToString(networkType), trafficArray); } + setResult(response, DConnectMessage.RESULT_OK); + } else { + MessageUtils.setIllegalServerStateError(response, "Failed to start collecting a traffic."); } + return true; } }; @@ -563,6 +571,10 @@ private String convertNetworkTypeToString(int networkType) { return "mobile"; case ConnectivityManager.TYPE_WIFI: return "wifi"; + case ConnectivityManager.TYPE_BLUETOOTH: + return "bluetooth"; + case ConnectivityManager.TYPE_ETHERNET: + return "ethernet"; default: return "unknown"; } @@ -580,16 +592,57 @@ private Bundle createNetworkBitrate(HostTraffic traffic) { Bundle data = new Bundle(); data.putBundle("send", send); data.putBundle("receive", receive); + data.putString("start", df.format(new Date(traffic.getStartTime()))); + data.putString("end", df.format(new Date(traffic.getEndTime()))); return data; } + private final DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); + + private HostTraffic getNetworkBitrate(int type) { + List trafficList = mHostConnectionManager.getLastTrafficForAllNetwork(); + for (HostTraffic traffic : trafficList) { + if (type == traffic.getNetworkType()) { + return traffic; + } + } + return null; + } + private Bundle createNetworkCaps() { HostConnectionManager.NetworkCaps networkCaps = mHostConnectionManager.getNetworkCaps(); Bundle network = new Bundle(); - network.putString("network", networkCaps.getTypeString()); + network.putString("type", networkCaps.getTypeString()); network.putInt("strengthLevel", networkCaps.getStrengthLevel()); network.putInt("upstream", networkCaps.getUpstreamBW()); network.putInt("downstream", networkCaps.getDownstreamBW()); + + if (HostConnectionManager.checkUsageAccessSettings(getContext())) { + switch (networkCaps.getType()) { + case TYPE_BLUETOOTH: + case TYPE_ETHERNET: + break; + case TYPE_WIFI: + { + HostTraffic traffic = getNetworkBitrate(ConnectivityManager.TYPE_WIFI); + if (traffic != null) { + network.putBundle("traffic", createNetworkBitrate(traffic)); + } + } break; + case TYPE_MOBILE: + case TYPE_LTE_CA: + case TYPE_NR_NSA: + case TYPE_NR_NSA_MMWAV: + case TYPE_LTE_ADVANCED_PRO: + { + HostTraffic traffic = getNetworkBitrate(ConnectivityManager.TYPE_MOBILE); + if (traffic != null) { + network.putBundle("traffic", createNetworkBitrate(traffic)); + } + } break; + } + } + return network; } From 2c881cdbe799a06429e96ad60e2aaaee5f4b546e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 31 Aug 2021 10:30:25 +0900 Subject: [PATCH 15/40] =?UTF-8?q?=E3=83=97=E3=83=AC=E3=83=93=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E3=82=B5=E3=83=BC=E3=83=90=E3=80=81=E9=85=8D=E4=BF=A1?= =?UTF-8?q?=E3=81=AA=E3=81=A9=E3=81=AE=E6=A9=9F=E8=83=BD=E3=82=92=E5=8B=95?= =?UTF-8?q?=E7=9A=84=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recorder/settings/SettingsActivity.java | 16 +- .../settings/SettingsBaseFragment.java | 8 + .../settings/SettingsBroadcastFragment.java | 45 +- .../settings/SettingsMJPEGFragment.java | 31 +- .../settings/SettingsMainFragment.java | 74 ++- .../settings/SettingsRTSPFragment.java | 44 +- .../settings/SettingsSRTFragment.java | 44 +- .../connection/HostConnectionManager.java | 14 +- .../HostMediaStreamingRecordingProfile.java | 105 +++- .../host/recorder/AbstractBroadcaster.java | 29 +- .../recorder/AbstractMJPEGPreviewServer.java | 4 +- .../host/recorder/AbstractPreviewServer.java | 188 ++++++- .../recorder/AbstractRTMPBroadcaster.java | 2 +- .../recorder/AbstractRTSPPreviewServer.java | 2 +- .../host/recorder/AbstractSRTBroadcaster.java | 2 +- .../recorder/AbstractSRTPreviewServer.java | 16 +- .../host/recorder/Broadcaster.java | 12 + .../host/recorder/CropInterface.java | 59 +++ .../host/recorder/HostMediaRecorder.java | 466 +++++++++++++++++- .../host/recorder/PreviewServer.java | 12 + .../recorder/audio/AudioPreviewServer.java | 2 +- .../camera/Camera2MJPEGPreviewServer.java | 2 - .../camera/Camera2PreviewServerProvider.java | 19 +- .../host/recorder/camera/Camera2Recorder.java | 35 ++ .../recorder/camera/CameraVideoEncoder.java | 29 ++ .../ScreenCastPreviewServerProvider.java | 21 +- .../recorder/screen/ScreenCastRecorder.java | 35 ++ .../ScreenCastSurfaceDrawingThread.java | 2 +- .../host/recorder/ui/PreviewSurfaceView.java | 270 +++++++--- .../host/recorder/util/MovingRectThread.java | 211 ++++++++ .../host/recorder/util/PropertyUtil.java | 37 ++ .../res/layout/host_preview_surface_view.xml | 1 + .../res/xml/settings_host_recorder_main.xml | 14 +- .../gles/EGLSurfaceDrawingThread.java | 16 +- .../gles/SurfaceTextureRenderer.java | 18 +- .../video/CameraSurfaceVideoEncoder.java | 10 - .../streaming/video/CanvasVideoEncoder.java | 2 +- .../streaming/video/SurfaceVideoEncoder.java | 36 +- .../streaming/video/VideoQuality.java | 16 +- 39 files changed, 1715 insertions(+), 234 deletions(-) create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java index 7193ce3d41..4f4b9b7228 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java @@ -8,6 +8,7 @@ import androidx.appcompat.app.ActionBar; +import org.deviceconnect.android.deviceplugin.host.HostDevicePlugin; import org.deviceconnect.android.deviceplugin.host.R; import org.deviceconnect.android.deviceplugin.host.activity.HostDevicePluginBindActivity; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; @@ -60,17 +61,20 @@ public int getDisplayOrientation() { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - public HostMediaRecorder getRecorder() { - String recorderId = null; + public String getRecorderId() { Intent intent = getIntent(); if (intent != null) { - recorderId = intent.getStringExtra("recorder_id"); + return intent.getStringExtra("recorder_id"); } - return getHostDevicePlugin().getHostMediaRecorderManager().getRecorder(recorderId); + return null; } - public String getRecorderId() { - return getRecorder().getId(); + public HostMediaRecorder getRecorder() { + HostDevicePlugin plugin = getHostDevicePlugin(); + if (plugin != null) { + return plugin.getHostMediaRecorderManager().getRecorder(getRecorderId()); + } + return null; } public static Intent createSettingsActivityIntent(Context context, String recorderId, Integer rotationFlag) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java index 303eaf112a..fc500b9926 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java @@ -49,4 +49,12 @@ public String getRecorderId() { } return null; } + + public String getSettingName() { + Bundle args = getArguments(); + if (args != null) { + return args.getString("setting_name"); + } + return null; + } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java index a7b12905e8..cb9b2ec077 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java @@ -24,16 +24,11 @@ import java.util.List; public class SettingsBroadcastFragment extends SettingsParameterFragment { - /** - * マイムタイプを定義します. - */ - private static final String MIME_TYPE = "video/x-rtmp"; - private HostMediaRecorder mMediaRecorder; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-rtmp"); + getPreferenceManager().setSharedPreferencesName(getSettingName()); setPreferencesFromResource(R.xml.settings_host_recorder_broadcast, rootKey); } @@ -41,13 +36,13 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); setPreviewServerPort(); - setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewServerUrl(settings.getPort()); setPreviewSizePreference(settings); setPreviewVideoEncoderPreference(settings); - setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(MIME_TYPE), false); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); setPreviewCutOutReset(); @@ -62,6 +57,12 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } + + private HostMediaRecorder.StreamingSettings getStreamingSetting() { + HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); + return s.getBroadcaster(getSettingName()); + } + private void setPreviewServerPort() { setInputTypeNumber("port"); EditTextPreference pref = findPreference("port"); @@ -109,7 +110,7 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - mMediaRecorder.getSettings().setDrawingRange(null); + mMediaRecorder.getSettings().setCropRect(null); return false; }); } @@ -120,10 +121,10 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { - List previewSizes = getSupportedPreviewSizes(settings); + List previewSizes = getSupportedPreviewSizes(); if (!previewSizes.isEmpty()) { List entryValues = new ArrayList<>(); for (Size preview : previewSizes) { @@ -134,7 +135,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { pref.setEntryValues(entryValues.toArray(new String[0])); pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - Size previewSize = settings.getPreviewSize(MIME_TYPE); + Size previewSize = settings.getPreviewSize(); if (previewSize != null) { pref.setValue(getValueFromSize(previewSize)); } @@ -150,7 +151,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { * * @param settings レコーダ設定 */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { + private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("preview_encoder"); if (pref != null) { List list = settings.getSupportedVideoEncoders(); @@ -173,7 +174,7 @@ private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings setting * @param encoderName エンコーダ * @param reset リセットフラグ */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSettings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { ListPreference pref = findPreference("preview_profile_level"); if (pref != null) { List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); @@ -195,7 +196,7 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings setting if (reset) { pref.setValue("none"); } else { - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(MIME_TYPE); + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); if (pl != null) { pref.setValue(getProfileLevel(encoderName, pl)); } @@ -220,11 +221,11 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings setting /** * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. * - * @param settings レコーダ * @return サポートしているプレビューサイズのリスト */ @NonNull - private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + private List getSupportedPreviewSizes() { + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); List previewSizes = new ArrayList<>(); if (settings != null) { previewSizes.addAll(settings.getSupportedPreviewSizes()); @@ -358,20 +359,20 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { Size size = getSizeFromValue((String) newValue); if (size != null) { - settings.setPreviewSize(MIME_TYPE, size); + settings.setPreviewSize(size); } } else if ("port".equalsIgnoreCase(key)) { setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく try { - settings.setProfileLevel(MIME_TYPE, null); + settings.setProfileLevel(null); } catch (Exception e) { return false; } @@ -380,7 +381,7 @@ private Integer getDrawingRange(String key) { setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { try { - settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); } catch (Exception e) { return false; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java index dc540f266d..91e767d89c 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -1,7 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; import android.os.Bundle; -import android.util.Log; import android.util.Size; import androidx.annotation.NonNull; @@ -21,14 +20,11 @@ import java.util.List; public class SettingsMJPEGFragment extends SettingsParameterFragment { - - private static final String MIME_TYPE = "video/x-mjpeg"; - private HostMediaRecorder mMediaRecorder; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-mjpeg"); + getPreferenceManager().setSharedPreferencesName(getSettingName()); setPreferencesFromResource(R.xml.settings_host_recorder_mjpeg, rootKey); } @@ -36,10 +32,10 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); setPreviewServerPort(); - setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewServerUrl(settings.getPort()); setPreviewSizePreference(settings); setPreviewJpegQuality(); @@ -53,6 +49,11 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } + private HostMediaRecorder.StreamingSettings getStreamingSetting() { + HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); + return s.getPreviewServer(getSettingName()); + } + private void setPreviewServerPort() { setInputTypeNumber("port"); EditTextPreference pref = findPreference("port"); @@ -117,7 +118,7 @@ private void setPreviewCutOutReset() { if (bottom != null) { bottom.setText(null); } - mMediaRecorder.getSettings().setDrawingRange(null); + mMediaRecorder.getSettings().setCropRect(null); return false; }); } @@ -128,10 +129,10 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { - List previewSizes = getSupportedPreviewSizes(settings); + List previewSizes = getSupportedPreviewSizes(); if (!previewSizes.isEmpty()) { List entryValues = new ArrayList<>(); for (Size preview : previewSizes) { @@ -142,7 +143,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { pref.setEntryValues(entryValues.toArray(new String[0])); pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - Size previewSize = settings.getPreviewSize(MIME_TYPE); + Size previewSize = settings.getPreviewSize(); if (previewSize != null) { pref.setValue(getValueFromSize(previewSize)); } @@ -165,11 +166,11 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { /** * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. * - * @param settings レコーダ * @return サポートしているプレビューサイズのリスト */ @NonNull - private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + private List getSupportedPreviewSizes() { + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); List previewSizes = new ArrayList<>(); if (settings != null) { previewSizes.addAll(settings.getSupportedPreviewSizes()); @@ -238,13 +239,13 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { Size size = getSizeFromValue((String) newValue); if (size != null) { - settings.setPreviewSize(MIME_TYPE, size); + settings.setPreviewSize(size); } } else if ("port".equalsIgnoreCase(key)) { setPreviewServerUrl(Integer.parseInt((String) newValue)); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java index 96183a5a2f..3c22d85af7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java @@ -3,8 +3,12 @@ import android.os.Bundle; import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; import org.deviceconnect.android.deviceplugin.host.R; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; + +import java.util.List; import static androidx.navigation.fragment.NavHostFragment.findNavController; @@ -34,12 +38,78 @@ public boolean onPreferenceTreeClick(final Preference preference) { findNavController(this).navigate(R.id.action_main_to_srt); } else if ("recorder_settings_broadcast".equals(preference.getKey())) { findNavController(this).navigate(R.id.action_main_to_broadcast); - } else if ("recorder_settings_port".equals(preference.getKey())) { - findNavController(this).navigate(R.id.action_main_to_port); + } else { + Bundle params = new Bundle(); + params.putString("setting_name", preference.getKey()); + + HostMediaRecorder recorder = getRecorder(); + HostMediaRecorder.Settings settings = recorder.getSettings(); + HostMediaRecorder.StreamingSettings s = settings.getPreviewServer(preference.getKey()); + if (s != null) { + String mimeType = s.getMimeType(); + if ("video/x-mjpeg".equalsIgnoreCase(mimeType)) { + findNavController(this).navigate(R.id.action_main_to_mjpeg, params); + } else if ("video/x-rtp".equalsIgnoreCase(mimeType)) { + findNavController(this).navigate(R.id.action_main_to_rtsp, params); + } else if ("video/MP2T".equalsIgnoreCase(mimeType)) { + findNavController(this).navigate(R.id.action_main_to_srt, params); + } + } else { + s = settings.getBroadcaster(preference.getKey()); + if (s != null) { + findNavController(this).navigate(R.id.action_main_to_broadcast, params); + } + } } return super.onPreferenceTreeClick(preference); } + @Override + public void onBindService() { + addPreviewServerList(); + addBroadcasterList(); + } + + private boolean isNotExistPreference(String name) { + return findPreference(name) == null; + } + + private void addPreviewServerList() { + HostMediaRecorder recorder = getRecorder(); + HostMediaRecorder.Settings settings = recorder.getSettings(); + List previewServerList = settings.getPreviewServerList(); + + PreferenceCategory preferenceCategory = findPreference("recorder_settings_preview_server"); + for (String name : previewServerList) { + HostMediaRecorder.StreamingSettings s = settings.getPreviewServer(name); + if (isNotExistPreference(name)) { + Preference preference = new Preference(requireContext()); + preference.setTitle(s.getMimeType()); + preference.setKey(name); + preference.setIconSpaceReserved(false); + preferenceCategory.addPreference(preference); + } + } + } + + private void addBroadcasterList() { + HostMediaRecorder recorder = getRecorder(); + HostMediaRecorder.Settings settings = recorder.getSettings(); + List broadcasterList = settings.getBroadcasterList(); + + PreferenceCategory preferenceCategory = findPreference("recorder_settings_broadcaster"); + for (String name : broadcasterList) { + HostMediaRecorder.StreamingSettings s = settings.getBroadcaster(name); + if (isNotExistPreference(name)) { + Preference preference = new Preference(requireContext()); + preference.setTitle(s.getMimeType()); + preference.setKey(name); + preference.setIconSpaceReserved(false); + preferenceCategory.addPreference(preference); + } + } + } + private void startManager() { SettingsActivity a = (SettingsActivity) getActivity(); if (a != null && !a.isManagerStarted()) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java index 99350de387..b1f684df48 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -24,16 +24,11 @@ import java.util.List; public class SettingsRTSPFragment extends SettingsParameterFragment { - /** - * マイムタイプを定義します. - */ - private static final String MIME_TYPE = "video/x-rtp"; - private HostMediaRecorder mMediaRecorder; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-rtsp"); + getPreferenceManager().setSharedPreferencesName(getSettingName()); setPreferencesFromResource(R.xml.settings_host_recorder_rtsp, rootKey); } @@ -41,13 +36,13 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); setPreviewServerPort(); - setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewServerUrl(settings.getPort()); setPreviewSizePreference(settings); setPreviewVideoEncoderPreference(settings); - setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(MIME_TYPE), false); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); setPreviewCutOutReset(); @@ -62,6 +57,11 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } + private HostMediaRecorder.StreamingSettings getStreamingSetting() { + HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); + return s.getPreviewServer(getSettingName()); + } + private void setPreviewServerPort() { setInputTypeNumber("port"); EditTextPreference pref = findPreference("port"); @@ -109,7 +109,7 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - mMediaRecorder.getSettings().setDrawingRange(null); + mMediaRecorder.getSettings().setCropRect(null); return false; }); } @@ -120,10 +120,10 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { - List previewSizes = getSupportedPreviewSizes(settings); + List previewSizes = getSupportedPreviewSizes(); if (!previewSizes.isEmpty()) { List entryValues = new ArrayList<>(); for (Size preview : previewSizes) { @@ -134,7 +134,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { pref.setEntryValues(entryValues.toArray(new String[0])); pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - Size previewSize = settings.getPreviewSize(MIME_TYPE); + Size previewSize = settings.getPreviewSize(); if (previewSize != null) { pref.setValue(getValueFromSize(previewSize)); } @@ -150,7 +150,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { * * @param settings レコーダ設定 */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { + private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("preview_encoder"); if (pref != null) { List list = settings.getSupportedVideoEncoders(); @@ -173,7 +173,7 @@ private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings setting * @param encoderName エンコーダ * @param reset リセットフラグ */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSettings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { ListPreference pref = findPreference("preview_profile_level"); if (pref != null) { List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); @@ -195,7 +195,7 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings setting if (reset) { pref.setValue("none"); } else { - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(MIME_TYPE); + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); if (pl != null) { pref.setValue(getProfileLevel(encoderName, pl)); } @@ -220,11 +220,11 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings setting /** * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. * - * @param settings レコーダ * @return サポートしているプレビューサイズのリスト */ @NonNull - private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + private List getSupportedPreviewSizes() { + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); List previewSizes = new ArrayList<>(); if (settings != null) { previewSizes.addAll(settings.getSupportedPreviewSizes()); @@ -358,20 +358,20 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { Size size = getSizeFromValue((String) newValue); if (size != null) { - settings.setPreviewSize(MIME_TYPE, size); + settings.setPreviewSize(size); } } else if ("port".equalsIgnoreCase(key)) { setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく try { - settings.setProfileLevel(MIME_TYPE, null); + settings.setProfileLevel(null); } catch (Exception e) { return false; } @@ -380,7 +380,7 @@ private Integer getDrawingRange(String key) { setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { try { - settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); } catch (Exception e) { return false; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index 59d0e8b753..d1937054dd 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -25,16 +25,11 @@ import java.util.List; public class SettingsSRTFragment extends SettingsParameterFragment { - /** - * マイムタイプを定義します. - */ - private static final String MIME_TYPE = "video/MP2T"; - private HostMediaRecorder mMediaRecorder; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId() + "-srt"); + getPreferenceManager().setSharedPreferencesName(getSettingName()); setPreferencesFromResource(R.xml.settings_host_recorder_srt, rootKey); setSummaryOptionAuto(getString(R.string.pref_key_settings_srt_inputbw)); @@ -53,13 +48,13 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); setPreviewServerPort(); - setPreviewServerUrl(settings.getPort(MIME_TYPE)); + setPreviewServerUrl(settings.getPort()); setPreviewSizePreference(settings); setPreviewVideoEncoderPreference(settings); - setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(MIME_TYPE), false); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); setPreviewCutOutReset(); @@ -74,6 +69,11 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } + private HostMediaRecorder.StreamingSettings getStreamingSetting() { + HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); + return s.getPreviewServer(getSettingName()); + } + private void setSummaryOptionAuto(String name) { EditTextPreference inputBwPref = findPreference(name); if (inputBwPref != null) { @@ -136,7 +136,7 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - mMediaRecorder.getSettings().setDrawingRange(null); + mMediaRecorder.getSettings().setCropRect(null); return false; }); } @@ -147,10 +147,10 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { + private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { - List previewSizes = getSupportedPreviewSizes(settings); + List previewSizes = getSupportedPreviewSizes(); if (!previewSizes.isEmpty()) { List entryValues = new ArrayList<>(); for (Size preview : previewSizes) { @@ -161,7 +161,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { pref.setEntryValues(entryValues.toArray(new String[0])); pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - Size previewSize = settings.getPreviewSize(MIME_TYPE); + Size previewSize = settings.getPreviewSize(); if (previewSize != null) { pref.setValue(getValueFromSize(previewSize)); } @@ -177,7 +177,7 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { * * @param settings レコーダ設定 */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings settings) { + private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSettings settings) { ListPreference pref = findPreference("preview_encoder"); if (pref != null) { List list = settings.getSupportedVideoEncoders(); @@ -200,7 +200,7 @@ private void setPreviewVideoEncoderPreference(HostMediaRecorder.Settings setting * @param encoderName エンコーダ * @param reset リセットフラグ */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSettings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { ListPreference pref = findPreference("preview_profile_level"); if (pref != null) { List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); @@ -222,7 +222,7 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings setting if (reset) { pref.setValue("none"); } else { - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(MIME_TYPE); + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); if (pl != null) { pref.setValue(getProfileLevel(encoderName, pl)); } @@ -247,11 +247,11 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.Settings setting /** * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. * - * @param settings レコーダ * @return サポートしているプレビューサイズのリスト */ @NonNull - private static List getSupportedPreviewSizes(HostMediaRecorder.Settings settings) { + private List getSupportedPreviewSizes() { + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); List previewSizes = new ArrayList<>(); if (settings != null) { previewSizes.addAll(settings.getSupportedPreviewSizes()); @@ -385,20 +385,20 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { Size size = getSizeFromValue((String) newValue); if (size != null) { - settings.setPreviewSize(MIME_TYPE, size); + settings.setPreviewSize(size); } } else if ("port".equalsIgnoreCase(key)) { setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく try { - settings.setProfileLevel(MIME_TYPE, null); + settings.setProfileLevel(null); } catch (Exception e) { return false; } @@ -407,7 +407,7 @@ private Integer getDrawingRange(String key) { setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { try { - settings.setProfileLevel(MIME_TYPE, getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); } catch (Exception e) { return false; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java index 052a750bb0..314e02f818 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java @@ -45,6 +45,8 @@ import java.util.ArrayList; import java.util.List; +import javax.sql.ConnectionEventListener; + public class HostConnectionManager { private static final int NOTIFICATION_ID = 3527; @@ -674,10 +676,14 @@ public static boolean checkUsageAccessSettings(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return true; } - AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, - android.os.Process.myUid(), context.getPackageName()); - return mode == AppOpsManager.MODE_ALLOWED; + try { + AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, + android.os.Process.myUid(), context.getPackageName()); + return mode == AppOpsManager.MODE_ALLOWED; + } catch (Exception e) { + return false; + } } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index da1726c848..cb0588095d 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -18,6 +18,7 @@ import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; +import org.deviceconnect.android.deviceplugin.host.recorder.CropInterface; import org.deviceconnect.android.deviceplugin.host.recorder.HostDevicePhotoRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.HostDeviceStreamRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; @@ -135,7 +136,7 @@ public void onAllowed() { } // 切り抜き設定 - Rect rect = settings.getDrawingRange(); + Rect rect = settings.getCropRect(); if (rect != null) { Bundle drawingRect = new Bundle(); drawingRect.putInt("left", rect.left); @@ -166,6 +167,101 @@ public void onDisallowed() { } }; + private final DConnectApi mPutCropApi = new PutApi() { + @Override + public String getAttribute() { + return "crop"; + } + + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String mimeType = getMIMEType(request); + Integer previewClipLeft = parseInteger(request, "left"); + Integer previewClipTop = parseInteger(request, "top"); + Integer previewClipRight = parseInteger(request, "right"); + Integer previewClipBottom = parseInteger(request, "bottom"); + Integer previewClipDuration = parseInteger(request, "duration"); + + final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + int duration = previewClipDuration != null ? previewClipDuration : 0; + Rect start = recorder.getSettings().getCropRect("video/x-rtp"); + if (start == null) { + int width = recorder.getSettings().getPreviewSize().getWidth(); + int height = recorder.getSettings().getPreviewSize().getHeight(); + start = new Rect(0, 0, width, height); + } + Rect end = new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom); + + PreviewServer ps = recorder.getServerProvider().getServerByMimeType("video/x-rtp"); + if (ps instanceof CropInterface) { + ((CropInterface) ps).moveCropRect(start, end, duration); + } + + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; + } + }; + + private final DConnectApi mDeleteCropApi = new DeleteApi() { + @Override + public String getAttribute() { + return "crop"; + } + + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String mimeType = getMIMEType(request); + + final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + PreviewServer ps = recorder.getServerProvider().getServerByMimeType("video/x-rtp"); + if (ps instanceof CropInterface) { + ((CropInterface) ps).setCropRect(null); + } + + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; + } + }; + + // GET /gotapi/mediaStreamRecording/options private final DConnectApi mGetOptionsApi = new GetApi() { @Override @@ -436,9 +532,9 @@ private void setOptions(final Intent request, final Intent response) { } if (previewClipReset != null && previewClipReset) { - settings.setDrawingRange(null); + settings.setCropRect(null); } else if (previewClipLeft != null) { - settings.setDrawingRange(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); + settings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); } try { @@ -1125,6 +1221,9 @@ public HostMediaStreamingRecordingProfile(final HostMediaRecorderManager mgr, fi addApi(mGetMediaRecorderApi); + addApi(mPutCropApi); + addApi(mDeleteCropApi); + addApi(mGetOptionsApi); addApi(mPutOptionsApi); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java index 4c84cf06a4..1db1dba759 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -import android.graphics.Rect; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioPlaybackCaptureConfiguration; @@ -27,9 +26,20 @@ public abstract class AbstractBroadcaster implements Broadcaster { */ private final HostMediaRecorder mRecorder; - public AbstractBroadcaster(HostMediaRecorder recorder, String broadcastURI) { + /** + * 配信名. + */ + private final String mName; + + public AbstractBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { mRecorder = recorder; mBroadcastURI = broadcastURI; + mName = name; + } + + @Override + public String getName() { + return mName; } @Override @@ -58,6 +68,10 @@ public void onConfigChange() { } } + @Override + public void release() { + } + /** * 映像の設定を取得します. * @@ -89,6 +103,15 @@ public HostMediaRecorder getRecorder() { return mRecorder; } + /** + * Broadcaster の設定を取得します. + * + * @return 設定 + */ + public HostMediaRecorder.StreamingSettings getStreamingSettings() { + return mRecorder.getSettings().getBroadcaster(getName()); + } + /** * VideoEncoder の設定に、HostMediaRecorder の設定を反映します. * @@ -107,7 +130,7 @@ public void setVideoQuality(VideoQuality videoQuality) { int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); videoQuality.setVideoWidth(w); videoQuality.setVideoHeight(h); - videoQuality.setDrawingRange(settings.getDrawingRange(getMimeType())); + videoQuality.setCropRect(settings.getCropRect(getMimeType())); videoQuality.setBitRate(settings.getPreviewBitRate(getMimeType())); videoQuality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval(getMimeType())); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index d45025f430..cdde3b39f8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -30,7 +30,7 @@ public abstract class AbstractMJPEGPreviewServer extends AbstractPreviewServer { private MJPEGServer mMJPEGServer; public AbstractMJPEGPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { - super(context, recorder, useSSL); + super(context, recorder, recorder.getId() + "-mjpeg", useSSL); } // PreviewServer @@ -141,7 +141,7 @@ private void setMJPEGQuality(MJPEGQuality quality) { quality.setHeight(h); quality.setQuality(settings.getPreviewQuality(getMimeType())); quality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); - quality.setDrawingRange(settings.getDrawingRange(getMimeType())); + quality.setDrawingRange(settings.getCropRect(getMimeType())); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java index 549246ba0a..d516b3aa44 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java @@ -1,6 +1,7 @@ package org.deviceconnect.android.deviceplugin.host.recorder; import android.content.Context; +import android.graphics.Rect; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioPlaybackCaptureConfiguration; @@ -10,11 +11,13 @@ import org.deviceconnect.android.deviceplugin.host.BuildConfig; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; +import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; import org.deviceconnect.android.libmedia.streaming.audio.MicAudioQuality; import org.deviceconnect.android.libmedia.streaming.audio.filter.HighPassFilter; import org.deviceconnect.android.libmedia.streaming.audio.filter.LowPassFilter; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; +import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; import javax.net.ssl.SSLContext; @@ -22,7 +25,7 @@ /** * プレビュー配信サーバ. */ -public abstract class AbstractPreviewServer implements PreviewServer { +public abstract class AbstractPreviewServer implements PreviewServer, CropInterface { protected static final boolean DEBUG = BuildConfig.DEBUG; protected static final String TAG = "host.dplugin"; @@ -56,41 +59,72 @@ public abstract class AbstractPreviewServer implements PreviewServer { */ private final boolean mUseSSL; + /** + * 名前. + */ + private final String mName; + + /** + * 描画スレッド.å + */ + private MovingRectThread mMovingRectThread; + + private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = new MovingRectThread.OnEventListener() { + @Override + public void onMoved(Rect rect) { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(new Rect(rect)); + } + mHostMediaRecorder.getSettings().setCropRect(getMimeType(), new Rect(rect)); + postOnMoved(rect); + } + }; + /** * コンストラクタ. * *

- * デフォルトでは、mute は true に設定しています。 + * デフォルトでは、ミュート設定は true に設定しています。 * デフォルトでは、mUseSSL は false に設定します。 *

* * @param context コンテキスト * @param recorder プレビューで表示するレコーダ + * @param name サーバ名 */ - public AbstractPreviewServer(Context context, HostMediaRecorder recorder) { - this(context, recorder, false); + public AbstractPreviewServer(Context context, HostMediaRecorder recorder, String name) { + this(context, recorder, name, false); } /** * コンストラクタ. * *

- * デフォルトでは、mute は true に設定しています。 + * デフォルトでは、ミュート設定は true に設定しています。 *

* * @param context コンテキスト * @param recorder プレビューで表示するレコーダ + * @param name 名前 * @param useSSL SSL使用フラグ */ - public AbstractPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { + public AbstractPreviewServer(Context context, HostMediaRecorder recorder, String name, boolean useSSL) { mContext = context; mHostMediaRecorder = recorder; mUseSSL = useSSL; mMute = true; + mName = name; + startMovingRectThread(); } // Implements PreviewServer methods. + @Override + public String getName() { + return mName; + } + @Override public int getPort() { return mPort; @@ -142,6 +176,114 @@ public SSLContext getSSLContext() { return mSSLContext; } + @Override + public void release() { + stopMovingRectThread(); + } + + // CropInterface implements + + private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); + + @Override + public void moveCropRect(Rect start, Rect end, int duration) { + if (end != null) { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + if (settings.getCropRect(getMimeType()) == null) { + postOnAdded(end); + } + } else { + postOnRemove(); + } + + if (mMovingRectThread != null) { + mMovingRectThread.move(start, end, duration); + } else { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + settings.setCropRect(getMimeType(), end); + + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(end); + } + } + } + + @Override + public void setCropRect(Rect rect) { + if (rect != null) { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + if (settings.getCropRect(getMimeType()) == null) { + postOnAdded(rect); + } + } else { + postOnRemove(); + } + + if (rect == null || mMovingRectThread == null) { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + settings.setCropRect(getMimeType(), rect); + + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(rect); + } + } else { + mMovingRectThread.set(rect); + } + } + + @Override + public Rect getCropRect() { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + return settings.getCropRect(getMimeType()); + } + + @Override + public void addOnEventListener(OnEventListener listener) { + mOnEventListeners.add(listener); + } + + @Override + public void removeOnEventListener(OnEventListener listener) { + mOnEventListeners.remove(listener); + } + + private void postOnAdded(Rect rect) { + for (OnEventListener l : mOnEventListeners.get()) { + l.onAdded(this, rect); + } + } + + private void postOnRemove() { + for (OnEventListener l : mOnEventListeners.get()) { + l.onRemoved(this); + } + } + + private void postOnMoved(Rect rect) { + for (OnEventListener l : mOnEventListeners.get()) { + l.onMoved(this, rect); + } + } + + private void startMovingRectThread() { + if (mMovingRectThread != null) { + return; + } + + mMovingRectThread = new MovingRectThread(); + mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); + mMovingRectThread.start(); + } + + private void stopMovingRectThread() { + if (mMovingRectThread != null) { + mMovingRectThread.stop(); + mMovingRectThread = null; + } + } + /** * コンテキストを取得します. * @@ -160,6 +302,15 @@ public HostMediaRecorder getRecorder() { return mHostMediaRecorder; } + /** + * プレビューの設定を取得します. + * + * @return プレビュー設定 + */ + public HostMediaRecorder.StreamingSettings getStreamingSettings() { + return mHostMediaRecorder.getSettings().getPreviewServer(getName()); + } + /** * 映像の設定を取得します. * @@ -189,26 +340,23 @@ protected AudioQuality getAudioQuality() { */ public void setVideoQuality(VideoQuality videoQuality) { HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(getMimeType()); - if (previewSize == null) { - previewSize = settings.getPreviewSize(); - } + Size previewSize = settings.getPreviewSize(); int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); videoQuality.setVideoWidth(w); videoQuality.setVideoHeight(h); - videoQuality.setDrawingRange(settings.getDrawingRange(getMimeType())); - videoQuality.setBitRate(settings.getPreviewBitRate(getMimeType())); - videoQuality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); - videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval(getMimeType())); - videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder(getMimeType())); - videoQuality.setIntraRefresh(settings.getIntraRefresh(getMimeType())); - videoQuality.setProfile(settings.getProfile(getMimeType())); - videoQuality.setLevel(settings.getLevel(getMimeType())); - if (settings.getPreviewBitRateMode(getMimeType()) != null) { + videoQuality.setCropRect(settings.getCropRect()); + videoQuality.setBitRate(settings.getPreviewBitRate()); + videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); + videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); + videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); + videoQuality.setIntraRefresh(settings.getIntraRefresh()); + videoQuality.setProfile(settings.getProfile()); + videoQuality.setLevel(settings.getLevel()); + if (settings.getPreviewBitRateMode() != null) { switch (settings.getPreviewBitRateMode()) { default: case VBR: diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java index b6d23927db..96feac5565 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java @@ -21,7 +21,7 @@ public abstract class AbstractRTMPBroadcaster extends AbstractBroadcaster { private OnEventListener mOnBroadcasterEventListener; public AbstractRTMPBroadcaster(HostMediaRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + super(recorder, recorder.getId() + "-rtmp", broadcastURI); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java index 25c98723aa..722783cd62 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java @@ -34,7 +34,7 @@ public AbstractRTSPPreviewServer(Context context, HostMediaRecorder recorder) { } public AbstractRTSPPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { - super(context, recorder, useSSL); + super(context, recorder, recorder.getId() + "-rtsp", useSSL); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java index 66df06d39f..10c8a42f3f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java @@ -21,7 +21,7 @@ public abstract class AbstractSRTBroadcaster extends AbstractBroadcaster { private OnEventListener mOnBroadcasterEventListener; public AbstractSRTBroadcaster(HostMediaRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + super(recorder, broadcastURI, recorder.getId() + "-rtmp"); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index e04b069a3a..367b8f4630 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -31,7 +31,7 @@ public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder) { } public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { - super(context, recorder, useSSL); + super(context, recorder, recorder.getId() + "-srt", useSSL); } @Override @@ -169,12 +169,26 @@ private void restartAudioEncoder() { /** * SRT 用の映像エンコーダを作成します. * + * null を返却した場合には、映像は配信しません。 + * + * このメソッドを実装することでエンコーダを切り替えます。 + * * @return SRT 用の映像エンコーダ */ protected VideoEncoder createVideoEncoder() { return null; } + /** + * SRT 用の音声エンコーダを作成します. + * + * null を返却した場合には、音声は配信しません。 + * + * デフォルトで、 aac のエンコーダを作成して返却します。 + * aac 以外のエンコーダを実装する場合には、このメソッドをオーバーライドします。 + * + * @return SRT 用の音声エンコーダ + */ protected AudioEncoder createAudioEncoder() { HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java index b9a6fdf8ae..4a53bda9bd 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java @@ -1,6 +1,13 @@ package org.deviceconnect.android.deviceplugin.host.recorder; public interface Broadcaster { + /** + * 名前を取得します. + * + * @return 名前 + */ + String getName(); + /** * マイムタイプを取得します. * @@ -58,6 +65,11 @@ public interface Broadcaster { */ void onConfigChange(); + /** + * 配信先を解放します. + */ + void release(); + /** * ブロードキャストの開始結果を通知するコールバック. */ diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java new file mode 100644 index 0000000000..8202ec4584 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java @@ -0,0 +1,59 @@ +package org.deviceconnect.android.deviceplugin.host.recorder; + +import android.graphics.Rect; + +public interface CropInterface { + /** + * start で指定された矩形から end で指定された矩形に移動します. + * + * @param start 開始する矩形 + * @param end 停止する矩形 + * @param duration 移動する時間(ミリ秒) + */ + void moveCropRect(Rect start, Rect end, int duration); + + /** + * クロップ範囲を設定します. + * + * @param rect クロップする範囲の矩形 + */ + void setCropRect(Rect rect); + + /** + * クロップ範囲を取得します. + * + * @return クロップする範囲の矩形 + */ + Rect getCropRect(); + + /** + * イベントリスナーを追加します. + * + * @param listener 追加するリスナー + */ + void addOnEventListener(OnEventListener listener); + + /** + * イベントリスナーを削除します. + * + * @param listener 削除するリスナー + */ + void removeOnEventListener(OnEventListener listener); + + interface OnEventListener { + /** + * クロップする矩形が追加されたことを通知します. + */ + void onAdded(CropInterface crop, Rect cropRect); + + /** + * クロップする矩形が削除されたことを通知します. + */ + void onRemoved(CropInterface crop); + + /** + * クロップする矩形が移動したことを通知します. + */ + void onMoved(CropInterface crop, Rect cropRect); + } +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index 96e1bd5013..b7ca8ebfab 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -460,6 +460,409 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host void onError(Exception e); } + public class StreamingSettings { + private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; + private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; + private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; + private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; + + private final PropertyUtil mProperty; + + public StreamingSettings(Context context, String name) { + mProperty = new PropertyUtil(context, name); + } + + public String getMimeType() { + return mProperty.getString("mimeType", null); + } + + public void setMimeType(String mimeType) { + mProperty.put("mimeType", mimeType); + } + + /** + * サーバ用のポート番号を取得します. + * + * @return サーバ用のポート番号 + */ + public Integer getPort() { + return mProperty.getInteger("port", 0); + } + + /** + * サーバ用のポート番号を設定します. + * + * @param port サーバ用のポート番号 + */ + public void setPort(int port) { + mProperty.put("port", port); + } + + //// MediaCodec + + /** + * プレビューサイズを取得します. + * + * @return プレビューサイズ + */ + public Size getPreviewSize() { + return mProperty.getSize("preview_size_width", "preview_size_height"); + } + + /** + * プレビューサイズを設定します. + * + * @param previewSize プレビューサイズ + */ + public void setPreviewSize(Size previewSize) { + mProperty.put("preview_size_width", "preview_size_height", previewSize); + } + + /** + * プレビュー配信エンコード名を取得します. + * + * @return エンコード名 + */ + public VideoEncoderName getPreviewEncoderName() { + return VideoEncoderName.nameOf(getPreviewEncoder()); + } + + /** + * プレビューの配信エンコードの名前を取得します. + * + * 未設定の場合は h264 を返却します。 + * + * @return プレビューの配信エンコードの名前 + */ + public String getPreviewEncoder() { + return mProperty.getString("preview_encoder", DEFAULT_PREVIEW_ENCODER); + } + + /** + * プレビューの配信エンコードの名前を設定します. + * + * @param encoder プレビューの配信エンコードの名前 + */ + public void setPreviewEncoder(String encoder) { + if (encoder == null) { + mProperty.remove("preview_encoder"); + } else { + if (!isSupportedVideoEncoder(encoder)) { + throw new IllegalArgumentException("encoder is not supported."); + } + mProperty.put("preview_encoder", encoder); + } + } + + /** + * プロファイルとレベルを取得します. + * + * 未設定の場合には、null を返却します。 + * + * @return プロファイルとレベル + */ + public ProfileLevel getProfileLevel() { + Integer profile = mProperty.getInteger("preview_profile", null); + Integer level = mProperty.getInteger("preview_level", null); + if (profile != null && level != null) { + return new ProfileLevel(profile, level); + } + return null; + } + + /** + * プロファイルとレベルを設定します. + * + * null が設定された場合には、値を削除して未設定にします。 + * + * サポートされていないプロファイルとレベルが設定された場合には例外を発生します。 + * + * @param pl プロファイルとレベル + */ + public void setProfileLevel(ProfileLevel pl) { + if (pl == null) { + mProperty.remove("preview_profile"); + mProperty.remove("preview_level"); + } else { + if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) { + throw new IllegalArgumentException("profile and level are not supported."); + } + mProperty.put("preview_profile", pl.getProfile()); + mProperty.put("preview_level", pl.getLevel()); + } + } + + /** + * 設定されているプロファイルを取得します. + * + * @return プロファイル + */ + public Integer getProfile() { + return mProperty.getInteger("preview_profile", 0); + } + + /** + * 設定されているレベルを取得します. + * + * @return レベル + */ + public Integer getLevel() { + return mProperty.getInteger("preview_level", 0); + } + + /** + * フレームレートを取得します. + * + * @return フレームレート + */ + public int getPreviewMaxFrameRate() { + return mProperty.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); + } + + /** + * フレームレートを設定します. + * + * @param previewMaxFrameRate フレームレート + */ + public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { + if (previewMaxFrameRate <= 0) { + throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); + } + mProperty.put("preview_framerate", previewMaxFrameRate); + } + + /** + * ビットレートを取得します. + * + * @return ビットレート(byte) + */ + public int getPreviewBitRate() { + return mProperty.getInteger("preview_bitrate", DEFAULT_PREVIEW_BITRATE); + } + + /** + * ビットレートを設定します. + * + * @param previewBitRate ビットレート(byte) + */ + public void setPreviewBitRate(int previewBitRate) { + if (previewBitRate <= 0) { + throw new IllegalArgumentException("previewBitRate is zero or negative."); + } + mProperty.put("preview_bitrate", String.valueOf(previewBitRate)); + } + + /** + * ビットレートモードを取得します. + * + * @return ビットレートモード + */ + public BitRateMode getPreviewBitRateMode() { + return BitRateMode.nameOf(mProperty.getString("preview_bitrate_mode", null)); + } + + /** + * ビットレートモードを設定します. + * + * @param mode ビットレートモード + */ + public void setPreviewBitRateMode(BitRateMode mode) { + if (mode == null) { + mProperty.remove("preview_bitrate_mode"); + } else { + mProperty.put("preview_bitrate_mode", mode.getName()); + } + } + + /** + * キーフレームインターバルを取得します. + * + * @return キーフレームを発行する間隔(ミリ秒) + */ + public int getPreviewKeyFrameInterval() { + return mProperty.getInteger("preview_i_frame_interval", DEFAULT_PREVIEW_KEY_FRAME_INTERVAL); + } + + /** + * キーフレームインターバルを設定します. + * + * @param previewKeyFrameInterval キーフレームを発行する間隔(ミリ秒) + */ + public void setPreviewKeyFrameInterval(int previewKeyFrameInterval) { + if (previewKeyFrameInterval <= 0) { + throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); + } + mProperty.put("preview_i_frame_interval", previewKeyFrameInterval); + } + + /** + * ソフトウェアエンコーダを優先的に使用するフラグを確認します. + * + * @return ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false + */ + public boolean isUseSoftwareEncoder() { + return mProperty.getBoolean("preview_use_software_encoder", false); + } + + /** + * ソフトウェアエンコーダを優先的に使用するフラグを設定します. + * + * @param used ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false + */ + public void setUseSoftwareEncoder(boolean used) { + mProperty.put("preview_use_software_encoder", used); + } + + /** + * イントラリフレッシュのフレーム数を取得します. + * + * @return イントラリフレッシュのフレーム数 + */ + public Integer getIntraRefresh() { + return mProperty.getInteger("preview_intra_refresh", 0); + } + + /** + * イントラリフレッシュのフレーム数を設定します. + * + * @param refresh イントラリフレッシュのフレーム数 + */ + public void setIntraRefresh(Integer refresh) { + if (refresh == null) { + mProperty.remove("preview_intra_refresh"); + } else { + mProperty.put("preview_intra_refresh", refresh); + } + } + + /** + * 切り抜き範囲を取得します. + * + * 範囲ば設定されていない場合には、null を返却します. + * + * @return 切り抜き範囲 + */ + public Rect getCropRect() { + return mProperty.getRect("preview_clip_left", + "preview_clip_top", + "preview_clip_right", + "preview_clip_bottom"); + } + + /** + * 切り抜き範囲を設定します. + * + * 引数に null が指定された場合には、切り抜き範囲を削除します。 + * + * @param rect 切り抜き範囲 + */ + public void setCropRect(Rect rect) { + if (rect == null) { + mProperty.remove("preview_clip_left"); + mProperty.remove("preview_clip_top"); + mProperty.remove("preview_clip_right"); + mProperty.remove("preview_clip_bottom"); + } else { + mProperty.put( + "preview_clip_left", + "preview_clip_top", + "preview_clip_right", + "preview_clip_bottom", + rect); + } + } + + /** + * プレビューの品質を取得します. + * + * @return プレビューの品質 + */ + public int getPreviewQuality() { + return mProperty.getInteger("preview_jpeg_quality", 80); + } + + /** + * プレビューの品質を設定します. + * + * 0 から 100 の間で設定することができます。 + * それ以外は例外が発生します。 + * + * @param quality プレビューの品質 + */ + public void setPreviewQuality(int quality) { + if (quality < 0) { + throw new IllegalArgumentException("quality is negative value."); + } + if (quality > 100) { + throw new IllegalArgumentException("quality is over 100."); + } + mProperty.put("preview_jpeg_quality", quality); + } + + /** + * サポートしているエンコーダのリストを取得します. + * + * @return サポートしているエンコーダのリスト + */ + public List getSupportedVideoEncoders() { + List list = new ArrayList<>(); + List supported = CapabilityUtil.getSupportedVideoEncoders(); + for (VideoEncoderName encoderName : VideoEncoderName.values()) { + if (supported.contains(encoderName.getMimeType())) { + list.add(encoderName.getName()); + } + } + return list; + } + + /** + * サポートしているプロファイル・レベルの一覧を取得します. + * + * @return サポートしているプロファイル・レベルの一覧 + */ + public List getSupportedProfileLevel() { + VideoEncoderName encoderName = getPreviewEncoderName(); + return CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + } + + /** + * 指定されたエンコーダがサポートされているか確認します. + * + * @param encoder エンコーダ名 + * @return サポートされている場合はtrue、それ以外はfalse + */ + public boolean isSupportedVideoEncoder(String encoder) { + List encoderList = getSupportedVideoEncoders(); + if (encoderList != null) { + for (String e : encoderList) { + if (e.equalsIgnoreCase(encoder)) { + return true; + } + } + } + return false; + } + + /** + * 指定されたプロファイルとレベルがサポートされているか確認します. + * + * @param profile プロファイル + * @param level レベル + * @return サポートされている場合はtrue、それ以外はfalse + */ + public boolean isSupportedProfileLevel(int profile, int level) { + List list = getSupportedProfileLevel(); + if (list != null) { + for (ProfileLevel pl : list) { + if (profile == pl.getProfile() && level == pl.getLevel()) { + return true; + } + } + } + return false; + } + } + /** * HostMediaRecorder の設定を保持するクラス. */ @@ -510,6 +913,61 @@ public void clear() { } } + public List getPreviewServerList() { + return mProperty.getArrayString("preview_server_list"); + } + + public StreamingSettings getPreviewServer(String name) { + List previewServerList = getPreviewServerList(); + if (previewServerList.contains(name)) { + return new StreamingSettings(mContext, name); + } + return null; + } + + public void addPreviewServer(String name) { + List previewServerList = getPreviewServerList(); + if (previewServerList.contains(name)) { + return; + } + previewServerList.add(name); + mProperty.put("preview_server_list", previewServerList); + } + + public void removePreviewServer(String name) { + List previewServerList = getPreviewServerList(); + previewServerList.remove(name); + mProperty.put("preview_server_list", previewServerList); + } + + public List getBroadcasterList() { + return mProperty.getArrayString("broadcaster_list"); + } + + public StreamingSettings getBroadcaster(String name) { + List broadcasterList = getBroadcasterList(); + if (broadcasterList.contains(name)) { + return new StreamingSettings(mContext, name); + } + return null; + } + + public void addBroadcaster(String name) { + List broadcasterList = getBroadcasterList(); + if (broadcasterList.contains(name)) { + return; + } + broadcasterList.add(name); + mProperty.put("broadcaster_list", broadcasterList); + } + + public void removeBroadcaster(String name) { + List broadcasterList = getBroadcasterList(); + broadcasterList.remove(name); + mProperty.put("broadcaster_list", broadcasterList); + } + + // カメラ設定 /** @@ -1266,7 +1724,7 @@ public void setIntraRefresh(Integer refresh) { } } - public Rect getDrawingRange(String mimeType) { + public Rect getCropRect(String mimeType) { PropertyUtil prof = mPropertyMap.get(mimeType); if (prof != null) { return prof.getRect("preview_clip_left", @@ -1277,7 +1735,7 @@ public Rect getDrawingRange(String mimeType) { return null; } - public void setDrawingRange(String mimeType, Rect rect) { + public void setCropRect(String mimeType, Rect rect) { PropertyUtil prof = mPropertyMap.get(mimeType); if (prof != null) { if (rect == null) { @@ -1303,7 +1761,7 @@ public void setDrawingRange(String mimeType, Rect rect) { * * @return 切り抜き範囲 */ - public Rect getDrawingRange() { + public Rect getCropRect() { return mProperty.getRect("preview_clip_left", "preview_clip_top", "preview_clip_right", @@ -1317,7 +1775,7 @@ public Rect getDrawingRange() { * * @param rect 切り抜き範囲 */ - public void setDrawingRange(Rect rect) { + public void setCropRect(Rect rect) { if (rect == null) { mProperty.remove("preview_clip_left"); mProperty.remove("preview_clip_top"); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java index 7dc5f8289d..8091aced4b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java @@ -12,6 +12,13 @@ * プレビュー配信用サーバを定義するインターフェース. */ public interface PreviewServer { + /** + * サーバ名を取得します. + * + * @return サーバ名 + */ + String getName(); + /** * サーバが配信するプレビューのマイムタイプを取得します. * @@ -103,6 +110,11 @@ public interface PreviewServer { */ long getBPS(); + /** + * サーバを解放します. + */ + void release(); + /** * Callback interface used to receive the result of starting a web server. */ diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java index bb0a0dc497..3032c20217 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java @@ -13,7 +13,7 @@ public class AudioPreviewServer extends AbstractPreviewServer { * @param recorder プレビューで表示するレコーダ */ public AudioPreviewServer(Context context, HostMediaRecorder recorder) { - super(context, recorder); + super(context, recorder, recorder.getId() + "-audio"); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java index 4340f54d68..bbdc5bb7c2 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java @@ -14,8 +14,6 @@ /** * カメラのプレビューをMJPEG形式で配信するサーバー. - * - * {@link SurfaceTexture} をもとに実装. */ class Camera2MJPEGPreviewServer extends AbstractMJPEGPreviewServer { Camera2MJPEGPreviewServer(Context context, Camera2Recorder recorder, boolean useSSL) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java index daff366917..cd53d3a0ec 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java @@ -7,6 +7,7 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; import android.content.Context; +import android.util.Log; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServerProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; @@ -37,10 +38,20 @@ class Camera2PreviewServerProvider extends AbstractPreviewServerProvider { mOverlayManager = new OverlayManager(context, recorder); - addServer(new Camera2MJPEGPreviewServer(context, recorder, false)); - addServer(new Camera2MJPEGPreviewServer(context, recorder, true)); - addServer(new Camera2RTSPPreviewServer(context, recorder)); - addServer(new Camera2SRTPreviewServer(context, recorder)); + List previewList = recorder.getSettings().getPreviewServerList(); + for (String name : previewList) { + HostMediaRecorder.StreamingSettings s = recorder.getSettings().getPreviewServer(name); + if (s != null) { + String mimeType = s.getMimeType(); + if ("video/x-mjpeg".equalsIgnoreCase(mimeType)) { + addServer(new Camera2MJPEGPreviewServer(context, recorder, false)); + } else if ("video/x-rtp".equalsIgnoreCase(mimeType)) { + addServer(new Camera2RTSPPreviewServer(context, recorder)); + } else if ("video/MP2T".equalsIgnoreCase(mimeType)) { + addServer(new Camera2SRTPreviewServer(context, recorder)); + } + } + } } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index c60d343494..ef1f4b510f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -13,6 +13,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.util.Log; import android.util.Range; import android.util.Size; import android.util.SparseIntArray; @@ -211,6 +212,40 @@ private void initSupportedSettings() { mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); + mSettings.addPreviewServer(getId() + "-mjpeg"); + StreamingSettings mjpeg = mSettings.getPreviewServer(getId() + "-mjpeg"); + mjpeg.setMimeType(MIME_TYPE_MJPEG); + mjpeg.setPort(11000 + mFacing.mValue); + mjpeg.setPreviewSize(options.getDefaultPreviewSize()); + mjpeg.setPreviewQuality(80); + mjpeg.setPreviewMaxFrameRate(30); + + mSettings.addPreviewServer(getId() + "-rtsp"); + StreamingSettings rtsp = mSettings.getPreviewServer(getId() + "-rtsp"); + rtsp.setMimeType(MIME_TYPE_RTSP); + rtsp.setPort(12000 + mFacing.mValue); + rtsp.setPreviewSize(options.getDefaultPreviewSize()); + rtsp.setPreviewBitRate(2 * 1024 * 1024); + rtsp.setPreviewMaxFrameRate(30); + rtsp.setPreviewKeyFrameInterval(5); + + mSettings.addPreviewServer(getId() + "-srt"); + StreamingSettings srt = mSettings.getPreviewServer(getId() + "-srt"); + srt.setMimeType(MIME_TYPE_SRT); + srt.setPort(13000 + mFacing.mValue); + srt.setPreviewSize(options.getDefaultPreviewSize()); + srt.setPreviewBitRate(2 * 1024 * 1024); + srt.setPreviewMaxFrameRate(30); + srt.setPreviewKeyFrameInterval(5); + + mSettings.addBroadcaster(getId() + "-rtmp"); + StreamingSettings rtmp = mSettings.getBroadcaster(getId() + "-rtmp"); + rtmp.setMimeType(MIME_TYPE_RTMP); + rtmp.setPreviewSize(options.getDefaultPreviewSize()); + rtmp.setPreviewBitRate(2 * 1024 * 1024); + rtmp.setPreviewMaxFrameRate(30); + rtmp.setPreviewKeyFrameInterval(5); + mSettings.finishInitialization(); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java index cf4ca573d0..c8c96c8392 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java @@ -1,5 +1,8 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; +import android.graphics.Rect; + +import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; import org.deviceconnect.android.libmedia.streaming.video.CameraVideoQuality; import org.deviceconnect.android.libmedia.streaming.video.SurfaceVideoEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; @@ -28,4 +31,30 @@ public CameraVideoEncoder(Camera2Recorder recorder, String mimeType) { public VideoQuality getVideoQuality() { return mVideoQuality; } + + private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(new Rect(rect)); + } + }; + + private MovingRectThread mMovingRectThread; + + public void startMovingRectThread() { + if (mMovingRectThread != null) { + return; + } + + mMovingRectThread = new MovingRectThread(); + mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); + mMovingRectThread.start(); + } + + public void stopMovingRectThread() { + if (mMovingRectThread != null) { + mMovingRectThread.stop(); + mMovingRectThread = null; + } + } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java index 8a2b443015..374186ee8c 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java @@ -3,6 +3,9 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServerProvider; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; + +import java.util.List; /** * スクリーンキャストのプレビューを配信するサーバを管理するクラス. @@ -11,9 +14,19 @@ class ScreenCastPreviewServerProvider extends AbstractPreviewServerProvider { ScreenCastPreviewServerProvider(Context context, ScreenCastRecorder recorder) { super(context, recorder); - addServer(new ScreenCastMJPEGPreviewServer(context, recorder, false)); - addServer(new ScreenCastMJPEGPreviewServer(context, recorder, true)); - addServer(new ScreenCastRTSPPreviewServer(context, recorder)); - addServer(new ScreenCastSRTPreviewServer(context, recorder)); + List previewList = recorder.getSettings().getPreviewServerList(); + for (String name : previewList) { + HostMediaRecorder.StreamingSettings s = recorder.getSettings().getPreviewServer(name); + if (s != null) { + String mimeType = s.getMimeType(); + if ("video/x-mjpeg".equalsIgnoreCase(mimeType)) { + addServer(new ScreenCastMJPEGPreviewServer(context, recorder, false)); + } else if ("video/x-rtp".equalsIgnoreCase(mimeType)) { + addServer(new ScreenCastRTSPPreviewServer(context, recorder)); + } else if ("video/MP2T".equalsIgnoreCase(mimeType)) { + addServer(new ScreenCastSRTPreviewServer(context, recorder)); + } + } + } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index f855ce67b4..513374b028 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -162,6 +162,41 @@ private void initSupportedSettings() { mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); + mSettings.addPreviewServer(getId() + "-mjpeg"); + StreamingSettings mjpeg = mSettings.getPreviewServer(getId() + "-mjpeg"); + mjpeg.setMimeType(MIME_TYPE_MJPEG); + mjpeg.setPort(21000); + mjpeg.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); + mjpeg.setPreviewQuality(80); + mjpeg.setPreviewMaxFrameRate(30); + + mSettings.addPreviewServer(getId() + "-rtsp"); + StreamingSettings rtsp = mSettings.getPreviewServer(getId() + "-rtsp"); + rtsp.setMimeType(MIME_TYPE_RTSP); + rtsp.setPort(22000); + rtsp.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); + rtsp.setPreviewBitRate(2 * 1024 * 1024); + rtsp.setPreviewMaxFrameRate(30); + rtsp.setPreviewKeyFrameInterval(5); + + mSettings.addPreviewServer(getId() + "-srt"); + StreamingSettings srt = mSettings.getPreviewServer(getId() + "-srt"); + srt.setMimeType(MIME_TYPE_SRT); + srt.setPort(23000); + srt.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); + srt.setPreviewBitRate(2 * 1024 * 1024); + srt.setPreviewMaxFrameRate(30); + srt.setPreviewKeyFrameInterval(5); + + mSettings.addBroadcaster(getId() + "-rtmp"); + StreamingSettings rtmp = mSettings.getBroadcaster(getId() + "-rtmp"); + rtmp.setMimeType(MIME_TYPE_RTMP); + rtmp.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); + rtmp.setPreviewBitRate(2 * 1024 * 1024); + rtmp.setPreviewMaxFrameRate(30); + rtmp.setPreviewKeyFrameInterval(5); + + mSettings.finishInitialization(); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java index d814b7794c..978e38fcca 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java @@ -31,7 +31,7 @@ public ScreenCastSurfaceDrawingThread(ScreenCastRecorder recorder) { // 画面の更新が発生しない場合は、MediaCodec に更新イベントが発生しないので // ここでは、画面更新のタイムアウトを 0 にして、タイムアウトが発生しないように設定 - setTimeout(0); + setRenderingTimeout(0); } // EGLSurfaceDrawingThread diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java index 7855cc5816..d54eb5f9ff 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java @@ -4,24 +4,34 @@ import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Log; import android.util.Size; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.ScaleGestureDetector; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.constraintlayout.widget.ConstraintLayout; + import org.deviceconnect.android.deviceplugin.host.R; +import java.util.HashMap; +import java.util.Map; + public class PreviewSurfaceView extends FrameLayout { - private Rect mDrawingRect; private int mPreviewWidth; private int mPreviewHeight; private int mSurfaceWidth; private int mSurfaceHeight; private int mDragStartX; private int mDragStartY; + private boolean mDragFlag; + private boolean mScaleFlag; + private ScaleGestureDetector mScaleGestureDetector; + private final Map mCropRectMap = new HashMap<>(); public PreviewSurfaceView(Context context) { super(context); @@ -29,73 +39,161 @@ public PreviewSurfaceView(Context context) { public PreviewSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); - initView(context, attrs, 0); + initView(context); } public PreviewSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initView(context, attrs, defStyleAttr); + initView(context); } @SuppressLint("ClickableViewAccessibility") - private void initView(Context context, AttributeSet attrs, int defStyle) { + private void initView(Context context) { LayoutInflater.from(context).inflate(R.layout.host_preview_surface_view, this); + mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() { + @Override + public boolean onScale(ScaleGestureDetector detector) { + CropRectHolder holder = getFocusedHolder(); + if (holder == null) { + return false; + } + + Rect cropRect = holder.mCropRect; + Object tag = holder.mTag; + + float newWidth = cropRect.width() * detector.getScaleFactor(); + float newHeight = cropRect.height() * detector.getScaleFactor(); + + if (mPreviewWidth < newWidth) { + newWidth = mPreviewWidth; + } + + if (mPreviewHeight < newHeight) { + newHeight = mPreviewHeight; + } + + int diffW = (int) ((newWidth - cropRect.width()) / 2.0f); + int diffH = (int) ((newHeight - cropRect.height()) / 2.0f); + + int newLeft = cropRect.left - diffW; + int newRight = cropRect.right + diffW; + int newTop = cropRect.top - diffH; + int newBottom = cropRect.bottom + diffH; + + cropRect.set(newLeft, newTop, newRight, newBottom); + + int diffX = 0; + int diffY = 0; + if (newLeft < 0) { + diffX = -cropRect.left; + } + + if (newRight >= mPreviewWidth) { + diffX = mPreviewWidth - cropRect.right; + } + + if (newTop < 0) { + diffY = -cropRect.top; + } + + if (newBottom >= mPreviewHeight) { + diffY = mPreviewHeight - cropRect.bottom; + } + + cropRect.offset(diffX, diffY); + onChangedCropRect(tag, cropRect); + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + mScaleFlag = true; + mDragFlag = false; + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + mScaleFlag = false; + } + }); + SurfaceView surfaceView = getSurfaceView(); - surfaceView.setOnTouchListener((v, event) -> { - Rect drawingRect = getDrawingRect(); - if (drawingRect == null) { - return false; + surfaceView.setOnTouchListener((view, event) -> { + mScaleGestureDetector.onTouchEvent(event); + + if (mScaleFlag) { + return true; } + final int[] anchorPos = new int[2]; + view.getLocationOnScreen(anchorPos); + float scale = getScale(); - int x = (int) event.getRawX(); - int y = (int) event.getRawY(); + int x = (int) event.getRawX() - anchorPos[0]; + int y = (int) event.getRawY() - anchorPos[1]; int orgX = (int) (x / scale); int orgY = (int) (y / scale); switch(event.getAction()) { case MotionEvent.ACTION_DOWN: - if (drawingRect.contains(orgX, orgY)) { - mDragStartX = x; - mDragStartY = y; - } else { - return false; + { + clearFocusCropRect(); + + for (CropRectHolder holder : mCropRectMap.values()) { + Rect cropRect = holder.mCropRect; + if (cropRect.contains(orgX, orgY)) { + mDragStartX = x; + mDragStartY = y; + mDragFlag = true; + holder.mFocused = true; + break; + } } - break; + } break; case MotionEvent.ACTION_MOVE: - int diffX = (int) ((x - mDragStartX) / scale); - int diffY = (int) ((y - mDragStartY) / scale); + { + CropRectHolder holder = getFocusedHolder(); + if (mDragFlag && holder != null) { + Rect cropRect = holder.mCropRect; + Object tag = holder.mTag; - int newLeft = drawingRect.left + diffX; - int newRight = drawingRect.right + diffX; - int newTop = drawingRect.top + diffY; - int newBottom = drawingRect.bottom + diffY; + int diffX = (int) ((x - mDragStartX) / scale); + int diffY = (int) ((y - mDragStartY) / scale); - if (newLeft < 0) { - diffX = -drawingRect.left; - } + int newLeft = cropRect.left + diffX; + int newRight = cropRect.right + diffX; + int newTop = cropRect.top + diffY; + int newBottom = cropRect.bottom + diffY; - if (newRight >= mPreviewWidth) { - diffX = mPreviewWidth - drawingRect.right; - } + if (newLeft < 0) { + diffX = -cropRect.left; + } - if (newTop < 0) { - diffY = -drawingRect.top; - } + if (newRight >= mPreviewWidth) { + diffX = mPreviewWidth - cropRect.right; + } - if (newBottom >= mPreviewHeight) { - diffY = mPreviewHeight - drawingRect.bottom; - } + if (newTop < 0) { + diffY = -cropRect.top; + } - drawingRect.offset(diffX, diffY); - mDragStartX = x; - mDragStartY = y; - setDrawingRange(drawingRect); - break; + if (newBottom >= mPreviewHeight) { + diffY = mPreviewHeight - cropRect.bottom; + } + + cropRect.offset(diffX, diffY); + onChangedCropRect(tag, cropRect); + + mDragStartX = x; + mDragStartY = y; + } + } break; case MotionEvent.ACTION_UP: + mDragFlag = false; break; } return true; @@ -118,27 +216,65 @@ public SurfaceView getSurfaceView() { return null; } - public void setDrawingRange(Rect drawingRange) { - mDrawingRect = drawingRange; - setDrawingRangeView(drawingRange); + public void addCropRect(Object key, Rect cropRect) { + if (key == null || cropRect == null) { + return; + } + + post(() -> { + CropRectHolder holder = mCropRectMap.get(key); + if (holder == null) { + holder = new CropRectHolder(); + holder.mTag = key; + holder.mCropRect = cropRect; + holder.mView = new View(getContext()); + holder.mView.setBackgroundResource(R.drawable.border_red); + + ConstraintLayout constraintLayout = findViewById(R.id.preview_root); + constraintLayout.addView(holder.mView); + + // addView を行った後でないと LayoutParams が null になるので注意 + ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) holder.mView.getLayoutParams(); + layoutParams.leftToLeft = R.id.preview_surface_view; + layoutParams.topToTop = R.id.preview_surface_view; + holder.mView.setLayoutParams(layoutParams); + + mCropRectMap.put(key, holder); + } else { + holder.mCropRect = cropRect; + } + + setCropRectView(holder); + }); } - public Rect getDrawingRect() { - return mDrawingRect; + public void removeCropRange(Object key) { + post(() -> { + CropRectHolder holder = mCropRectMap.remove(key); + if (holder != null && holder.mView != null) { + ConstraintLayout constraintLayout = findViewById(R.id.preview_root); + constraintLayout.removeView(holder.mView); + } + }); } - private void setDrawingRangeView(Rect drawingRange) { - View root = findViewById(R.id.preview_root); - View frameView = root.findViewById(R.id.preview_drawing_range); - if (drawingRange == null || mPreviewHeight == 0) { - frameView.setVisibility(GONE); - } else { + protected void onChangedCropRect(Object key, Rect drawingRange) { + CropRectHolder holder = mCropRectMap.get(key); + if (holder != null) { + setCropRectView(holder); + } + } + + private void setCropRectView(CropRectHolder holder) { + Rect cropRect = holder.mCropRect; + View frameView = holder.mView; + if (frameView != null) { float scale = getScale(); - int left = (int) (drawingRange.left * scale); - int top = (int) (drawingRange.top * scale); + int left = (int) (cropRect.left * scale); + int top = (int) (cropRect.top * scale); ViewGroup.LayoutParams layoutParams = frameView.getLayoutParams(); - layoutParams.width = (int) (drawingRange.width() * scale); - layoutParams.height = (int) (drawingRange.height() * scale); + layoutParams.width = (int) (cropRect.width() * scale); + layoutParams.height = (int) (cropRect.height() * scale); MarginLayoutParams mlp = (MarginLayoutParams) layoutParams; mlp.setMargins(left, top, 0, 0); frameView.setLayoutParams(layoutParams); @@ -146,6 +282,21 @@ private void setDrawingRangeView(Rect drawingRange) { } } + private void clearFocusCropRect() { + for (CropRectHolder h : mCropRectMap.values()) { + h.mFocused = false; + } + } + + private CropRectHolder getFocusedHolder() { + for (CropRectHolder h : mCropRectMap.values()) { + if (h.mFocused) { + return h; + } + } + return null; + } + /** * アスペクトを保持したまま画面全体にプレビューを表示するように調整します. * @@ -184,8 +335,6 @@ public void fullSurfaceView(int previewWidth, int previewHeight) { layoutParams.height = mSurfaceHeight; surfaceView.setLayoutParams(layoutParams); surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); - - setDrawingRangeView(mDrawingRect); }); } @@ -213,8 +362,6 @@ public void adjustSurfaceView(int previewWidth, int previewHeight) { mPreviewHeight = previewHeight; mSurfaceWidth = changeSize.getWidth(); mSurfaceHeight = changeSize.getHeight(); - - setDrawingRangeView(mDrawingRect); }); } @@ -251,4 +398,11 @@ private Size calculateViewSize(int width, int height, Size viewSize) { } return new Size(viewSize.getWidth(), h); } + + private static class CropRectHolder { + private Object mTag; + private Rect mCropRect; + private View mView; + private boolean mFocused; + } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java new file mode 100644 index 0000000000..b06c16e0da --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java @@ -0,0 +1,211 @@ +package org.deviceconnect.android.deviceplugin.host.recorder.util; + +import android.graphics.Rect; + +import org.deviceconnect.android.libmedia.streaming.util.QueueThread; +import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; + +public class MovingRectThread { + private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); + private WorkThread mWorkThread; + private int mFrameRate = 30; + + public void setFrameRate(int frameRate) { + if (frameRate <= 0) { + throw new IllegalArgumentException("frameRate is negative or zero."); + } + mFrameRate = frameRate; + } + + /** + * 範囲指定の移動処理を開始します. + */ + public synchronized void start() { + if (mWorkThread != null) { + return; + } + mWorkThread = new WorkThread(); + mWorkThread.setName("MovingRectThread"); + mWorkThread.start(); + } + + /** + * 範囲指定の移動処理を停止します. + */ + public synchronized void stop() { + if (mWorkThread != null) { + mWorkThread.terminate(); + mWorkThread = null; + } + } + + /** + * 矩形を設定します. + * + * @param rect 矩形 + */ + public void set(Rect rect) { + move(null, rect, 0); + } + + /** + * 描画範囲の移動を設定します. + * + * @param start 開始する矩形 + * @param end 停止する矩形 + * @param duration 移動時間(ミリ秒) + * @param cancel 移動中の処理をキャンセルするか設定 + */ + public synchronized void move(Rect start, Rect end, int duration, boolean cancel) { + if (mWorkThread == null) { + return; + } + + if (duration < 0 || end == null) { + return; + } + + if (cancel) { + mWorkThread.cancel(); + } + + MovingObject object = new MovingObject(); + object.mStartRect = start; + object.mEndRect = end; + object.mDuration = duration; + object.mInterval = 1000 / mFrameRate; + mWorkThread.add(object); + } + + /** + * 描画範囲の移動を設定します. + * + * 追加された時にアニメーション中の場合には、移動をキャンセルして、次の移動を開始します。 + * + * @param start 開始する矩形 + * @param end 停止する矩形 + * @param duration 移動時間(ミリ秒) + */ + public void move(Rect start, Rect end, int duration) { + move(start, end, duration, true); + } + + /** + * 範囲の移動イベントを通知するリスナーを設定します. + * + * @param listener 追加するリスナー + */ + public void addOnEventListener(OnEventListener listener) { + if (listener != null) { + mOnEventListeners.add(listener); + } + } + + /** + * 範囲の移動イベントを通知するリスナーを削除します. + * + * @param listener 削除するリスナー + */ + public void removeOnEventListener(OnEventListener listener) { + if (listener != null) { + mOnEventListeners.remove(listener); + } + } + + public interface OnEventListener { + void onMoved(Rect rect); + } + + private void postOnMoved(Rect rect) { + for (OnEventListener cb : mOnEventListeners) { + cb.onMoved(rect); + } + } + + private class MovingObject implements Runnable { + private boolean mStopFlag = false; + private final Rect mRect = new Rect(); + private Rect mStartRect; + private Rect mEndRect; + private int mDuration; + private int mInterval; + + public void terminate() { + mStopFlag = true; + } + + @Override + public void run() { + int count = mDuration / mInterval; + if (count > 0 && mStartRect != null) { + float c = mDuration / (float) mInterval; + float deltaLeft = (mEndRect.left - mStartRect.left) / c; + float deltaTop = (mEndRect.top - mStartRect.top) / c; + float deltaRight = (mEndRect.right - mStartRect.right) / c; + float deltaBottom = (mEndRect.bottom - mStartRect.bottom) / c; + + float left = mStartRect.left; + float top = mStartRect.top; + float right = mStartRect.right; + float bottom = mStartRect.bottom; + + while (!mStopFlag && count > 0) { + left += deltaLeft; + top += deltaTop; + right += deltaRight; + bottom += deltaBottom; + mRect.set((int) left, (int) top, (int) right, (int) bottom); + count--; + + postOnMoved(mRect); + + try { + Thread.sleep(mInterval); + } catch (Exception e) { + return; + } + } + } + + mRect.set(mEndRect); + postOnMoved(mRect); + } + } + + private static class WorkThread extends QueueThread { + private boolean mStopFlag = false; + private MovingObject mMovingObject; + + private void cancel() { + if (mMovingObject != null) { + mMovingObject.terminate(); + } + } + + /** + * スレッドを終了します. + */ + private void terminate() { + mStopFlag = true; + cancel(); + interrupt(); + try { + join(500); + } catch (InterruptedException e) { + // ignore. + } + } + + @Override + public void run() { + try { + while (!mStopFlag) { + mMovingObject = get(); + mMovingObject.run(); + } + } catch (InterruptedException e) { + // ignore. + } + } + } +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PropertyUtil.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PropertyUtil.java index f65a4bd979..d241287ad2 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PropertyUtil.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/PropertyUtil.java @@ -5,6 +5,11 @@ import android.graphics.Rect; import android.util.Size; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; import java.util.Set; /** @@ -59,6 +64,38 @@ public void put(String leftKey, String topKey, String rightKey, String bottomKey .apply(); } + public void put(String key, List values) { + StringBuilder value = new StringBuilder(); + for (String v : values) { + if (value.length() > 0) { + value.append(","); + } + try { + value.append(URLEncoder.encode(v, "UTF-8")); + } catch (UnsupportedEncodingException e) { + // ignore. + } + } + mPref.edit().putString(key, value.toString()).apply(); + } + + public List getArrayString(String key) { + String string = mPref.getString(key, null); + if (string == null) { + return new ArrayList<>(); + } + List array = new ArrayList<>(); + String[] split = string.split(","); + for (String v : split) { + try { + array.add(URLDecoder.decode(v, "UTF-8")); + } catch (UnsupportedEncodingException e) { + // ignore. + } + } + return array; + } + public Integer getInteger(String key, Integer defaultValue) { String value = mPref.getString(key, String.valueOf(defaultValue)); try { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml index 89b048aa11..d0a9ea3c08 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/host_preview_surface_view.xml @@ -37,6 +37,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/border_red" + android:visibility="gone" app:layout_constraintLeft_toLeftOf="@id/preview_surface_view" app:layout_constraintTop_toTopOf="@id/preview_surface_view" /> diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml index ec7e4bfc80..45fb851892 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml @@ -6,12 +6,12 @@ android:title="@string/host_recorder_settings_title" app:iconSpaceReserved="false"> - - @@ -19,20 +19,21 @@ - - - @@ -40,10 +41,11 @@ - diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java index c739640f72..937163df88 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java @@ -2,7 +2,6 @@ import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.util.Log; import android.view.Surface; import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; @@ -39,7 +38,7 @@ public class EGLSurfaceDrawingThread { /** * レンダリングのタイムアウト(ミリ秒). */ - private int mTimeout = 10 * 1000; + private int mRenderingTimeout = 10 * 1000; /** * イベントを通知するリスナー. @@ -83,11 +82,11 @@ public void removeOnDrawingEventListener(OnDrawingEventListener listener) { * * @param timeout タイムアウト */ - public void setTimeout(int timeout) { + public void setRenderingTimeout(int timeout) { if (timeout < 0) { throw new IllegalArgumentException("timeout cannot set negative value."); } - mTimeout = timeout; + mRenderingTimeout = timeout; } /** @@ -95,8 +94,8 @@ public void setTimeout(int timeout) { * * @return タイムアウト */ - public int getTimeout() { - return mTimeout; + public int getRenderingTimeout() { + return mRenderingTimeout; } /** @@ -530,7 +529,7 @@ public void run() { mEGLCore.makeCurrent(); mStManager = createStManager(); - mStManager.setTimeout(mTimeout); + mStManager.setTimeout(mRenderingTimeout); synchronized (mEGLSurfaceBases) { for (EGLSurfaceBase surfaceBase : mEGLSurfaceBases) { @@ -599,6 +598,9 @@ public void run() { } } + /** + * 描画イベントを通知するリスナー. + */ public interface OnDrawingEventListener { /** * 描画の開始を通知します. diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java index 3570714694..ef44c6b3c8 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java @@ -39,6 +39,7 @@ public class SurfaceTextureRenderer { }; private final FloatBuffer mTriangleVertices; + private boolean mInverse; private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + @@ -77,6 +78,7 @@ public class SurfaceTextureRenderer { * @param inverse テクスチャの反転フラグ */ SurfaceTextureRenderer(boolean inverse) { + mInverse = inverse; mTriangleVertices = ByteBuffer.allocateDirect( TRIANGLE_VERTICES_DATA.length * FLOAT_SIZE_BYTES) .order(ByteOrder.nativeOrder()).asFloatBuffer(); @@ -220,11 +222,25 @@ public void drawFrame(SurfaceTexture st, int displayRotation) { GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); } + /** + * 描画範囲を削除します. + */ public void clearDrawingRange() { mTriangleVertices.clear(); - mTriangleVertices.put(TRIANGLE_VERTICES_DATA).position(0); + if (mInverse) { + mTriangleVertices.put(TRIANGLE_VERTICES_DATA_2).position(0); + } else { + mTriangleVertices.put(TRIANGLE_VERTICES_DATA).position(0); + } } + /** + * 描画範囲を設定します. + * + * @param rect 範囲 + * @param width 描画元の横幅 + * @param height 描画元の縦幅 + */ public void setDrawingRange(Rect rect, int width, int height) { setDrawingRange(rect.left, rect.top, rect.right, rect.bottom, width, height); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraSurfaceVideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraSurfaceVideoEncoder.java index b96360b13b..78c4efd4e2 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraSurfaceVideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraSurfaceVideoEncoder.java @@ -62,14 +62,4 @@ public CameraSurfaceVideoEncoder(CameraVideoQuality videoQuality, EGLSurfaceDraw public VideoQuality getVideoQuality() { return mVideoQuality; } - - // SurfaceVideoEncoder - - @Override - protected void onStartSurfaceDrawing() { - } - - @Override - protected void onStopSurfaceDrawing() { - } } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CanvasVideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CanvasVideoEncoder.java index e65e00fb14..d30a14c16d 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CanvasVideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CanvasVideoEncoder.java @@ -131,7 +131,7 @@ private void terminate() { interrupt(); try { - join(200); + join(500); } catch (InterruptedException e) { // ignore. } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java index 171081491e..b9d80db909 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java @@ -1,5 +1,6 @@ package org.deviceconnect.android.libmedia.streaming.video; +import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.media.MediaCodecInfo; import android.view.Surface; @@ -29,6 +30,11 @@ public abstract class SurfaceVideoEncoder extends VideoEncoder { */ private final boolean mInternalCreateSurfaceDrawingThread; + /** + * 描画先の EGLSurfaceBase. + */ + private EGLSurfaceBase mEGLSurfaceBase; + /** * EGLSurfaceDrawingThread からのイベントを受け取るためのリスナー. */ @@ -50,7 +56,13 @@ public void onError(Exception e) { @Override public void onDrawn(EGLSurfaceBase eglSurfaceBase) { - // ignore. + if (mEGLSurfaceBase == eglSurfaceBase) { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + eglSurfaceBase.setDrawingRange(videoQuality.getCropRect()); + } + onDrawnSurface(); + } } }; @@ -63,6 +75,17 @@ public SurfaceVideoEncoder(EGLSurfaceDrawingThread thread) { mInternalCreateSurfaceDrawingThread = false; } + /** + * 描画先の EGLSurfaceBase を取得します. + * + * エンコードが開始されていない場合には null を返却します。 + * + * @return 描画先の EGLSurfaceBase + */ + public EGLSurfaceBase getEGLSurfaceBase() { + return mEGLSurfaceBase; + } + // MediaEncoder @Override @@ -107,9 +130,11 @@ private void startDrawingThreadInternal() { } mSurfaceDrawingThread.setSize(quality.getVideoWidth(), quality.getVideoHeight()); - mSurfaceDrawingThread.addEGLSurfaceBase(mMediaCodecSurface, quality.getDrawingRange()); + mSurfaceDrawingThread.addEGLSurfaceBase(mMediaCodecSurface, quality.getCropRect()); mSurfaceDrawingThread.addOnDrawingEventListener(mOnDrawingEventListener); mSurfaceDrawingThread.start(); + + mEGLSurfaceBase = mSurfaceDrawingThread.findEGLSurfaceBaseByTag(mMediaCodecSurface); } /** @@ -123,6 +148,7 @@ private void stopDrawingThreadInternal() { if (mInternalCreateSurfaceDrawingThread) { mSurfaceDrawingThread = null; } + mEGLSurfaceBase = null; } } @@ -161,4 +187,10 @@ protected void onStartSurfaceDrawing() { */ protected void onStopSurfaceDrawing() { } + + /** + * Surface への描画を行ったことを通知します. + */ + protected void onDrawnSurface() { + } } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java index 1066f92b68..ced8b61454 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java @@ -31,7 +31,7 @@ public class VideoQuality { * 描画範囲. * null の場合は範囲してい無し。 */ - private Rect mDrawingRange; + private Rect mCropRect; /** * コンストラクタ. @@ -58,8 +58,8 @@ public void set(VideoQuality quality) { mUseSoftwareEncoder = quality.isUseSoftwareEncoder(); mProfile = quality.getProfile(); mLevel = quality.getLevel(); - if (quality.mDrawingRange != null) { - mDrawingRange = new Rect(quality.mDrawingRange); + if (quality.mCropRect != null) { + mCropRect = new Rect(quality.mCropRect); } } @@ -301,8 +301,8 @@ public enum BitRateMode { * * @return 描画範囲 */ - public Rect getDrawingRange() { - return mDrawingRange; + public Rect getCropRect() { + return mCropRect; } /** @@ -310,10 +310,10 @@ public Rect getDrawingRange() { * * null が指定された場合には、描画範囲を削除します。 * - * @param drawingRange 描画範囲 + * @param cropRect 描画範囲 */ - public void setDrawingRange(Rect drawingRange) { - mDrawingRange = drawingRange; + public void setCropRect(Rect cropRect) { + mCropRect = cropRect; } @Override From df5b33534ed81ed99011750a55c1167c138fc996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 1 Sep 2021 14:41:03 +0900 Subject: [PATCH 16/40] =?UTF-8?q?=E3=83=97=E3=83=AC=E3=83=93=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E9=85=8D=E4=BF=A1=E3=82=B5=E3=83=BC=E3=83=90=E3=81=AA?= =?UTF-8?q?=E3=81=A9=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20=E9=85=8D=E4=BF=A1=E5=85=88=E3=81=A8=E5=85=83?= =?UTF-8?q?=E3=81=AE=E6=98=A0=E5=83=8F=E3=81=AE=E8=A7=A3=E5=83=8F=E5=BA=A6?= =?UTF-8?q?=E3=81=A7=E3=82=A2=E3=82=B9=E3=83=9A=E3=82=AF=E3=83=88=E6=AF=94?= =?UTF-8?q?=E3=82=92=E5=90=88=E3=82=8F=E3=81=9B=E3=81=A6=E9=85=8D=E4=BF=A1?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/SettingsBaseFragment.java | 5 + .../settings/SettingsBroadcastFragment.java | 34 +- .../settings/SettingsMJPEGFragment.java | 2 +- .../settings/SettingsMainFragment.java | 33 +- .../settings/SettingsRTSPFragment.java | 2 +- .../settings/SettingsSRTFragment.java | 2 +- .../host/profile/HostCameraProfile.java | 2 +- .../profile/HostLiveStreamingProfile.java | 8 +- .../HostMediaStreamingRecordingProfile.java | 233 ++-- .../host/recorder/AbstractBroadcaster.java | 208 +++- .../recorder/AbstractMJPEGPreviewServer.java | 10 +- .../host/recorder/AbstractPreviewServer.java | 37 +- .../recorder/AbstractRTMPBroadcaster.java | 33 +- .../recorder/AbstractRTSPPreviewServer.java | 1 - .../host/recorder/AbstractSRTBroadcaster.java | 33 +- .../recorder/AbstractSRTPreviewServer.java | 2 +- .../host/recorder/HostMediaRecorder.java | 1070 ++++++----------- .../audio/AudioBroadcasterProvider.java | 13 +- .../recorder/audio/AudioRTMPBroadcaster.java | 5 +- .../audio/AudioRTSPPreviewServer.java | 2 +- .../recorder/audio/AudioSRTBroadcaster.java | 4 +- .../recorder/audio/AudioSRTPreviewServer.java | 3 +- .../recorder/audio/HostAudioRecorder.java | 4 +- .../camera/Camera2BroadcasterProvider.java | 13 +- .../camera/Camera2MJPEGPreviewServer.java | 2 +- .../camera/Camera2RTMPBroadcaster.java | 4 +- .../camera/Camera2RTSPPreviewServer.java | 6 +- .../host/recorder/camera/Camera2Recorder.java | 58 +- .../camera/Camera2SRTBroadcaster.java | 7 +- .../camera/Camera2SRTPreviewServer.java | 6 +- .../recorder/camera/CameraVideoEncoder.java | 29 - .../screen/ScreenCastBroadcasterProvider.java | 13 +- .../screen/ScreenCastMJPEGPreviewServer.java | 2 +- .../screen/ScreenCastRTMPBroadcaster.java | 4 +- .../screen/ScreenCastRTSPPreviewServer.java | 5 +- .../recorder/screen/ScreenCastRecorder.java | 52 +- .../screen/ScreenCastSRTBroadcaster.java | 7 +- .../screen/ScreenCastSRTPreviewServer.java | 5 +- .../recorder/screen/SurfaceScreenCast.java | 2 +- .../host/recorder/util/MovingRectThread.java | 14 +- .../res/xml/settings_host_recorder_main.xml | 20 - .../streaming/gles/EGLSurfaceBase.java | 10 +- .../gles/EGLSurfaceDrawingThread.java | 66 +- .../streaming/gles/SurfaceTextureManager.java | 12 +- .../gles/SurfaceTextureRenderer.java | 14 +- .../streaming/video/SurfaceVideoEncoder.java | 3 +- 46 files changed, 909 insertions(+), 1191 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java index fc500b9926..0eaf2f5581 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java @@ -50,6 +50,11 @@ public String getRecorderId() { return null; } + /** + * プレビューサーバ、配信設定ファイル名を取得します. + * + * @return 設定のファイル名 + */ public String getSettingName() { Bundle args = getArguments(); if (args != null) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java index cb9b2ec077..baf1c83f35 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java @@ -16,7 +16,6 @@ import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; -import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; import java.util.ArrayList; import java.util.Collections; @@ -38,8 +37,6 @@ public void onBindService() { HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); - setPreviewServerPort(); - setPreviewServerUrl(settings.getPort()); setPreviewSizePreference(settings); setPreviewVideoEncoderPreference(settings); setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); @@ -57,28 +54,11 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } - private HostMediaRecorder.StreamingSettings getStreamingSetting() { HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); return s.getBroadcaster(getSettingName()); } - private void setPreviewServerPort() { - setInputTypeNumber("port"); - EditTextPreference pref = findPreference("port"); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - } - } - - private void setPreviewServerUrl(int port) { - EditTextPreference pref = findPreference("url"); - if (pref != null) { - String ipAddress = NetworkUtil.getIPAddress(requireContext()); - pref.setText("rtsp://" + ipAddress + ":" + port); - } - } - /** * 切り抜き範囲の設定にリスナーを設定します. * @@ -110,7 +90,7 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - mMediaRecorder.getSettings().setCropRect(null); + getStreamingSetting().setCropRect(null); return false; }); } @@ -339,7 +319,7 @@ private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEn * @param key キー * @return 切り抜き範囲 */ - private Integer getDrawingRange(String key) { + private Integer getCropRect(String key) { EditTextPreference pref = findPreference(key); if (pref != null) { try { @@ -367,8 +347,6 @@ private Integer getDrawingRange(String key) { if (size != null) { settings.setPreviewSize(size); } - } else if ("port".equalsIgnoreCase(key)) { - setPreviewServerUrl(Integer.parseInt((String) newValue)); } else if ("preview_encoder".equals(key)) { // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく try { @@ -388,7 +366,7 @@ private Integer getDrawingRange(String key) { } else if ("preview_clip_left".equalsIgnoreCase(key)) { try { int clipLeft = Integer.parseInt((String) newValue); - Integer clipRight = getDrawingRange("preview_clip_right"); + Integer clipRight = getCropRect("preview_clip_right"); if (clipRight != null && clipRight <= clipLeft) { return false; } @@ -398,7 +376,7 @@ private Integer getDrawingRange(String key) { } else if ("preview_clip_top".equalsIgnoreCase(key)) { try { int clipTop = Integer.parseInt((String) newValue); - Integer clipBottom = getDrawingRange("preview_clip_bottom"); + Integer clipBottom = getCropRect("preview_clip_bottom"); if (clipBottom != null && clipBottom <= clipTop) { return false; } @@ -408,7 +386,7 @@ private Integer getDrawingRange(String key) { } else if ("preview_clip_right".equalsIgnoreCase(key)) { try { int clipRight = Integer.parseInt((String) newValue); - Integer clipLeft = getDrawingRange("preview_clip_left"); + Integer clipLeft = getCropRect("preview_clip_left"); if (clipLeft != null && clipRight <= clipLeft) { return false; } @@ -418,7 +396,7 @@ private Integer getDrawingRange(String key) { } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { try { int clipBottom = Integer.parseInt((String) newValue); - Integer clipTop = getDrawingRange("preview_clip_top"); + Integer clipTop = getCropRect("preview_clip_top"); if (clipTop != null && clipBottom <= clipTop) { return false; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java index 91e767d89c..a245ad0585 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -118,7 +118,7 @@ private void setPreviewCutOutReset() { if (bottom != null) { bottom.setText(null); } - mMediaRecorder.getSettings().setCropRect(null); + getStreamingSetting().setCropRect(null); return false; }); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java index 3c22d85af7..3c11ef61b3 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java @@ -1,6 +1,7 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; import android.os.Bundle; +import android.util.Log; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; @@ -30,14 +31,6 @@ public boolean onPreferenceTreeClick(final Preference preference) { findNavController(this).navigate(R.id.action_main_to_video); } else if ("recorder_settings_audio".equals(preference.getKey())) { findNavController(this).navigate(R.id.action_main_to_audio); - } else if ("recorder_settings_mjpeg".equals(preference.getKey())) { - findNavController(this).navigate(R.id.action_main_to_mjpeg); - } else if ("recorder_settings_rtsp".equals(preference.getKey())) { - findNavController(this).navigate(R.id.action_main_to_rtsp); - } else if ("recorder_settings_srt".equals(preference.getKey())) { - findNavController(this).navigate(R.id.action_main_to_srt); - } else if ("recorder_settings_broadcast".equals(preference.getKey())) { - findNavController(this).navigate(R.id.action_main_to_broadcast); } else { Bundle params = new Bundle(); params.putString("setting_name", preference.getKey()); @@ -70,8 +63,8 @@ public void onBindService() { addBroadcasterList(); } - private boolean isNotExistPreference(String name) { - return findPreference(name) == null; + private boolean isNotExistPreference(String key) { + return findPreference(key) == null; } private void addPreviewServerList() { @@ -80,12 +73,12 @@ private void addPreviewServerList() { List previewServerList = settings.getPreviewServerList(); PreferenceCategory preferenceCategory = findPreference("recorder_settings_preview_server"); - for (String name : previewServerList) { - HostMediaRecorder.StreamingSettings s = settings.getPreviewServer(name); - if (isNotExistPreference(name)) { + for (String previewServerId : previewServerList) { + HostMediaRecorder.StreamingSettings s = settings.getPreviewServer(previewServerId); + if (isNotExistPreference(previewServerId)) { Preference preference = new Preference(requireContext()); - preference.setTitle(s.getMimeType()); - preference.setKey(name); + preference.setTitle(s.getName()); + preference.setKey(previewServerId); preference.setIconSpaceReserved(false); preferenceCategory.addPreference(preference); } @@ -98,12 +91,12 @@ private void addBroadcasterList() { List broadcasterList = settings.getBroadcasterList(); PreferenceCategory preferenceCategory = findPreference("recorder_settings_broadcaster"); - for (String name : broadcasterList) { - HostMediaRecorder.StreamingSettings s = settings.getBroadcaster(name); - if (isNotExistPreference(name)) { + for (String broadcasterId : broadcasterList) { + HostMediaRecorder.StreamingSettings s = settings.getBroadcaster(broadcasterId); + if (isNotExistPreference(broadcasterId)) { Preference preference = new Preference(requireContext()); - preference.setTitle(s.getMimeType()); - preference.setKey(name); + preference.setTitle(s.getName()); + preference.setKey(broadcasterId); preference.setIconSpaceReserved(false); preferenceCategory.addPreference(preference); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java index b1f684df48..e6f4b7a277 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -109,7 +109,7 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - mMediaRecorder.getSettings().setCropRect(null); + getStreamingSetting().setCropRect(null); return false; }); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index d1937054dd..c109fa117d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -136,7 +136,7 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - mMediaRecorder.getSettings().setCropRect(null); + getStreamingSetting().setCropRect(null); return false; }); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostCameraProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostCameraProfile.java index ecfa77b208..f4d59bb66e 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostCameraProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostCameraProfile.java @@ -322,7 +322,7 @@ public boolean onRequest(final Intent request, final Intent response) { } if (sensorSensitivity != null) { - if (!settings.isSupportedSensorSensorSensitivity(sensorSensitivity)) { + if (!settings.isSupportedSensorSensitivity(sensorSensitivity)) { MessageUtils.setInvalidRequestParameterError(response, "sensorSensitivity is invalid."); return true; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java index 41ae5a36d6..ce0606f05e 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java @@ -162,9 +162,9 @@ public boolean onRequest(final Intent request, final Intent response) { settings.setPreviewSize(new Size(width, height)); } - if (bitrate != null) { - settings.setPreviewBitRate(bitrate); - } +// if (bitrate != null) { +// settings.setPreviewBitRate(bitrate); +// } if (frameRate != null) { settings.setPreviewMaxFrameRate(frameRate); @@ -409,7 +409,7 @@ private Bundle createStreamingBundle(Broadcaster broadcaster, String status) { video.putString(PARAM_KEY_URI, mHostMediaRecorder.getId()); video.putInt(PARAM_KEY_WIDTH, settings.getPreviewSize().getWidth()); video.putInt(PARAM_KEY_HEIGHT, settings.getPreviewSize().getHeight()); - video.putInt(PARAM_KEY_BITRATE, settings.getPreviewBitRate()); +// video.putInt(PARAM_KEY_BITRATE, settings.getPreviewBitRate()); video.putInt(PARAM_KEY_FRAME_RATE, settings.getPreviewMaxFrameRate()); video.putString(PARAM_KEY_MIME_TYPE, broadcaster.getMimeType()); streaming.putParcelable(PARAM_KEY_VIDEO, video); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index cb0588095d..8eb2e076ff 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -98,53 +98,53 @@ public void onAllowed() { if (recorder.getMimeType().startsWith("image/") || recorder.getMimeType().startsWith("video/")) { // 静止画の設定 - Size pictureSize = settings.getPictureSize(); - if (pictureSize != null) { - setRecorderImageWidth(info, pictureSize.getWidth()); - setRecorderImageHeight(info, pictureSize.getHeight()); - } - - // プレビュー設定 - Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - setRecorderPreviewWidth(info, previewSize.getWidth()); - setRecorderPreviewHeight(info, previewSize.getHeight()); - setRecorderPreviewMaxFrameRate(info, settings.getPreviewMaxFrameRate()); - info.putInt("previewBitRate", settings.getPreviewBitRate() / 1024); - info.putInt("previewKeyFrameInterval", settings.getPreviewKeyFrameInterval()); - info.putString("previewEncoder", settings.getPreviewEncoder()); - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); - if (pl != null) { - switch (HostMediaRecorder.VideoEncoderName.nameOf(settings.getPreviewEncoder())) { - case H264: - info.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName()); - info.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName()); - break; - case H265: - info.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName()); - info.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName()); - break; - } - } - info.putFloat("previewJpegQuality", settings.getPreviewQuality() / 100.0f); - - Bundle status = new Bundle(); - status.putBoolean("preview", recorder.isPreviewRunning()); - status.putBoolean("broadcast", recorder.isBroadcasterRunning()); - status.putBoolean("recording", recorder.getState() == HostMediaRecorder.State.RECORDING); - info.putParcelable("status", status); - } - - // 切り抜き設定 - Rect rect = settings.getCropRect(); - if (rect != null) { - Bundle drawingRect = new Bundle(); - drawingRect.putInt("left", rect.left); - drawingRect.putInt("top", rect.top); - drawingRect.putInt("right", rect.right); - drawingRect.putInt("bottom", rect.bottom); - info.putBundle("previewClip", drawingRect); - } +// Size pictureSize = settings.getPictureSize(); +// if (pictureSize != null) { +// setRecorderImageWidth(info, pictureSize.getWidth()); +// setRecorderImageHeight(info, pictureSize.getHeight()); +// } +// +// // プレビュー設定 +// Size previewSize = settings.getPreviewSize(); +// if (previewSize != null) { +// setRecorderPreviewWidth(info, previewSize.getWidth()); +// setRecorderPreviewHeight(info, previewSize.getHeight()); +// setRecorderPreviewMaxFrameRate(info, settings.getPreviewMaxFrameRate()); +// info.putInt("previewBitRate", settings.getPreviewBitRate() / 1024); +// info.putInt("previewKeyFrameInterval", settings.getPreviewKeyFrameInterval()); +// info.putString("previewEncoder", settings.getPreviewEncoder()); +// HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); +// if (pl != null) { +// switch (HostMediaRecorder.VideoEncoderName.nameOf(settings.getPreviewEncoder())) { +// case H264: +// info.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName()); +// info.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName()); +// break; +// case H265: +// info.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName()); +// info.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName()); +// break; +// } +// } +// info.putFloat("previewJpegQuality", settings.getPreviewQuality() / 100.0f); +// +// Bundle status = new Bundle(); +// status.putBoolean("preview", recorder.isPreviewRunning()); +// status.putBoolean("broadcast", recorder.isBroadcasterRunning()); +// status.putBoolean("recording", recorder.getState() == HostMediaRecorder.State.RECORDING); +// info.putParcelable("status", status); +// } +// +// // 切り抜き設定 +// Rect rect = settings.getCropRect(); +// if (rect != null) { +// Bundle drawingRect = new Bundle(); +// drawingRect.putInt("left", rect.left); +// drawingRect.putInt("top", rect.top); +// drawingRect.putInt("right", rect.right); +// drawingRect.putInt("bottom", rect.bottom); +// info.putBundle("previewClip", drawingRect); +// } } else if (recorder.getMimeType().startsWith("audio/")) { // 音声の設定 } @@ -167,6 +167,7 @@ public void onDisallowed() { } }; + // PUT /gotapi/mediaStreamRecording/crop private final DConnectApi mPutCropApi = new PutApi() { @Override public String getAttribute() { @@ -192,19 +193,21 @@ public boolean onRequest(Intent request, Intent response) { recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { - int duration = previewClipDuration != null ? previewClipDuration : 0; - Rect start = recorder.getSettings().getCropRect("video/x-rtp"); - if (start == null) { - int width = recorder.getSettings().getPreviewSize().getWidth(); - int height = recorder.getSettings().getPreviewSize().getHeight(); - start = new Rect(0, 0, width, height); - } - Rect end = new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom); - - PreviewServer ps = recorder.getServerProvider().getServerByMimeType("video/x-rtp"); - if (ps instanceof CropInterface) { - ((CropInterface) ps).moveCropRect(start, end, duration); - } +// HostMediaRecorder.StreamingSettings settings = recorder.getSettings().getPreviewServer(mimeType); +// +// int duration = previewClipDuration != null ? previewClipDuration : 0; +// Rect start = settings.getCropRect(); +// if (start == null) { +// int width = recorder.getSettings().getPreviewSize().getWidth(); +// int height = recorder.getSettings().getPreviewSize().getHeight(); +// start = new Rect(0, 0, width, height); +// } +// Rect end = new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom); +// +// PreviewServer ps = recorder.getServerProvider().getServerByMimeType("video/x-rtp"); +// if (ps instanceof CropInterface) { +// ((CropInterface) ps).moveCropRect(start, end, duration); +// } setResult(response, DConnectMessage.RESULT_OK); sendResponse(response); @@ -221,6 +224,7 @@ public void onDisallowed() { } }; + // DELETE /gotapi/mediaStreamRecording/crop private final DConnectApi mDeleteCropApi = new DeleteApi() { @Override public String getAttribute() { @@ -241,11 +245,10 @@ public boolean onRequest(Intent request, Intent response) { recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { - PreviewServer ps = recorder.getServerProvider().getServerByMimeType("video/x-rtp"); + PreviewServer ps = recorder.getServerProvider().getServerByMimeType(mimeType); if (ps instanceof CropInterface) { ((CropInterface) ps).setCropRect(null); } - setResult(response, DConnectMessage.RESULT_OK); sendResponse(response); } @@ -412,28 +415,28 @@ private void setOptions(final Intent request, final Intent response) { return; } - switch (settings.getPreviewEncoderName()) { - case H264: { - H264Profile p = H264Profile.nameOf(previewProfile); - H264Level l = H264Level.nameOf(previewLevel); - if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) { - MessageUtils.setInvalidRequestParameterError(response, - "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); - return; - } - profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } break; - case H265: { - H265Profile p = H265Profile.nameOf(previewProfile); - H265Level l = H265Level.nameOf(previewLevel); - if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) { - MessageUtils.setInvalidRequestParameterError(response, - "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); - return; - } - profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } break; - } +// switch (settings.getPreviewEncoderName()) { +// case H264: { +// H264Profile p = H264Profile.nameOf(previewProfile); +// H264Level l = H264Level.nameOf(previewLevel); +// if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) { +// MessageUtils.setInvalidRequestParameterError(response, +// "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); +// return; +// } +// profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); +// } break; +// case H265: { +// H265Profile p = H265Profile.nameOf(previewProfile); +// H265Level l = H265Level.nameOf(previewLevel); +// if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) { +// MessageUtils.setInvalidRequestParameterError(response, +// "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); +// return; +// } +// profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); +// } break; +// } } if (previewJpegQuality != null) { @@ -505,37 +508,37 @@ private void setOptions(final Intent request, final Intent response) { settings.setPreviewMaxFrameRate(previewMaxFrameRate.intValue()); } - if (previewBitRate != null) { - settings.setPreviewBitRate(previewBitRate * 1024); - } - - if (previewKeyFrameInterval != null) { - settings.setPreviewKeyFrameInterval(previewKeyFrameInterval); - } - - if (previewEncoder != null) { - settings.setPreviewEncoder(previewEncoder); - // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする - settings.setProfileLevel(null); - } - - if (profileLevel != null) { - settings.setProfileLevel(profileLevel); - } - - if (previewIntraRefresh != null) { - settings.setIntraRefresh(previewIntraRefresh); - } - - if (previewJpegQuality != null) { - settings.setPreviewQuality((int) (previewJpegQuality * 100)); - } - - if (previewClipReset != null && previewClipReset) { - settings.setCropRect(null); - } else if (previewClipLeft != null) { - settings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); - } +// if (previewBitRate != null) { +// settings.setPreviewBitRate(previewBitRate * 1024); +// } +// +// if (previewKeyFrameInterval != null) { +// settings.setPreviewKeyFrameInterval(previewKeyFrameInterval); +// } +// +// if (previewEncoder != null) { +// settings.setPreviewEncoder(previewEncoder); +// // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする +// settings.setProfileLevel(null); +// } +// +// if (profileLevel != null) { +// settings.setProfileLevel(profileLevel); +// } +// +// if (previewIntraRefresh != null) { +// settings.setIntraRefresh(previewIntraRefresh); +// } +// +// if (previewJpegQuality != null) { +// settings.setPreviewQuality((int) (previewJpegQuality * 100)); +// } +// +// if (previewClipReset != null && previewClipReset) { +// settings.setCropRect(null); +// } else if (previewClipLeft != null) { +// settings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); +// } try { recorder.onConfigChange(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java index 1db1dba759..57620dd589 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java @@ -1,5 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.recorder; +import android.graphics.Rect; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioPlaybackCaptureConfiguration; @@ -8,14 +9,19 @@ import android.util.Size; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; +import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; +import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; +import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder; import org.deviceconnect.android.libmedia.streaming.audio.MicAudioQuality; import org.deviceconnect.android.libmedia.streaming.audio.filter.HighPassFilter; import org.deviceconnect.android.libmedia.streaming.audio.filter.LowPassFilter; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; +import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; +import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; -public abstract class AbstractBroadcaster implements Broadcaster { +public abstract class AbstractBroadcaster implements Broadcaster, CropInterface { /** * 配信先の URI. */ @@ -31,10 +37,28 @@ public abstract class AbstractBroadcaster implements Broadcaster { */ private final String mName; + /** + * 切り抜き範囲移動用スレッド. + */ + private MovingRectThread mMovingRectThread; + + /** + * 切り抜き範囲移動用スレッドからのイベントを受け取るリスナー. + */ + private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(new Rect(rect)); + } + getStreamingSettings().setCropRect(rect); + postOnMoved(rect); + }; + public AbstractBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { mRecorder = recorder; mBroadcastURI = broadcastURI; mName = name; + startMovingRectThread(); } @Override @@ -70,46 +94,167 @@ public void onConfigChange() { @Override public void release() { + stopMovingRectThread(); + } + + // CropInterface implements + + private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); + + @Override + public void moveCropRect(Rect start, Rect end, int duration) { + if (end != null) { + if (getStreamingSettings().getCropRect() == null) { + postOnAdded(end); + } + } else { + postOnRemove(); + } + + if (mMovingRectThread != null) { + mMovingRectThread.move(start, end, duration); + } else { + getStreamingSettings().setCropRect(end); + + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(end); + } + } + } + + @Override + public void setCropRect(Rect rect) { + if (rect != null) { + if (getStreamingSettings().getCropRect() == null) { + postOnAdded(rect); + } + } else { + postOnRemove(); + } + + if (rect == null || mMovingRectThread == null) { + getStreamingSettings().setCropRect(rect); + + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(rect); + } + } else { + mMovingRectThread.set(rect); + } + } + + @Override + public Rect getCropRect() { + return getStreamingSettings().getCropRect(); + } + + @Override + public void addOnEventListener(CropInterface.OnEventListener listener) { + mOnEventListeners.add(listener); + } + + @Override + public void removeOnEventListener(CropInterface.OnEventListener listener) { + mOnEventListeners.remove(listener); + } + + private void postOnAdded(Rect rect) { + for (CropInterface.OnEventListener l : mOnEventListeners.get()) { + l.onAdded(this, rect); + } + } + + private void postOnRemove() { + for (CropInterface.OnEventListener l : mOnEventListeners.get()) { + l.onRemoved(this); + } + } + + private void postOnMoved(Rect rect) { + for (CropInterface.OnEventListener l : mOnEventListeners.get()) { + l.onMoved(this, rect); + } + } + + private void startMovingRectThread() { + if (mMovingRectThread != null) { + return; + } + + mMovingRectThread = new MovingRectThread(); + mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); + mMovingRectThread.start(); + } + + private void stopMovingRectThread() { + if (mMovingRectThread != null) { + mMovingRectThread.stop(); + mMovingRectThread = null; + } } /** - * 映像の設定を取得します. + * Broadcaster で使用するレコーダを取得します. * - * 映像が使用されていない場合は null を返却すること。 + * @return Broadcaster で使用するレコーダ + */ + public HostMediaRecorder getRecorder() { + return mRecorder; + } + + /** + * Broadcaster の設定を取得します. * - * @return 映像の設定 + * @return 設定 */ - protected VideoQuality getVideoQuality() { - return null; + public HostMediaRecorder.StreamingSettings getStreamingSettings() { + return mRecorder.getSettings().getBroadcaster(getName()); } /** - * 音声の設定を取得します. + * 配信するための映像用エンコーダを取得します. * - * 音声が使用されていない場合は null を返却すること。 + * @return 配信するための映像用エンコーダ + */ + protected VideoEncoder createVideoEncoder() { + return null; + } + + /** + * 配信するための音声用エンコーダを取得します. * - * @return 音声の設定 + * @return 配信するための音声用エンコーダ */ - protected AudioQuality getAudioQuality() { + protected AudioEncoder createAudioEncoder() { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + if (settings.isAudioEnabled()) { + return new MicAACLATMEncoder(); + } return null; } /** - * Broadcaster で使用するレコーダを取得します. + * 映像の設定を取得します. * - * @return Broadcaster で使用するレコーダ + * 映像が使用しない場合は null を返却すること。 + * + * @return 映像の設定 */ - public HostMediaRecorder getRecorder() { - return mRecorder; + protected VideoQuality getVideoQuality() { + return null; } /** - * Broadcaster の設定を取得します. + * 音声の設定を取得します. * - * @return 設定 + * 音声が使用しない場合は null を返却すること。 + * + * @return 音声の設定 */ - public HostMediaRecorder.StreamingSettings getStreamingSettings() { - return mRecorder.getSettings().getBroadcaster(getName()); + protected AudioQuality getAudioQuality() { + return null; } /** @@ -119,27 +264,24 @@ public HostMediaRecorder.StreamingSettings getStreamingSettings() { */ public void setVideoQuality(VideoQuality videoQuality) { HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(getMimeType()); - if (previewSize == null) { - previewSize = settings.getPreviewSize(); - } + Size previewSize = settings.getPreviewSize(); int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); videoQuality.setVideoWidth(w); videoQuality.setVideoHeight(h); - videoQuality.setCropRect(settings.getCropRect(getMimeType())); - videoQuality.setBitRate(settings.getPreviewBitRate(getMimeType())); - videoQuality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); - videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval(getMimeType())); - videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder(getMimeType())); - videoQuality.setIntraRefresh(settings.getIntraRefresh(getMimeType())); - videoQuality.setProfile(settings.getProfile(getMimeType())); - videoQuality.setLevel(settings.getLevel(getMimeType())); - if (settings.getPreviewBitRateMode(getMimeType()) != null) { - switch (settings.getPreviewBitRateMode(getMimeType())) { + videoQuality.setCropRect(settings.getCropRect()); + videoQuality.setBitRate(settings.getPreviewBitRate()); + videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); + videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); + videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); + videoQuality.setIntraRefresh(settings.getIntraRefresh()); + videoQuality.setProfile(settings.getProfile()); + videoQuality.setLevel(settings.getLevel()); + if (settings.getPreviewBitRateMode() != null) { + switch (settings.getPreviewBitRateMode()) { default: case VBR: videoQuality.setBitRateMode(VideoQuality.BitRateMode.VBR); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index cdde3b39f8..2fa0e6ce3f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -128,10 +128,10 @@ private void restartEncoder() { */ private void setMJPEGQuality(MJPEGQuality quality) { HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(getMimeType()); + Size previewSize = settings.getPreviewSize(); if (previewSize == null) { previewSize = settings.getPreviewSize(); } @@ -139,9 +139,9 @@ private void setMJPEGQuality(MJPEGQuality quality) { int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); quality.setWidth(w); quality.setHeight(h); - quality.setQuality(settings.getPreviewQuality(getMimeType())); - quality.setFrameRate(settings.getPreviewMaxFrameRate(getMimeType())); - quality.setDrawingRange(settings.getCropRect(getMimeType())); + quality.setQuality(settings.getPreviewQuality()); + quality.setFrameRate(settings.getPreviewMaxFrameRate()); + quality.setDrawingRange(settings.getCropRect()); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java index d516b3aa44..13897faada 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java @@ -60,25 +60,25 @@ public abstract class AbstractPreviewServer implements PreviewServer, CropInterf private final boolean mUseSSL; /** - * 名前. + * プレビュー配信サーバ名. */ private final String mName; /** - * 描画スレッド.å + * 切り抜き範囲移動用スレッド. */ private MovingRectThread mMovingRectThread; - private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = new MovingRectThread.OnEventListener() { - @Override - public void onMoved(Rect rect) { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(new Rect(rect)); - } - mHostMediaRecorder.getSettings().setCropRect(getMimeType(), new Rect(rect)); - postOnMoved(rect); + /** + * 切り抜き範囲移動用スレッドからのイベントを受け取るリスナー. + */ + private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(new Rect(rect)); } + getStreamingSettings().setCropRect(rect); + postOnMoved(rect); }; /** @@ -188,8 +188,7 @@ public void release() { @Override public void moveCropRect(Rect start, Rect end, int duration) { if (end != null) { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - if (settings.getCropRect(getMimeType()) == null) { + if (getStreamingSettings().getCropRect() == null) { postOnAdded(end); } } else { @@ -199,8 +198,7 @@ public void moveCropRect(Rect start, Rect end, int duration) { if (mMovingRectThread != null) { mMovingRectThread.move(start, end, duration); } else { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - settings.setCropRect(getMimeType(), end); + getStreamingSettings().setCropRect(end); VideoQuality videoQuality = getVideoQuality(); if (videoQuality != null) { @@ -212,8 +210,7 @@ public void moveCropRect(Rect start, Rect end, int duration) { @Override public void setCropRect(Rect rect) { if (rect != null) { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - if (settings.getCropRect(getMimeType()) == null) { + if (getStreamingSettings().getCropRect() == null) { postOnAdded(rect); } } else { @@ -221,8 +218,7 @@ public void setCropRect(Rect rect) { } if (rect == null || mMovingRectThread == null) { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - settings.setCropRect(getMimeType(), rect); + getStreamingSettings().setCropRect(rect); VideoQuality videoQuality = getVideoQuality(); if (videoQuality != null) { @@ -235,8 +231,7 @@ public void setCropRect(Rect rect) { @Override public Rect getCropRect() { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - return settings.getCropRect(getMimeType()); + return getStreamingSettings().getCropRect(); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java index 96feac5565..9139c93ba5 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java @@ -3,7 +3,6 @@ import org.deviceconnect.android.libmedia.streaming.MediaEncoderException; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; -import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder; import org.deviceconnect.android.libmedia.streaming.rtmp.RtmpClient; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; @@ -18,36 +17,14 @@ public abstract class AbstractRTMPBroadcaster extends AbstractBroadcaster { /** * イベントを通知するためのリスナー. */ - private OnEventListener mOnBroadcasterEventListener; + private Broadcaster.OnEventListener mOnBroadcasterEventListener; - public AbstractRTMPBroadcaster(HostMediaRecorder recorder, String broadcastURI) { - super(recorder, recorder.getId() + "-rtmp", broadcastURI); - } - - /** - * RTMP で配信するための映像用エンコーダを取得します. - * - * @return RTMP で配信するための映像用エンコーダ - */ - protected VideoEncoder createVideoEncoder() { - return null; - } - - /** - * RTMP で配信するための音声用エンコーダを取得します. - * - * @return RTMP で配信するための音声用エンコーダ - */ - protected AudioEncoder createAudioEncoder() { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - if (settings.isAudioEnabled()) { - return new MicAACLATMEncoder(); - } - return null; + public AbstractRTMPBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } @Override - public void setOnEventListener(OnEventListener listener) { + public void setOnEventListener(Broadcaster.OnEventListener listener) { mOnBroadcasterEventListener = listener; } @@ -68,7 +45,7 @@ public void start(OnStartCallback callback) { setAudioQuality(audioEncoder.getAudioQuality()); } - HostMediaRecorder.Settings settings = getRecorder().getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); mRtmpClient = new RtmpClient(getBroadcastURI()); mRtmpClient.setMaxRetryCount(settings.getRetryCount()); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java index 722783cd62..536f7e6119 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java @@ -183,7 +183,6 @@ protected VideoStream createVideoStream() { protected AudioStream createAudioStream() { HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - if (settings.isAudioEnabled()) { return new MicAACLATMStream(5004); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java index 10c8a42f3f..0b652d5235 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java @@ -9,7 +9,6 @@ import org.deviceconnect.android.libsrt.broadcast.SRTClient; public abstract class AbstractSRTBroadcaster extends AbstractBroadcaster { - /** * RTMP 配信クライアント. */ @@ -18,36 +17,14 @@ public abstract class AbstractSRTBroadcaster extends AbstractBroadcaster { /** * イベントを通知するためのリスナー. */ - private OnEventListener mOnBroadcasterEventListener; - - public AbstractSRTBroadcaster(HostMediaRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI, recorder.getId() + "-rtmp"); - } - - /** - * SRT 配信に使用する VideoEncoder のインスタンスを作成します. - * - * @return SRT 配信に使用する VideoEncoder - */ - protected VideoEncoder createVideoEncoder() { - return null; - } + private Broadcaster.OnEventListener mOnBroadcasterEventListener; - /** - * SRT 配信に使用する AudioEncoder のインスタンスを作成します. - * - * @return SRT 配信に使用する AudioEncoder - */ - protected AudioEncoder createAudioEncoder() { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - if (settings.isAudioEnabled()) { - return new MicAACLATMEncoder(); - } - return null; + public AbstractSRTBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } @Override - public void setOnEventListener(OnEventListener listener) { + public void setOnEventListener(Broadcaster.OnEventListener listener) { mOnBroadcasterEventListener = listener; } @@ -68,7 +45,7 @@ public void start(OnStartCallback callback) { setAudioQuality(audioEncoder.getAudioQuality()); } - HostMediaRecorder.Settings settings = getRecorder().getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); mSrtClient = new SRTClient(getBroadcastURI()); mSrtClient.setMaxRetryCount(settings.getRetryCount()); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index 367b8f4630..e0d1e787c7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -48,7 +48,7 @@ public String getMimeType() { public void startWebServer(final OnWebServerStartCallback callback) { if (mSRTServer == null) { try { - HostMediaRecorder.Settings settings = getRecorder().getSettings(); + HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); mSRTServer = new SRTServer(getPort()); mSRTServer.setStatsInterval(BuildConfig.STATS_INTERVAL); mSRTServer.setShowStats(DEBUG); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index b7ca8ebfab..26e2e8cb36 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -244,6 +244,32 @@ interface PermissionCallback { void onDisallowed(); } + enum MimeType { + MJPEG("video/x-mjpeg"), + RTSP("video/x-rtp"), + SRT("video/MP2T"), + RTMP("video/x-rtmp"); + + private final String mValue; + + MimeType(String value) { + mValue = value; + } + + public String getValue() { + return mValue; + } + + public static MimeType typeOf(String mimeType) { + for (MimeType type : values()) { + if (type.mValue.equalsIgnoreCase(mimeType)) { + return type; + } + } + return null; + } + } + enum AudioSource { DEFAULT("default"), MIC("mic"), @@ -460,22 +486,59 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host void onError(Exception e); } - public class StreamingSettings { + class StreamingSettings { private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; private final PropertyUtil mProperty; + private final Context mContext; public StreamingSettings(Context context, String name) { + mContext = context; mProperty = new PropertyUtil(context, name); } + /** + * データを削除します. + */ + public void clear() { + mProperty.clear(); + } + + /** + * 名前を取得します. + * + * @return 名前 + */ + public String getName() { + return mProperty.getString("name", null); + } + + /** + * 名前を設定します. + * + * @param name 名前 + */ + public void setName(String name) { + mProperty.put("name", name); + } + + /** + * マイムタイプを取得します. + * + * @return マイムタイプ + */ public String getMimeType() { return mProperty.getString("mimeType", null); } + /** + * マイムタイプを設定します. + * + * @param mimeType マイムタイプ + */ public void setMimeType(String mimeType) { mProperty.put("mimeType", mimeType); } @@ -861,6 +924,141 @@ public boolean isSupportedProfileLevel(int profile, int level) { } return false; } + + + /** + * SRT サーバに対して設定するオプションの一覧を作成します. + * + * @return オプションの一覧 + */ + public Map getSRTSocketOptions() { + Map options = new HashMap<>(); + for (SRTOptionItem item : SRT_OPTION_ITEMS) { + String key = mContext.getString(item.getPrefKey()); + String value = mProperty.getString(key, null); + if (value == null || "".equals(value)) { + continue; + } + + try { + if (item.getValueClass() == Long.class) { + options.put(item.getOptionEnum(), Long.parseLong(value)); + } else if (item.getValueClass() == Integer.class) { + options.put(item.getOptionEnum(), Integer.parseInt(value)); + } else { + options.put(item.getOptionEnum(), value); + } + } catch (Exception ignored) {} + } + return options; + } + + /** + * 設定画面でサポートする SRT オプションの定義. + */ + private static final List SRT_OPTION_ITEMS = Arrays.asList( + new SRTOptionItem(SRT.SRTO_PEERLATENCY, Integer.class, R.string.pref_key_settings_srt_peerlatency), + new SRTOptionItem(SRT.SRTO_LOSSMAXTTL, Integer.class, R.string.pref_key_settings_srt_lossmaxttl), + new SRTOptionItem(SRT.SRTO_INPUTBW, Long.class, R.string.pref_key_settings_srt_inputbw), + new SRTOptionItem(SRT.SRTO_OHEADBW, Integer.class, R.string.pref_key_settings_srt_oheadbw), + new SRTOptionItem(SRT.SRTO_CONNTIMEO, Integer.class, R.string.pref_key_settings_srt_conntimeo), + new SRTOptionItem(SRT.SRTO_PEERIDLETIMEO, Integer.class, R.string.pref_key_settings_srt_peeridletimeo), + new SRTOptionItem(SRT.SRTO_PACKETFILTER, String.class, R.string.pref_key_settings_srt_packetfilter)); + + /** + * SRT オプション設定項目の定義. + * + * SRT オプションの列挙子 ({@link SRT} で定義されているもの) に対して、値の型とプリファレンスキーを対応づける. + */ + private static class SRTOptionItem { + final int mOptionEnum; + final Class mValueClass; + final int mPrefKey; + + SRTOptionItem(int optionEnum, Class valueClass, int prefKey) { + mOptionEnum = optionEnum; + mValueClass = valueClass; + mPrefKey = prefKey; + } + + int getOptionEnum() { + return mOptionEnum; + } + + int getPrefKey() { + return mPrefKey; + } + + Class getValueClass() { + return mValueClass; + } + } + + // 配信 + + /** + * 配信先の URI を取得します. + * + * 設定されていない場合は null を返却します. + * + * @return 配信先の URI + */ + public String getBroadcastURI() { + return mProperty.getString("broadcast_uri", null); + } + + /** + * 配信先の URI を設定します. + * + * @param broadcastURI 配信先の URI + */ + public void setBroadcastURI(String broadcastURI) { + mProperty.put("broadcast_uri", broadcastURI); + } + + /** + * リトライ回数を取得します. + * + * @return リトライ回数 + */ + public int getRetryCount() { + return mProperty.getInteger("broadcast_retry_count", 0); + } + + /** + * リトライ回数を設定します. + * + * @param count リトライ回数 + */ + public void setRetryCount(int count) { + if (count < 0) { + mProperty.remove("broadcast_retry_count"); + } else { + mProperty.put("broadcast_retry_count", count); + } + } + + /** + * リトライのインターバルを取得します. + * + * @return リトライのインターバル + */ + public int getRetryInterval() { + return mProperty.getInteger("broadcast_retry_interval", 3000); + } + + /** + * リトライのインターバルを設定します. + * + * @param interval リトライのインターバル + */ + public void setRetryInterval(int interval) { + if (interval < 0) { + mProperty.remove("broadcast_retry_interval"); + } else { + mProperty.put("broadcast_retry_interval", interval); + } + } } /** @@ -868,20 +1066,16 @@ public boolean isSupportedProfileLevel(int profile, int level) { */ abstract class Settings { private final PropertyUtil mProperty; - private final Map mPropertyMap = new HashMap<>(); private final Context mContext; private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; + private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; public Settings(Context context, HostMediaRecorder recorder) { mContext = context; mProperty = new PropertyUtil(context, recorder.getId()); - mPropertyMap.put("video/x-mjpeg", new PropertyUtil(context, recorder.getId() + "-mjpeg")); - mPropertyMap.put("video/x-rtp", new PropertyUtil(context, recorder.getId() + "-rtsp")); - mPropertyMap.put("video/MP2T", new PropertyUtil(context, recorder.getId() + "-srt")); - mPropertyMap.put("video/x-rtmp", new PropertyUtil(context, recorder.getId() + "-rtmp")); } /** @@ -904,13 +1098,13 @@ public void finishInitialization() { * 保存データを初期化します. */ public void clear() { - mProperty.clear(); - for (String key : mPropertyMap.keySet()) { - PropertyUtil prof = mPropertyMap.get(key); - if (prof != null) { - prof.clear(); - } + for (String name : getPreviewServerList()) { + getPreviewServer(name).clear(); } + for (String name : getBroadcasterList()) { + getBroadcaster(name).clear(); + } + mProperty.clear(); } public List getPreviewServerList() { @@ -1019,6 +1213,27 @@ public void setPreviewSize(Size previewSize) { mProperty.put("preview_size_width", "preview_size_height", previewSize); } + /** + * フレームレートを取得します. + * + * @return フレームレート + */ + public int getPreviewMaxFrameRate() { + return mProperty.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); + } + + /** + * フレームレートを設定します. + * + * @param previewMaxFrameRate フレームレート + */ + public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { + if (previewMaxFrameRate <= 0) { + throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); + } + mProperty.put("preview_framerate", previewMaxFrameRate); + } + /** * 自動フォーカスモードを取得します. * @@ -1177,6 +1392,13 @@ public void setDigitalZoom(Float zoom) { } } + /** + * 焦点距離を取得します. + * + * 未設定の場合は null を返却します。 + * + * @return 焦点距離 + */ public Float getFocalLength() { Float value = mProperty.getFloat("preview_focal_length", null); if (value == null) { @@ -1191,6 +1413,14 @@ public Float getFocalLength() { return null; } + /** + * 焦点距離を設定します. + * + * focalLength に null が指定された場合には、設定を削除します。 + * サポートされていない値が指定された場合には、IllegalArgumentException が発生します。 + * + * @param focalLength 焦点距離 + */ public void setFocalLength(Float focalLength) { if (focalLength == null) { mProperty.remove("preview_focal_length"); @@ -1202,10 +1432,25 @@ public void setFocalLength(Float focalLength) { } } + /** + * ノイズ低減モードを取得します. + * + * 未設定の場合は、null を返却します。 + * + * @return ノイズ低減モード + */ public Integer getNoiseReduction() { return mProperty.getInteger("preview_reduction_noise", null); } + /** + * ノイズ低減モードを設定します. + * + * mode に null が指定された場合には、設定を削除します。 + * サポートされていない値が指定された場合には、IllegalArgumentException が発生します。 + * + * @param mode ノイズ低減モード + */ public void setNoiseReduction(Integer mode) { if (mode == null) { mProperty.remove("preview_reduction_noise"); @@ -1255,17 +1500,29 @@ public void setSensorSensitivity(Integer sensitivity) { if (sensitivity == null) { mProperty.remove("preview_sensor_sensitivity"); } else { - if (!isSupportedSensorSensorSensitivity(sensitivity)) { + if (!isSupportedSensorSensitivity(sensitivity)) { throw new IllegalArgumentException("sensitivity cannot set."); } mProperty.put("preview_sensor_sensitivity", sensitivity); } } + /** + * フレーム時間を取得します. + * + * 未設定の場合は null を返却します。 + * + * @return フレーム時間 + */ public Long getSensorFrameDuration() { return mProperty.getLong("preview_sensor_frame_duration", null); } + /** + * フレーム時間を設定します. + * + * @param frameDuration フレーム時間 + */ public void setSensorFrameDuration(Long frameDuration) { if (frameDuration == null) { mProperty.remove("preview_sensor_frame_duration"); @@ -1277,10 +1534,22 @@ public void setSensorFrameDuration(Long frameDuration) { } } + /** + * 色温度を取得します. + * + * 未設定の場合は null を返却します。 + * + * @return 色温度 + */ public Integer getPreviewWhiteBalanceTemperature() { return mProperty.getInteger("preview_sensor_white_balance_temperature", null); } + /** + * 色温度を設定します. + * + * @param temperature 色温度 + */ public void setPreviewWhiteBalanceTemperature(Integer temperature) { if (temperature == null) { mProperty.remove("preview_sensor_white_balance_temperature"); @@ -1292,586 +1561,40 @@ public void setPreviewWhiteBalanceTemperature(Integer temperature) { } } - /// ポート番号 + + /// サポートしているデータサイズ /** - * サーバ用のポート番号を取得します. + * サポートしている写真サイズを取得します. * - * @return サーバ用のポート番号 + * サポートしていない場合には空のリストを返却します。 + * + * @return サポートしている写真サイズ */ - public Integer getPort(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("port", 0); - } - return 0; + public List getSupportedPictureSizes() { + return new ArrayList<>(); } /** - * サーバ用のポート番号を設定します. + * サポートしているプレビューサイズを取得します. * - * @param mimeType マイムタイプ - * @param port サーバ用のポート番号 + * サポートしていない場合には空のリストを返却します。 + * + * @return サポートしているプレビューサイズ */ - public void setPort(String mimeType, int port) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("port", port); - } + public List getSupportedPreviewSizes() { + return new ArrayList<>(); } - //// MediaCodec - - public Size getPreviewSize(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getSize("preview_size_width", "preview_size_height"); - } - return null; - } - - public void setPreviewSize(String mimeType, Size previewSize) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("preview_size_width", "preview_size_height", previewSize); - } - } - - public VideoEncoderName getPreviewEncoderName(String mimeType) { - return VideoEncoderName.nameOf(getPreviewEncoder(mimeType)); - } - - public String getPreviewEncoder(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getString("preview_encoder", DEFAULT_PREVIEW_ENCODER); - } - return DEFAULT_PREVIEW_ENCODER; - } - - public void setPreviewEncoder(String mimeType, String encoder) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - if (encoder == null) { - prof.remove("preview_encoder"); - } else { - if (!isSupportedVideoEncoder(encoder)) { - throw new IllegalArgumentException("encoder is not supported."); - } - prof.put("preview_encoder", encoder); - } - } - } - - /** - * プレビュー配信エンコード名を取得します. - * - * @return エンコード名 - */ - public VideoEncoderName getPreviewEncoderName() { - return VideoEncoderName.nameOf(getPreviewEncoder()); - } - - /** - * プレビューの配信エンコードの名前を取得します. - * - * 未設定の場合は h264 を返却します。 - * - * @return プレビューの配信エンコードの名前 - */ - public String getPreviewEncoder() { - return mProperty.getString("preview_encoder", DEFAULT_PREVIEW_ENCODER); - } - - /** - * プレビューの配信エンコードの名前を設定します. - * - * @param encoder プレビューの配信エンコードの名前 - */ - public void setPreviewEncoder(String encoder) { - if (encoder == null) { - mProperty.remove("preview_encoder"); - } else { - if (!isSupportedVideoEncoder(encoder)) { - throw new IllegalArgumentException("encoder is not supported."); - } - mProperty.put("preview_encoder", encoder); - } - } - - public ProfileLevel getProfileLevel(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - Integer profile = prof.getInteger("preview_profile", null); - Integer level = prof.getInteger("preview_level", null); - if (profile != null && level != null) { - return new ProfileLevel(profile, level); - } - } - return null; - } - - public void setProfileLevel(String mimeType, ProfileLevel pl) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - if (pl == null) { - prof.remove("preview_profile"); - prof.remove("preview_level"); - } else { - if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) { - throw new IllegalArgumentException("profile and level are not supported."); - } - prof.put("preview_profile", pl.getProfile()); - prof.put("preview_level", pl.getLevel()); - } - } - } - - /** - * プロファイルとレベルを取得します. - * - * 未設定の場合には、null を返却します。 - * - * @return プロファイルとレベル - */ - public ProfileLevel getProfileLevel() { - Integer profile = mProperty.getInteger("preview_profile", null); - Integer level = mProperty.getInteger("preview_level", null); - if (profile != null && level != null) { - return new ProfileLevel(profile, level); - } - return null; - } - - /** - * プロファイルとレベルを設定します. - * - * null が設定された場合には、値を削除して未設定にします。 - * - * サポートされていないプロファイルとレベルが設定された場合には例外を発生します。 - * - * @param pl プロファイルとレベル - */ - public void setProfileLevel(ProfileLevel pl) { - if (pl == null) { - mProperty.remove("preview_profile"); - mProperty.remove("preview_level"); - } else { - if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) { - throw new IllegalArgumentException("profile and level are not supported."); - } - mProperty.put("preview_profile", pl.getProfile()); - mProperty.put("preview_level", pl.getLevel()); - } - } - - public Integer getProfile(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("preview_profile", 0); - } - return 0; - } - - /** - * 設定されているプロファイルを取得します. - * - * @return プロファイル - */ - public Integer getProfile() { - return mProperty.getInteger("preview_profile", 0); - } - - public Integer getLevel(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.getInteger("preview_level", 0); - } - return 0; - } - - /** - * 設定されているレベルを取得します. - * - * @return レベル - */ - public Integer getLevel() { - return mProperty.getInteger("preview_level", 0); - } - - public int getPreviewMaxFrameRate(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); - } - return DEFAULT_PREVIEW_MAX_FRAME_RATE; - } - - public void setPreviewMaxFrameRate(String mimeType, Integer previewMaxFrameRate) { - if (previewMaxFrameRate <= 0) { - throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); - } - - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("preview_framerate", previewMaxFrameRate); - } - } - - /** - * フレームレートを取得します. - * - * @return フレームレート - */ - public int getPreviewMaxFrameRate() { - return mProperty.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); - } - - /** - * フレームレートを設定します. - * - * @param previewMaxFrameRate フレームレート - */ - public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { - if (previewMaxFrameRate <= 0) { - throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); - } - mProperty.put("preview_framerate", previewMaxFrameRate); - } - - public int getPreviewBitRate(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("preview_bitrate", DEFAULT_PREVIEW_BITRATE); - } - return DEFAULT_PREVIEW_BITRATE; - } - - public void setPreviewBitRate(String mimeType, int previewBitRate) { - if (previewBitRate <= 0) { - throw new IllegalArgumentException("previewBitRate is zero or negative."); - } - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("preview_bitrate", String.valueOf(previewBitRate)); - } - } - - /** - * ビットレートを取得します. - * - * @return ビットレート(byte) - */ - public int getPreviewBitRate() { - return mProperty.getInteger("preview_bitrate", DEFAULT_PREVIEW_BITRATE); - } - - /** - * ビットレートを設定します. - * - * @param previewBitRate ビットレート(byte) - */ - public void setPreviewBitRate(int previewBitRate) { - if (previewBitRate <= 0) { - throw new IllegalArgumentException("previewBitRate is zero or negative."); - } - mProperty.put("preview_bitrate", String.valueOf(previewBitRate)); - } - - public BitRateMode getPreviewBitRateMode(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return BitRateMode.nameOf(prof.getString("preview_bitrate_mode", null)); - } - return null; - } - - public void setPreviewBitRateMode(String mimeType, BitRateMode mode) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - if (mode == null) { - prof.remove("preview_bitrate_mode"); - } else { - prof.put("preview_bitrate_mode", mode.getName()); - } - } - } - - public BitRateMode getPreviewBitRateMode() { - return BitRateMode.nameOf(mProperty.getString("preview_bitrate_mode", null)); - } - - public void setPreviewBitRateMode(BitRateMode mode) { - if (mode == null) { - mProperty.remove("preview_bitrate_mode"); - } else { - mProperty.put("preview_bitrate_mode", mode.getName()); - } - } - - private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; - - public int getPreviewKeyFrameInterval(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("preview_i_frame_interval", DEFAULT_PREVIEW_KEY_FRAME_INTERVAL); - } - return DEFAULT_PREVIEW_KEY_FRAME_INTERVAL; - } - - public void setPreviewKeyFrameInterval(String mimeType, int previewKeyFrameInterval) { - if (previewKeyFrameInterval <= 0) { - throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); - } - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("preview_i_frame_interval", previewKeyFrameInterval); - } - } - - /** - * キーフレームインターバルを取得します. - * - * @return キーフレームを発行する間隔(ミリ秒) - */ - public int getPreviewKeyFrameInterval() { - return mProperty.getInteger("preview_i_frame_interval", DEFAULT_PREVIEW_KEY_FRAME_INTERVAL); - } - - /** - * キーフレームインターバルを設定します. - * - * @param previewKeyFrameInterval キーフレームを発行する間隔(ミリ秒) - */ - public void setPreviewKeyFrameInterval(int previewKeyFrameInterval) { - if (previewKeyFrameInterval <= 0) { - throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); - } - mProperty.put("preview_i_frame_interval", previewKeyFrameInterval); - } - - public boolean isUseSoftwareEncoder(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getBoolean("preview_use_software_encoder", false); - } - return false; - } - - public void setUseSoftwareEncoder(String mimeType, boolean used) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("preview_use_software_encoder", used); - } - } - - /** - * ソフトウェアエンコーダを優先的に使用するフラグを確認します. - * - * @return ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false - */ - public boolean isUseSoftwareEncoder() { - return mProperty.getBoolean("preview_use_software_encoder", false); - } - - /** - * ソフトウェアエンコーダを優先的に使用するフラグを設定します. - * - * @param used ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false - */ - public void setUseSoftwareEncoder(boolean used) { - mProperty.put("preview_use_software_encoder", used); - } - - public Integer getIntraRefresh(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("preview_intra_refresh", 0); - } - return 0; - } - - public void setIntraRefresh(String mimeType, Integer refresh) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - if (refresh == null) { - prof.remove("preview_intra_refresh"); - } else { - prof.put("preview_intra_refresh", refresh); - } - } - } - - /** - * イントラリフレッシュのフレーム数を取得します. - * - * @return イントラリフレッシュのフレーム数 - */ - public Integer getIntraRefresh() { - return mProperty.getInteger("preview_intra_refresh", 0); - } - - /** - * イントラリフレッシュのフレーム数を設定します. - * - * @param refresh イントラリフレッシュのフレーム数 - */ - public void setIntraRefresh(Integer refresh) { - if (refresh == null) { - mProperty.remove("preview_intra_refresh"); - } else { - mProperty.put("preview_intra_refresh", refresh); - } - } - - public Rect getCropRect(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getRect("preview_clip_left", - "preview_clip_top", - "preview_clip_right", - "preview_clip_bottom"); - } - return null; - } - - public void setCropRect(String mimeType, Rect rect) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - if (rect == null) { - prof.remove("preview_clip_left"); - prof.remove("preview_clip_top"); - prof.remove("preview_clip_right"); - prof.remove("preview_clip_bottom"); - } else { - prof.put( - "preview_clip_left", - "preview_clip_top", - "preview_clip_right", - "preview_clip_bottom", - rect); - } - } - } - - /** - * 切り抜き範囲を取得します. - * - * 範囲ば設定されていない場合には、null を返却します. - * - * @return 切り抜き範囲 - */ - public Rect getCropRect() { - return mProperty.getRect("preview_clip_left", - "preview_clip_top", - "preview_clip_right", - "preview_clip_bottom"); - } - - /** - * 切り抜き範囲を設定します. - * - * 引数に null が指定された場合には、切り抜き範囲を削除します。 - * - * @param rect 切り抜き範囲 - */ - public void setCropRect(Rect rect) { - if (rect == null) { - mProperty.remove("preview_clip_left"); - mProperty.remove("preview_clip_top"); - mProperty.remove("preview_clip_right"); - mProperty.remove("preview_clip_bottom"); - } else { - mProperty.put( - "preview_clip_left", - "preview_clip_top", - "preview_clip_right", - "preview_clip_bottom", - rect); - } - } - - public int getPreviewQuality(String mimeType) { - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - return prof.getInteger("preview_jpeg_quality", 80); - } - return 80; - } - - public void setPreviewQuality(String mimeType, int quality) { - if (quality < 0) { - throw new IllegalArgumentException("quality is negative value."); - } - if (quality > 100) { - throw new IllegalArgumentException("quality is over 100."); - } - PropertyUtil prof = mPropertyMap.get(mimeType); - if (prof != null) { - prof.put("preview_jpeg_quality", quality); - } - } - - /** - * プレビューの品質を取得します. - * - * @return プレビューの品質 - */ - public int getPreviewQuality() { - return mProperty.getInteger("preview_jpeg_quality", 80); - } - - /** - * プレビューの品質を設定します. - * - * 0 から 100 の間で設定することができます。 - * それ以外は例外が発生します。 - * - * @param quality プレビューの品質 - */ - public void setPreviewQuality(int quality) { - if (quality < 0) { - throw new IllegalArgumentException("quality is negative value."); - } - if (quality > 100) { - throw new IllegalArgumentException("quality is over 100."); - } - mProperty.put("preview_jpeg_quality", quality); - } - - /// サポートしているデータサイズ - - /** - * サポートしている写真サイズを取得します. - * - * サポートしていない場合には空のリストを返却します。 - * - * @return サポートしている写真サイズ - */ - public List getSupportedPictureSizes() { - return new ArrayList<>(); - } - - /** - * サポートしているプレビューサイズを取得します. - * - * サポートしていない場合には空のリストを返却します。 - * - * @return サポートしているプレビューサイズ - */ - public List getSupportedPreviewSizes() { - return new ArrayList<>(); - } - - /** - * サポートしている FPS のリストを取得します. - * - * サポートしていない場合には空のリストを返却します。 - * - * @return サポートしている FPS のリスト - */ - public List> getSupportedFps() { - return new ArrayList<>(); + /** + * サポートしている FPS のリストを取得します. + * + * サポートしていない場合には空のリストを返却します。 + * + * @return サポートしている FPS のリスト + */ + public List> getSupportedFps() { + return new ArrayList<>(); } /** @@ -1983,8 +1706,7 @@ public List getSupportedVideoEncoders() { * * @return サポートしているプロファイル・レベルの一覧 */ - public List getSupportedProfileLevel() { - VideoEncoderName encoderName = getPreviewEncoderName(); + public List getSupportedProfileLevel(VideoEncoderName encoderName) { return CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); } @@ -2121,6 +1843,12 @@ public boolean isSupportedWhiteBalanceMode(int whiteBalance) { return false; } + /** + * 指定された色温度がサポートされているか確認します. + * + * @param temperature 色温度 + * @return サポートされている場合はtrue、それ以外はfalse + */ public boolean isSupportedWhiteBalanceTemperature(int temperature) { Range range = getSupportedWhiteBalanceTemperature(); if (range != null) { @@ -2129,6 +1857,12 @@ public boolean isSupportedWhiteBalanceTemperature(int temperature) { return false; } + /** + * 指定された自動露出モードがサポートされているか確認します. + * + * @param mode 自動露出モード + * @return サポートされている場合はtrue、それ以外はfalse + */ public boolean isSupportedAutoExposureMode(Integer mode) { List modeList = getSupportedAutoExposureModeList(); if (modeList != null) { @@ -2141,6 +1875,12 @@ public boolean isSupportedAutoExposureMode(Integer mode) { return false; } + /** + * 指定された露出時間がサポートされているか確認します. + * + * @param time 露出時間 + * @return サポートされている場合はtrue、それ以外はfalse + */ public boolean isSupportedSensorExposureTime(long time) { Range range = getSupportedSensorExposureTime(); if (range != null) { @@ -2149,7 +1889,13 @@ public boolean isSupportedSensorExposureTime(long time) { return false; } - public boolean isSupportedSensorSensorSensitivity(int sensitivity) { + /** + * 指定された ISO 感度がサポートされているか確認します. + * + * @param sensitivity ISO 感度 + * @return サポートされている場合はtrue、それ以外はfalse + */ + public boolean isSupportedSensorSensitivity(int sensitivity) { Range range = getSupportedSensorSensitivity(); if (range != null) { return range.getLower() <= sensitivity && sensitivity <= range.getUpper(); @@ -2157,6 +1903,12 @@ public boolean isSupportedSensorSensorSensitivity(int sensitivity) { return false; } + /** + * 指定されたフレーム時間がサポートされているか確認します. + * + * @param frameDuration フレーム時間 + * @return サポートされている場合はtrue、それ以外はfalse + */ public boolean isSupportedSensorFrameDuration(long frameDuration) { Long maxFrameDuration = getMaxSensorFrameDuration(); if (maxFrameDuration != null) { @@ -2190,8 +1942,8 @@ public boolean isSupportedVideoEncoder(String encoder) { * @param level レベル * @return サポートされている場合はtrue、それ以外はfalse */ - public boolean isSupportedProfileLevel(int profile, int level) { - List list = getSupportedProfileLevel(); + public boolean isSupportedProfileLevel(VideoEncoderName encoderName, int profile, int level) { + List list = getSupportedProfileLevel(encoderName); if (list != null) { for (ProfileLevel pl : list) { if (profile == pl.getProfile() && level == pl.getLevel()) { @@ -2495,149 +2247,5 @@ public boolean isSupportedSampleRate(int sampleRate) { } return false; } - - // 配信 - - /** - * 配信先の URI を取得します. - * - * 設定されていない場合は null を返却します. - * - * @return 配信先の URI - */ - public String getBroadcastURI() { - PropertyUtil prof = mPropertyMap.get("video/x-rtmp"); - if (prof != null) { - return prof.getString("broadcast_uri", null); - } - return mProperty.getString("broadcast_uri", null); - } - - /** - * 配信先の URI を設定します. - * - * @param broadcastURI 配信先の URI - */ - public void setBroadcastURI(String broadcastURI) { - mProperty.put("broadcast_uri", broadcastURI); - } - - /** - * リトライ回数を取得します. - * - * @return リトライ回数 - */ - public int getRetryCount() { - return mProperty.getInteger("broadcast_retry_count", 0); - } - - /** - * リトライ回数を設定します. - * - * @param count リトライ回数 - */ - public void setRetryCount(int count) { - if (count < 0) { - mProperty.remove("broadcast_retry_count"); - } else { - mProperty.put("broadcast_retry_count", count); - } - } - - /** - * リトライのインターバルを取得します. - * - * @return リトライのインターバル - */ - public int getRetryInterval() { - return mProperty.getInteger("broadcast_retry_interval", 3000); - } - - /** - * リトライのインターバルを設定します. - * - * @param interval リトライのインターバル - */ - public void setRetryInterval(int interval) { - if (interval < 0) { - mProperty.remove("broadcast_retry_interval"); - } else { - mProperty.put("broadcast_retry_interval", interval); - } - } - - // SRT - - /** - * SRT サーバに対して設定するオプションの一覧を作成します. - * - * @return オプションの一覧 - */ - public Map getSRTSocketOptions() { - PropertyUtil prof = mPropertyMap.get("video/MP2T"); - if (prof != null) { - Map options = new HashMap<>(); - for (SRTOptionItem item : SRT_OPTION_ITEMS) { - String key = mContext.getString(item.getPrefKey()); - String value = prof.getString(key, null); - if (value == null || "".equals(value)) { - continue; - } - - try { - if (item.getValueClass() == Long.class) { - options.put(item.getOptionEnum(), Long.parseLong(value)); - } else if (item.getValueClass() == Integer.class) { - options.put(item.getOptionEnum(), Integer.parseInt(value)); - } else { - options.put(item.getOptionEnum(), value); - } - } catch (Exception ignored) {} - } - return options; - } - return null; - } - - /** - * 設定画面でサポートする SRT オプションの定義. - */ - private static final List SRT_OPTION_ITEMS = Arrays.asList( - new SRTOptionItem(SRT.SRTO_PEERLATENCY, Integer.class, R.string.pref_key_settings_srt_peerlatency), - new SRTOptionItem(SRT.SRTO_LOSSMAXTTL, Integer.class, R.string.pref_key_settings_srt_lossmaxttl), - new SRTOptionItem(SRT.SRTO_INPUTBW, Long.class, R.string.pref_key_settings_srt_inputbw), - new SRTOptionItem(SRT.SRTO_OHEADBW, Integer.class, R.string.pref_key_settings_srt_oheadbw), - new SRTOptionItem(SRT.SRTO_CONNTIMEO, Integer.class, R.string.pref_key_settings_srt_conntimeo), - new SRTOptionItem(SRT.SRTO_PEERIDLETIMEO, Integer.class, R.string.pref_key_settings_srt_peeridletimeo), - new SRTOptionItem(SRT.SRTO_PACKETFILTER, String.class, R.string.pref_key_settings_srt_packetfilter)); - - /** - * SRT オプション設定項目の定義. - * - * SRT オプションの列挙子 ({@link SRT} で定義されているもの) に対して、値の型とプリファレンスキーを対応づける. - */ - private static class SRTOptionItem { - final int mOptionEnum; - final Class mValueClass; - final int mPrefKey; - - SRTOptionItem(int optionEnum, Class valueClass, int prefKey) { - mOptionEnum = optionEnum; - mValueClass = valueClass; - mPrefKey = prefKey; - } - - int getOptionEnum() { - return mOptionEnum; - } - - int getPrefKey() { - return mPrefKey; - } - - Class getValueClass() { - return mValueClass; - } - } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java index 488d9ab72a..5b88dbccad 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java @@ -4,6 +4,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractBroadcastProvider; import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class AudioBroadcasterProvider extends AbstractBroadcastProvider { @@ -16,10 +17,18 @@ public AudioBroadcasterProvider(Context context, HostAudioRecorder recorder) { @Override public Broadcaster createBroadcaster(String broadcastURI) { + String name = null; + for (String n : mRecorder.getSettings().getBroadcasterList()) { + HostMediaRecorder.StreamingSettings s = mRecorder.getSettings().getBroadcaster(n); + if (broadcastURI.equals(s.getBroadcastURI())) { + name = n; + } + } + if (broadcastURI.startsWith("srt://")) { - return new AudioSRTBroadcaster(mRecorder, broadcastURI); + return new AudioSRTBroadcaster(mRecorder, broadcastURI, name); } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) { - return new AudioRTMPBroadcaster(mRecorder, broadcastURI); + return new AudioRTMPBroadcaster(mRecorder, broadcastURI, name); } else { return null; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java index ac0eaf6a0f..382476d608 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java @@ -4,13 +4,14 @@ import android.graphics.Color; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractRTMPBroadcaster; +import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; import org.deviceconnect.android.libmedia.streaming.video.CanvasVideoEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class AudioRTMPBroadcaster extends AbstractRTMPBroadcaster { private final HostAudioRecorder mRecorder; - public AudioRTMPBroadcaster(HostAudioRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + public AudioRTMPBroadcaster(HostAudioRecorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); mRecorder = recorder; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java index 47a269b5d4..0baa474837 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java @@ -8,6 +8,6 @@ public class AudioRTSPPreviewServer extends AbstractRTSPPreviewServer { AudioRTSPPreviewServer(Context context, HostMediaRecorder recorder) { super(context, recorder); - setPort(recorder.getSettings().getPort(getMimeType())); + setPort(getStreamingSettings().getPort()); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java index 35e58e4050..804a3a6bb3 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java @@ -3,7 +3,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTBroadcaster; public class AudioSRTBroadcaster extends AbstractSRTBroadcaster { - public AudioSRTBroadcaster(HostAudioRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + public AudioSRTBroadcaster(HostAudioRecorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java index 5c62af7c73..ab6816a609 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java @@ -1,7 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.recorder.audio; import android.content.Context; -import android.util.Log; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; @@ -9,6 +8,6 @@ public class AudioSRTPreviewServer extends AbstractSRTPreviewServer { AudioSRTPreviewServer(final Context context, final HostMediaRecorder recorder) { super(context, recorder); - setPort(recorder.getSettings().getPort(getMimeType())); + setPort(getStreamingSettings().getPort()); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java index a97d1ab3da..3d76317dd4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java @@ -75,9 +75,9 @@ public HostAudioRecorder(final Context context, FileManager fileManager, MediaPr private void initSettings() { if (!mSettings.isInitialized()) { mSettings.setPreviewSize(new Size(320, 240)); - mSettings.setPreviewBitRate(512 * 1024); +// mSettings.setPreviewBitRate(512 * 1024); mSettings.setPreviewMaxFrameRate(30); - mSettings.setPreviewKeyFrameInterval(1); +// mSettings.setPreviewKeyFrameInterval(1); mSettings.setPreviewAudioSource(AudioSource.DEFAULT); mSettings.setPreviewAudioBitRate(128 * 1024); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java index 95d167af93..60a0f058cc 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java @@ -4,6 +4,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractBroadcastProvider; import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class Camera2BroadcasterProvider extends AbstractBroadcastProvider { /** @@ -18,10 +19,18 @@ public Camera2BroadcasterProvider(Context context, Camera2Recorder recorder) { @Override public Broadcaster createBroadcaster(String broadcastURI) { + String name = null; + for (String n : mRecorder.getSettings().getBroadcasterList()) { + HostMediaRecorder.StreamingSettings s = mRecorder.getSettings().getBroadcaster(n); + if (broadcastURI.equals(s.getBroadcastURI())) { + name = n; + } + } + if (broadcastURI.startsWith("srt://")) { - return new Camera2SRTBroadcaster(mRecorder, broadcastURI); + return new Camera2SRTBroadcaster(mRecorder, broadcastURI, name); } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) { - return new Camera2RTMPBroadcaster(mRecorder, broadcastURI); + return new Camera2RTMPBroadcaster(mRecorder, broadcastURI, name); } else { return null; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java index bbdc5bb7c2..13bc6410cb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java @@ -18,7 +18,7 @@ class Camera2MJPEGPreviewServer extends AbstractMJPEGPreviewServer { Camera2MJPEGPreviewServer(Context context, Camera2Recorder recorder, boolean useSSL) { super(context, recorder, useSSL); - setPort(recorder.getSettings().getPort(getMimeType()) + (useSSL ? 1 : 0)); + setPort(getStreamingSettings().getPort() + (useSSL ? 1 : 0)); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java index 96ab040074..b6e7d1869b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java @@ -5,8 +5,8 @@ public class Camera2RTMPBroadcaster extends AbstractRTMPBroadcaster { - public Camera2RTMPBroadcaster(Camera2Recorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + public Camera2RTMPBroadcaster(Camera2Recorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java index 300a4b01c7..dc5807de34 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java @@ -6,7 +6,6 @@ import androidx.annotation.RequiresApi; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractRTSPPreviewServer; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.VideoStream; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) @@ -14,14 +13,13 @@ class Camera2RTSPPreviewServer extends AbstractRTSPPreviewServer { Camera2RTSPPreviewServer(Context context, Camera2Recorder recorder) { super(context, recorder); - setPort(recorder.getSettings().getPort(getMimeType())); + setPort(getStreamingSettings().getPort()); } @Override protected VideoStream createVideoStream() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName(getMimeType())) { + switch (getStreamingSettings().getPreviewEncoderName()) { case H264: default: return new CameraH264VideoStream(recorder, 5006); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index ef1f4b510f..cd510ede73 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -13,7 +13,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; -import android.util.Log; import android.util.Range; import android.util.Size; import android.util.SparseIntArray; @@ -174,46 +173,50 @@ private void initSupportedSettings() { mSettings.mSupportedPreviewSize = supportPreviewSizes; if (!mSettings.isInitialized()) { + // カメラ設定 mSettings.setPictureSize(options.getDefaultPictureSize()); mSettings.setPreviewSize(options.getDefaultPreviewSize()); - mSettings.setPreviewBitRate(2 * 1024 * 1024); +// mSettings.setPreviewBitRate(2 * 1024 * 1024); mSettings.setPreviewMaxFrameRate(30); - mSettings.setPreviewKeyFrameInterval(1); - mSettings.setPreviewQuality(80); +// mSettings.setPreviewKeyFrameInterval(1); +// mSettings.setPreviewQuality(80); mSettings.setPreviewAutoFocusMode(options.getAutoFocusMode()); mSettings.setPreviewWhiteBalance(options.getAutoWhiteBalanceMode()); mSettings.setPreviewWhiteBalanceTemperature(5600); + // 音声設定 mSettings.setPreviewAudioSource(null); mSettings.setPreviewAudioBitRate(128 * 1024); mSettings.setPreviewSampleRate(48000); mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); - mSettings.setPort(MIME_TYPE_MJPEG, 11000 + mFacing.mValue); - mSettings.setPreviewSize(MIME_TYPE_MJPEG, options.getDefaultPreviewSize()); - mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); - - mSettings.setPort(MIME_TYPE_RTSP, 12000 + mFacing.mValue); - mSettings.setPreviewSize(MIME_TYPE_RTSP, options.getDefaultPreviewSize()); - mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); - mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); - - mSettings.setPort(MIME_TYPE_SRT, 13000 + mFacing.mValue); - mSettings.setPreviewSize(MIME_TYPE_SRT, options.getDefaultPreviewSize()); - mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); - mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); - - mSettings.setPreviewSize(MIME_TYPE_RTMP, options.getDefaultPreviewSize()); - mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); - mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); - +// mSettings.setPort(MIME_TYPE_MJPEG, 11000 + mFacing.mValue); +// mSettings.setPreviewSize(MIME_TYPE_MJPEG, options.getDefaultPreviewSize()); +// mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); +// +// mSettings.setPort(MIME_TYPE_RTSP, 12000 + mFacing.mValue); +// mSettings.setPreviewSize(MIME_TYPE_RTSP, options.getDefaultPreviewSize()); +// mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); +// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); +// +// mSettings.setPort(MIME_TYPE_SRT, 13000 + mFacing.mValue); +// mSettings.setPreviewSize(MIME_TYPE_SRT, options.getDefaultPreviewSize()); +// mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); +// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); +// +// mSettings.setPreviewSize(MIME_TYPE_RTMP, options.getDefaultPreviewSize()); +// mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); +// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); + + // 各サーバ設定 mSettings.addPreviewServer(getId() + "-mjpeg"); StreamingSettings mjpeg = mSettings.getPreviewServer(getId() + "-mjpeg"); + mjpeg.setName("MJPEG"); mjpeg.setMimeType(MIME_TYPE_MJPEG); mjpeg.setPort(11000 + mFacing.mValue); mjpeg.setPreviewSize(options.getDefaultPreviewSize()); @@ -222,6 +225,7 @@ private void initSupportedSettings() { mSettings.addPreviewServer(getId() + "-rtsp"); StreamingSettings rtsp = mSettings.getPreviewServer(getId() + "-rtsp"); + rtsp.setName("RTSP"); rtsp.setMimeType(MIME_TYPE_RTSP); rtsp.setPort(12000 + mFacing.mValue); rtsp.setPreviewSize(options.getDefaultPreviewSize()); @@ -231,6 +235,7 @@ private void initSupportedSettings() { mSettings.addPreviewServer(getId() + "-srt"); StreamingSettings srt = mSettings.getPreviewServer(getId() + "-srt"); + srt.setName("SRT"); srt.setMimeType(MIME_TYPE_SRT); srt.setPort(13000 + mFacing.mValue); srt.setPreviewSize(options.getDefaultPreviewSize()); @@ -240,6 +245,7 @@ private void initSupportedSettings() { mSettings.addBroadcaster(getId() + "-rtmp"); StreamingSettings rtmp = mSettings.getBroadcaster(getId() + "-rtmp"); + rtmp.setName("RTMP"); rtmp.setMimeType(MIME_TYPE_RTMP); rtmp.setPreviewSize(options.getDefaultPreviewSize()); rtmp.setPreviewBitRate(2 * 1024 * 1024); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java index 54e3ea26c2..c705a7c8b5 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java @@ -6,15 +6,14 @@ public class Camera2SRTBroadcaster extends AbstractSRTBroadcaster { - public Camera2SRTBroadcaster(Camera2Recorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + public Camera2SRTBroadcaster(Camera2Recorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } @Override protected VideoEncoder createVideoEncoder() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName()) { + switch (getStreamingSettings().getPreviewEncoderName()) { case H264: default: return new CameraVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java index 3195ed0a11..1f5459be36 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java @@ -3,20 +3,18 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class Camera2SRTPreviewServer extends AbstractSRTPreviewServer { Camera2SRTPreviewServer(final Context context, final Camera2Recorder recorder) { super(context, recorder); - setPort(recorder.getSettings().getPort(getMimeType())); + setPort(getStreamingSettings().getPort()); } @Override protected VideoEncoder createVideoEncoder() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName(getMimeType())) { + switch (getStreamingSettings().getPreviewEncoderName()) { case H264: default: return new CameraVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java index c8c96c8392..cf4ca573d0 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraVideoEncoder.java @@ -1,8 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; -import android.graphics.Rect; - -import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; import org.deviceconnect.android.libmedia.streaming.video.CameraVideoQuality; import org.deviceconnect.android.libmedia.streaming.video.SurfaceVideoEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; @@ -31,30 +28,4 @@ public CameraVideoEncoder(Camera2Recorder recorder, String mimeType) { public VideoQuality getVideoQuality() { return mVideoQuality; } - - private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(new Rect(rect)); - } - }; - - private MovingRectThread mMovingRectThread; - - public void startMovingRectThread() { - if (mMovingRectThread != null) { - return; - } - - mMovingRectThread = new MovingRectThread(); - mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); - mMovingRectThread.start(); - } - - public void stopMovingRectThread() { - if (mMovingRectThread != null) { - mMovingRectThread.stop(); - mMovingRectThread = null; - } - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java index 140084d553..c45cdc9deb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java @@ -4,6 +4,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractBroadcastProvider; import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class ScreenCastBroadcasterProvider extends AbstractBroadcastProvider { @@ -16,10 +17,18 @@ public ScreenCastBroadcasterProvider(Context context, ScreenCastRecorder recorde @Override public Broadcaster createBroadcaster(String broadcastURI) { + String name = null; + for (String n : mRecorder.getSettings().getBroadcasterList()) { + HostMediaRecorder.StreamingSettings s = mRecorder.getSettings().getBroadcaster(n); + if (broadcastURI.equals(s.getBroadcastURI())) { + name = n; + } + } + if (broadcastURI.startsWith("srt://")) { - return new ScreenCastSRTBroadcaster(mRecorder, broadcastURI); + return new ScreenCastSRTBroadcaster(mRecorder, broadcastURI, name); } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) { - return new ScreenCastRTMPBroadcaster(mRecorder, broadcastURI); + return new ScreenCastRTMPBroadcaster(mRecorder, broadcastURI, name); } else { return null; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java index 70ee087991..15e7261c56 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java @@ -12,7 +12,7 @@ class ScreenCastMJPEGPreviewServer extends AbstractMJPEGPreviewServer { ScreenCastMJPEGPreviewServer(Context context, ScreenCastRecorder recorder, boolean useSSL) { super(context, recorder, useSSL); - setPort(recorder.getSettings().getPort(getMimeType()) + (useSSL ? 1 : 0)); + setPort(getStreamingSettings().getPort() + (useSSL ? 1 : 0)); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java index 3a7a3b3f27..4192362592 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java @@ -4,8 +4,8 @@ import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class ScreenCastRTMPBroadcaster extends AbstractRTMPBroadcaster { - public ScreenCastRTMPBroadcaster(ScreenCastRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + public ScreenCastRTMPBroadcaster(ScreenCastRecorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java index 03311486b7..369627494a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java @@ -13,14 +13,13 @@ class ScreenCastRTSPPreviewServer extends AbstractRTSPPreviewServer { ScreenCastRTSPPreviewServer(Context context, ScreenCastRecorder recorder) { super(context, recorder); - setPort(recorder.getSettings().getPort(getMimeType())); + setPort(getStreamingSettings().getPort()); } @Override protected VideoStream createVideoStream() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName(getMimeType())) { + switch (getStreamingSettings().getPreviewEncoderName()) { case H264: default: return new ScreenCastH264VideoStream(recorder, 5006); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index 513374b028..b95c95bc82 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -129,10 +129,10 @@ private void initSupportedSettings() { if (!mSettings.isInitialized()) { mSettings.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); mSettings.setPictureSize(mSettings.getSupportedPictureSizes().get(0)); - mSettings.setPreviewBitRate(2 * 1024 * 1024); +// mSettings.setPreviewBitRate(2 * 1024 * 1024); mSettings.setPreviewMaxFrameRate(30); - mSettings.setPreviewKeyFrameInterval(1); - mSettings.setPreviewQuality(80); +// mSettings.setPreviewKeyFrameInterval(1); +// mSettings.setPreviewQuality(80); mSettings.setPreviewAudioSource(null); mSettings.setPreviewAudioBitRate(128 * 1024); @@ -140,30 +140,31 @@ private void initSupportedSettings() { mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); - mSettings.setPort(MIME_TYPE_MJPEG, 21000); - mSettings.setPreviewSize(MIME_TYPE_MJPEG, mSettings.getSupportedPreviewSizes().get(0)); - mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); - - mSettings.setPort(MIME_TYPE_RTSP, 22000); - mSettings.setPreviewSize(MIME_TYPE_RTSP, mSettings.getSupportedPreviewSizes().get(0)); - mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); - mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); - - mSettings.setPort(MIME_TYPE_SRT, 23000); - mSettings.setPreviewSize(MIME_TYPE_SRT, mSettings.getSupportedPreviewSizes().get(0)); - mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); - mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); - - mSettings.setPreviewSize(MIME_TYPE_RTMP, mSettings.getSupportedPreviewSizes().get(0)); - mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); - mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); +// mSettings.setPort(MIME_TYPE_MJPEG, 21000); +// mSettings.setPreviewSize(MIME_TYPE_MJPEG, mSettings.getSupportedPreviewSizes().get(0)); +// mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); +// +// mSettings.setPort(MIME_TYPE_RTSP, 22000); +// mSettings.setPreviewSize(MIME_TYPE_RTSP, mSettings.getSupportedPreviewSizes().get(0)); +// mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); +// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); +// +// mSettings.setPort(MIME_TYPE_SRT, 23000); +// mSettings.setPreviewSize(MIME_TYPE_SRT, mSettings.getSupportedPreviewSizes().get(0)); +// mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); +// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); +// +// mSettings.setPreviewSize(MIME_TYPE_RTMP, mSettings.getSupportedPreviewSizes().get(0)); +// mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); +// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); +// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); mSettings.addPreviewServer(getId() + "-mjpeg"); StreamingSettings mjpeg = mSettings.getPreviewServer(getId() + "-mjpeg"); + mjpeg.setName("MJPEG"); mjpeg.setMimeType(MIME_TYPE_MJPEG); mjpeg.setPort(21000); mjpeg.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); @@ -172,6 +173,7 @@ private void initSupportedSettings() { mSettings.addPreviewServer(getId() + "-rtsp"); StreamingSettings rtsp = mSettings.getPreviewServer(getId() + "-rtsp"); + rtsp.setName("RTSP"); rtsp.setMimeType(MIME_TYPE_RTSP); rtsp.setPort(22000); rtsp.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); @@ -181,6 +183,7 @@ private void initSupportedSettings() { mSettings.addPreviewServer(getId() + "-srt"); StreamingSettings srt = mSettings.getPreviewServer(getId() + "-srt"); + srt.setName("SRT"); srt.setMimeType(MIME_TYPE_SRT); srt.setPort(23000); srt.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); @@ -190,6 +193,7 @@ private void initSupportedSettings() { mSettings.addBroadcaster(getId() + "-rtmp"); StreamingSettings rtmp = mSettings.getBroadcaster(getId() + "-rtmp"); + rtmp.setName("RTMP"); rtmp.setMimeType(MIME_TYPE_RTMP); rtmp.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); rtmp.setPreviewBitRate(2 * 1024 * 1024); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java index 316ff11461..ccb5762dc3 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java @@ -6,15 +6,14 @@ public class ScreenCastSRTBroadcaster extends AbstractSRTBroadcaster { - public ScreenCastSRTBroadcaster(ScreenCastRecorder recorder, String broadcastURI) { - super(recorder, broadcastURI); + public ScreenCastSRTBroadcaster(ScreenCastRecorder recorder, String broadcastURI, String name) { + super(recorder, broadcastURI, name); } @Override protected VideoEncoder createVideoEncoder() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName(getMimeType())) { + switch (getStreamingSettings().getPreviewEncoderName()) { case H264: default: return new ScreenCastVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java index d7d4a45c1b..49976367fa 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java @@ -20,14 +20,13 @@ class ScreenCastSRTPreviewServer extends AbstractSRTPreviewServer { ScreenCastSRTPreviewServer(final Context context, final ScreenCastRecorder recorder) { super(context, recorder); - setPort(recorder.getSettings().getPort(getMimeType())); + setPort(getStreamingSettings().getPort()); } @Override protected VideoEncoder createVideoEncoder() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - switch (settings.getPreviewEncoderName(getMimeType())) { + switch (getStreamingSettings().getPreviewEncoderName()) { case H264: default: return new ScreenCastVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/SurfaceScreenCast.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/SurfaceScreenCast.java index 291c43d3dd..8f1c8c0cb2 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/SurfaceScreenCast.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/SurfaceScreenCast.java @@ -9,7 +9,7 @@ @TargetApi(Build.VERSION_CODES.LOLLIPOP) class SurfaceScreenCast extends AbstractScreenCast { - private Surface mOutputSurface; + private final Surface mOutputSurface; SurfaceScreenCast(Context context, MediaProjection mediaProjection, Surface outputSurface, int width, int height) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java index b06c16e0da..cb62c2bdbb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java @@ -10,10 +10,18 @@ public class MovingRectThread { private WorkThread mWorkThread; private int mFrameRate = 30; + /** + * スレッドを動作させるフレームレートを設定します. + * + * @param frameRate フレームレート + */ public void setFrameRate(int frameRate) { if (frameRate <= 0) { throw new IllegalArgumentException("frameRate is negative or zero."); } + if (frameRate >= 1000) { + throw new IllegalArgumentException("frameRate is over 1000."); + } mFrameRate = frameRate; } @@ -118,7 +126,11 @@ public interface OnEventListener { private void postOnMoved(Rect rect) { for (OnEventListener cb : mOnEventListeners) { - cb.onMoved(rect); + try { + cb.onMoved(rect); + } catch (Exception e) { + // ignore. + } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml index 45fb851892..3d7de267b7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml @@ -23,21 +23,6 @@ android:title="@string/host_recorder_settings_server" app:iconSpaceReserved="false"> - - - - - - - - \ No newline at end of file diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java index 1d24c976a2..851297ef6c 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceBase.java @@ -16,7 +16,7 @@ public abstract class EGLSurfaceBase { private int mWidth = -1; private int mHeight = -1; private Object mTag; - private Rect mDrawingRange; + private Rect mCropRect; EGLSurfaceBase() { } @@ -90,8 +90,8 @@ public int getHeight() { * * @param rect 描画範囲 */ - public void setDrawingRange(Rect rect) { - mDrawingRange = rect; + public void setCropRect(Rect rect) { + mCropRect = rect; } /** @@ -101,8 +101,8 @@ public void setDrawingRange(Rect rect) { * * @return 描画範囲 */ - public Rect getDrawingRange() { - return mDrawingRange; + public Rect getCropRect() { + return mCropRect; } /** diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java index 937163df88..16e8d5f115 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java @@ -2,6 +2,7 @@ import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.util.Size; import android.view.Surface; import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; @@ -201,7 +202,7 @@ public void addEGLSurfaceBase(Surface surface, Rect drawingRange) { public void addEGLSurfaceBase(Surface surface, Object tag, Rect drawingRange) { EGLSurfaceBase eglSurfaceBase = createEGLSurfaceBase(surface); eglSurfaceBase.setTag(tag); - eglSurfaceBase.setDrawingRange(drawingRange); + eglSurfaceBase.setCropRect(drawingRange); addEGLSurfaceBase(eglSurfaceBase); } @@ -232,7 +233,7 @@ public void addEGLSurfaceBase(int width, int height, Object tag) { public void addEGLSurfaceBase(int width, int height, Object tag, Rect drawingRange) { EGLSurfaceBase eglSurfaceBase = createEGLSurfaceBase(width, height); eglSurfaceBase.setTag(tag); - eglSurfaceBase.setDrawingRange(drawingRange); + eglSurfaceBase.setCropRect(drawingRange); addEGLSurfaceBase(eglSurfaceBase); } @@ -471,7 +472,11 @@ protected void postOnError(Exception e) { private void postOnDrawn(EGLSurfaceBase eglSurfaceBase) { for (OnDrawingEventListener l : mOnDrawingEventListeners) { - l.onDrawn(eglSurfaceBase); + try { + l.onDrawn(eglSurfaceBase); + } catch (Exception e) { + // ignore. + } } } @@ -549,13 +554,37 @@ public void run() { synchronized (mEGLSurfaceBases) { for (EGLSurfaceBase eglSurfaceBase : mEGLSurfaceBases) { eglSurfaceBase.makeCurrent(); - Rect drawingRange = eglSurfaceBase.getDrawingRange(); - if (drawingRange != null) { - mStManager.setDrawingRange(drawingRange, mWidth, mHeight); + int viewportX = 0; + int viewportY = 0; + int viewportW = eglSurfaceBase.getWidth(); + int viewportH = eglSurfaceBase.getHeight(); + Rect cropRect = eglSurfaceBase.getCropRect(); + if (cropRect != null) { + mStManager.setCropRect(cropRect, mWidth, mHeight); + + // 出力先のアスペクト比に合わせて計算を行う + Size size = calculateViewSize(cropRect.width(), cropRect.height(), viewportW, viewportH); + if (viewportW > size.getWidth()) { + viewportX = (viewportW - size.getWidth()) / 2; + viewportW = size.getWidth(); + } else if (viewportH > size.getHeight()) { + viewportY = (viewportH - size.getHeight()) / 2; + viewportH = size.getHeight(); + } } else { - mStManager.clearDrawingRange(); + mStManager.clearCropRect(); + + // 出力先のアスペクト比に合わせて計算を行う + Size size = calculateViewSize(mWidth, mHeight, viewportW, viewportH); + if (viewportW > size.getWidth()) { + viewportX = (viewportW - size.getWidth()) / 2; + viewportW = size.getWidth(); + } else if (viewportH > size.getHeight()) { + viewportY = (viewportH - size.getHeight()) / 2; + viewportH = size.getHeight(); + } } - mStManager.setViewport(0, 0, eglSurfaceBase.getWidth(), eglSurfaceBase.getHeight()); + mStManager.setViewport(viewportX, viewportY,viewportW, viewportH); mStManager.drawImage(getDisplayRotation()); eglSurfaceBase.setPresentationTime(st.getTimestamp()); eglSurfaceBase.swapBuffers(); @@ -598,6 +627,27 @@ public void run() { } } + /** + * 指定された View のサイズにフィットするサイズを計算します. + * + * @param width 横幅 + * @param height 縦幅 + * @param viewWidth View のサイズ + * @param viewHeight View のサイズ + * @return View にフィットするサイズ + */ + private Size calculateViewSize(int width, int height, int viewWidth, int viewHeight) { + int h = (int) (height * (viewWidth / (float) width)); + if (viewHeight < h) { + int w = (int) (width * (viewHeight / (float) height)); + if (w % 2 != 0) { + w--; + } + return new Size(w, viewHeight); + } + return new Size(viewWidth, h); + } + /** * 描画イベントを通知するリスナー. */ diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java index c3c2000ab7..1d1bc33218 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureManager.java @@ -145,8 +145,8 @@ public void setViewport(int x, int y, int width, int height) { * @param width 映像の横幅 * @param height 映像の縦幅 */ - public void setDrawingRange(int l, int t, int r, int b, int width, int height) { - mTextureRenderer.setDrawingRange(l, t, r, b, width, height); + public void setCropRect(int l, int t, int r, int b, int width, int height) { + mTextureRenderer.setCropRect(l, t, r, b, width, height); } /** @@ -156,15 +156,15 @@ public void setDrawingRange(int l, int t, int r, int b, int width, int height) { * @param width 映像の横幅 * @param height 映像の縦幅 */ - public void setDrawingRange(Rect rect, int width, int height) { - mTextureRenderer.setDrawingRange(rect, width, height); + public void setCropRect(Rect rect, int width, int height) { + mTextureRenderer.setCropRect(rect, width, height); } /** * 描画範囲を解除します. */ - public void clearDrawingRange() { - mTextureRenderer.clearDrawingRange(); + public void clearCropRect() { + mTextureRenderer.clearCropRect(); } /** diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java index ef44c6b3c8..3f5d19b35d 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java @@ -39,7 +39,7 @@ public class SurfaceTextureRenderer { }; private final FloatBuffer mTriangleVertices; - private boolean mInverse; + private final boolean mInverse; private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + @@ -225,7 +225,7 @@ public void drawFrame(SurfaceTexture st, int displayRotation) { /** * 描画範囲を削除します. */ - public void clearDrawingRange() { + public void clearCropRect() { mTriangleVertices.clear(); if (mInverse) { mTriangleVertices.put(TRIANGLE_VERTICES_DATA_2).position(0); @@ -241,8 +241,8 @@ public void clearDrawingRange() { * @param width 描画元の横幅 * @param height 描画元の縦幅 */ - public void setDrawingRange(Rect rect, int width, int height) { - setDrawingRange(rect.left, rect.top, rect.right, rect.bottom, width, height); + public void setCropRect(Rect rect, int width, int height) { + setCropRect(rect.left, rect.top, rect.right, rect.bottom, width, height); } /** @@ -255,15 +255,15 @@ public void setDrawingRange(Rect rect, int width, int height) { * @param width 画像の横幅 * @param height 画像の縦幅 */ - public void setDrawingRange(int left, int top, int right, int bottom, int width, int height) { + public void setCropRect(int left, int top, int right, int bottom, int width, int height) { float l = left / (float) width; float t = top / (float) height; float r = right / (float) width; float b = bottom / (float) height; - setDrawingRange(l, t, r, b); + setCropRect(l, t, r, b); } - private void setDrawingRange(float l, float t, float r, float b) { + private void setCropRect(float l, float t, float r, float b) { // 映像がはみ出した分は縮めて描画するように座標を計算 float sx = -1.0f; float sy = 1.0f; diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java index b9d80db909..27ad190ffc 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/SurfaceVideoEncoder.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.libmedia.streaming.video; -import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.media.MediaCodecInfo; import android.view.Surface; @@ -59,7 +58,7 @@ public void onDrawn(EGLSurfaceBase eglSurfaceBase) { if (mEGLSurfaceBase == eglSurfaceBase) { VideoQuality videoQuality = getVideoQuality(); if (videoQuality != null) { - eglSurfaceBase.setDrawingRange(videoQuality.getCropRect()); + eglSurfaceBase.setCropRect(videoQuality.getCropRect()); } onDrawnSurface(); } From 3b41f6710e901f9fca716e1957142e60dfffd794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Fri, 10 Sep 2021 12:21:18 +0900 Subject: [PATCH 17/40] =?UTF-8?q?=E3=83=93=E3=83=83=E3=83=88=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E6=83=85=E5=A0=B1=E3=82=92=E6=99=82?= =?UTF-8?q?=E5=88=BB=E3=81=A7=E7=AF=84=E5=9B=B2=E6=8C=87=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connection/HostConnectionManager.java | 2 +- .../host/profile/HostConnectionProfile.java | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java index 314e02f818..55f15dede9 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/connection/HostConnectionManager.java @@ -295,7 +295,7 @@ public NetworkCaps getNetworkCaps() { * ネットワークの情報を格納するクラス. */ public class NetworkCaps { - private NetworkType mType; + private NetworkType mType = NetworkType.TYPE_NONE; private int mUpstreamBW; private int mDownstreamBW; private int mStrengthLevel; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java index 2e3f5196f4..b5afc62129 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostConnectionProfile.java @@ -29,6 +29,7 @@ import org.deviceconnect.message.intent.message.IntentDConnectMessage; import java.text.DateFormat; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -394,6 +395,27 @@ public String getAttribute() { @Override public boolean onRequest(final Intent request, final Intent response) { + String sinceString = request.getStringExtra("since"); + String untilString = request.getStringExtra("until"); + Long since = null; + Long until = null; + + if (sinceString != null) { + try { + since = df.parse(sinceString).getTime(); + } catch (ParseException e) { + // ignore. + } + } + + if (untilString != null) { + try { + until = df.parse(untilString).getTime(); + } catch (ParseException e) { + // ignore. + } + } + if (!HostConnectionManager.checkUsageAccessSettings(getContext())) { HostConnectionManager.openUsageAccessSettings(getContext()); @@ -421,6 +443,18 @@ public boolean onRequest(final Intent request, final Intent response) { List trafficList = mHostConnectionManager.getTrafficList(networkType); ArrayList trafficArray = new ArrayList<>(); for (HostTraffic traffic : trafficList) { + if (until != null) { + if (until < traffic.getStartTime()) { + continue; + } + } + + if (since != null) { + if (traffic.getEndTime() < since) { + continue; + } + } + trafficArray.add(createNetworkBitrate(traffic)); } response.putExtra(convertNetworkTypeToString(networkType), trafficArray); From 3d4a2823c4896816346ee08ed77187088bc350a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Sat, 11 Sep 2021 14:25:46 +0900 Subject: [PATCH 18/40] =?UTF-8?q?Host=E3=83=97=E3=83=A9=E3=82=B0=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E3=83=AC=E3=82=B3=E3=83=BC=E3=83=80=E3=81=AE?= =?UTF-8?q?=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92?= =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/SettingsBaseFragment.java | 4 +- .../settings/SettingsBroadcastFragment.java | 32 +- .../settings/SettingsMJPEGFragment.java | 12 +- .../settings/SettingsMainFragment.java | 76 +- .../settings/SettingsRTSPFragment.java | 32 +- .../settings/SettingsSRTFragment.java | 32 +- .../settings/SettingsVideoFragment.java | 49 + .../profile/HostLiveStreamingProfile.java | 102 +- .../HostMediaStreamingRecordingProfile.java | 2534 ++++++++++------- .../recorder/AbstractBroadcastProvider.java | 211 +- .../host/recorder/AbstractBroadcaster.java | 320 +-- .../host/recorder/AbstractLiveStreaming.java | 412 +++ .../AbstractLiveStreamingProvider.java | 222 ++ .../recorder/AbstractMJPEGPreviewServer.java | 29 +- .../host/recorder/AbstractMediaRecorder.java | 165 +- .../host/recorder/AbstractPreviewServer.java | 405 +-- .../AbstractPreviewServerProvider.java | 216 +- .../recorder/AbstractRTMPBroadcaster.java | 46 +- .../recorder/AbstractRTSPPreviewServer.java | 60 +- .../host/recorder/AbstractSRTBroadcaster.java | 44 +- .../recorder/AbstractSRTPreviewServer.java | 59 +- .../host/recorder/Broadcaster.java | 104 +- .../host/recorder/BroadcasterProvider.java | 70 +- .../host/recorder/CropInterface.java | 8 + .../host/recorder/HostMediaRecorder.java | 349 ++- .../recorder/HostMediaRecorderManager.java | 32 +- .../host/recorder/LiveStreaming.java | 148 + .../host/recorder/LiveStreamingProvider.java | 109 + .../host/recorder/PreviewServer.java | 122 +- .../host/recorder/PreviewServerProvider.java | 97 +- .../audio/AudioBroadcasterProvider.java | 24 +- .../host/recorder/audio/AudioConst.java | 73 - .../recorder/audio/AudioPreviewServer.java | 46 - .../audio/AudioPreviewServerProvider.java | 13 +- .../recorder/audio/AudioRTMPBroadcaster.java | 5 +- .../audio/AudioRTSPPreviewServer.java | 7 +- .../recorder/audio/AudioSRTBroadcaster.java | 4 +- .../recorder/audio/AudioSRTPreviewServer.java | 7 +- .../recorder/audio/HostAudioRecorder.java | 38 +- .../camera/Camera2BroadcasterProvider.java | 27 +- .../camera/Camera2MJPEGPreviewServer.java | 8 +- .../camera/Camera2PreviewServerProvider.java | 39 +- .../camera/Camera2RTMPBroadcaster.java | 4 +- .../camera/Camera2RTSPPreviewServer.java | 8 +- .../host/recorder/camera/Camera2Recorder.java | 76 +- .../camera/Camera2SRTBroadcaster.java | 7 +- .../camera/Camera2SRTPreviewServer.java | 9 +- .../camera/CameraSurfaceDrawingThread.java | 2 +- .../screen/ScreenCastBroadcasterProvider.java | 24 +- .../screen/ScreenCastMJPEGPreviewServer.java | 6 +- .../ScreenCastPreviewServerProvider.java | 25 +- .../screen/ScreenCastRTMPBroadcaster.java | 4 +- .../screen/ScreenCastRTSPPreviewServer.java | 9 +- .../recorder/screen/ScreenCastRecorder.java | 71 +- .../screen/ScreenCastSRTBroadcaster.java | 7 +- .../screen/ScreenCastSRTPreviewServer.java | 8 +- .../host/recorder/ui/PreviewSurfaceView.java | 43 +- .../host/recorder/util/CapabilityUtil.java | 10 + .../util/MediaProjectionProvider.java | 6 +- .../host/recorder/util/MovingRectThread.java | 6 +- .../src/main/res/layout/item_crop_frame.xml | 21 + .../res/xml/settings_host_recorder_main.xml | 2 + .../res/xml/settings_host_recorder_video.xml | 6 + .../libmedia/build.gradle | 2 +- .../gles/EGLSurfaceDrawingThread.java | 60 +- .../streaming/video/CameraVideoQuality.java | 29 + .../streaming/video/VideoEncoder.java | 28 + .../streaming/video/VideoQuality.java | 68 +- 68 files changed, 3597 insertions(+), 3336 deletions(-) create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java delete mode 100755 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioConst.java delete mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/item_crop_frame.xml diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java index 0eaf2f5581..6a3e216cd9 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBaseFragment.java @@ -55,10 +55,10 @@ public String getRecorderId() { * * @return 設定のファイル名 */ - public String getSettingName() { + public String getEncoderId() { Bundle args = getArguments(); if (args != null) { - return args.getString("setting_name"); + return args.getString("encoder_id"); } return null; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java index baf1c83f35..bc940fbcd6 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java @@ -27,7 +27,7 @@ public class SettingsBroadcastFragment extends SettingsParameterFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getSettingName()); + getPreferenceManager().setSharedPreferencesName(getEncoderId()); setPreferencesFromResource(R.xml.settings_host_recorder_broadcast, rootKey); } @@ -35,7 +35,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); setPreviewSizePreference(settings); setPreviewVideoEncoderPreference(settings); @@ -54,9 +54,9 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } - private HostMediaRecorder.StreamingSettings getStreamingSetting() { + private HostMediaRecorder.EncoderSettings getStreamingSetting() { HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getBroadcaster(getSettingName()); + return s.getEncoderSetting(getEncoderId()); } /** @@ -101,7 +101,7 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { List previewSizes = getSupportedPreviewSizes(); @@ -131,7 +131,7 @@ private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settin * * @param settings レコーダ設定 */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewVideoEncoderPreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("preview_encoder"); if (pref != null) { List list = settings.getSupportedVideoEncoders(); @@ -151,19 +151,19 @@ private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSetting * エンコーダのプロファイルとレベルを設定します. * * @param settings レコーダ設定 - * @param encoderName エンコーダ + * @param videoCodec エンコーダ * @param reset リセットフラグ */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSettings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + private void setPreviewProfileLevelPreference(HostMediaRecorder.EncoderSettings settings, HostMediaRecorder.VideoCodec videoCodec, boolean reset) { ListPreference pref = findPreference("preview_profile_level"); if (pref != null) { - List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + List list = CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType()); if (!list.isEmpty()) { List entryValues = new ArrayList<>(); entryValues.add("none"); for (HostMediaRecorder.ProfileLevel pl : list) { - String value = getProfileLevel(encoderName, pl); + String value = getProfileLevel(videoCodec, pl); if (value != null) { entryValues.add(value); } @@ -178,7 +178,7 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSetting } else { HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); if (pl != null) { - pref.setValue(getProfileLevel(encoderName, pl)); + pref.setValue(getProfileLevel(videoCodec, pl)); } } @@ -253,7 +253,7 @@ private Size getSizeFromValue(String value) { * @param pl プロファイルとレベル * @return 文字列 */ - private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { + private String getProfileLevel(HostMediaRecorder.VideoCodec encoderName, HostMediaRecorder.ProfileLevel pl) { switch (encoderName) { case H264: { H264Profile p = H264Profile.valueOf(pl.getProfile()); @@ -282,7 +282,7 @@ private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, H * @param value 変換する文字列 * @return プロファイルとレベル */ - private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoCodec encoderName, String value) { String[] t = value.split("-"); if (t.length == 2) { try { @@ -339,7 +339,7 @@ private Integer getCropRect(String key) { return false; } - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { @@ -354,8 +354,8 @@ private Integer getCropRect(String key) { } catch (Exception e) { return false; } - HostMediaRecorder.VideoEncoderName encoderName = - HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); + HostMediaRecorder.VideoCodec encoderName = + HostMediaRecorder.VideoCodec.nameOf((String) newValue); setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { try { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java index a245ad0585..ec8ad7eb12 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -24,7 +24,7 @@ public class SettingsMJPEGFragment extends SettingsParameterFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getSettingName()); + getPreferenceManager().setSharedPreferencesName(getEncoderId()); setPreferencesFromResource(R.xml.settings_host_recorder_mjpeg, rootKey); } @@ -32,7 +32,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); setPreviewServerPort(); setPreviewServerUrl(settings.getPort()); @@ -49,9 +49,9 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } - private HostMediaRecorder.StreamingSettings getStreamingSetting() { + private HostMediaRecorder.EncoderSettings getStreamingSetting() { HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getPreviewServer(getSettingName()); + return s.getEncoderSetting(getEncoderId()); } private void setPreviewServerPort() { @@ -129,7 +129,7 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { List previewSizes = getSupportedPreviewSizes(); @@ -239,7 +239,7 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java index 3c11ef61b3..5178a4dba4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java @@ -1,7 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; import android.os.Bundle; -import android.util.Log; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; @@ -33,24 +32,24 @@ public boolean onPreferenceTreeClick(final Preference preference) { findNavController(this).navigate(R.id.action_main_to_audio); } else { Bundle params = new Bundle(); - params.putString("setting_name", preference.getKey()); - + params.putString("encoder_id", preference.getKey()); HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - HostMediaRecorder.StreamingSettings s = settings.getPreviewServer(preference.getKey()); + HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(preference.getKey()); if (s != null) { - String mimeType = s.getMimeType(); - if ("video/x-mjpeg".equalsIgnoreCase(mimeType)) { - findNavController(this).navigate(R.id.action_main_to_mjpeg, params); - } else if ("video/x-rtp".equalsIgnoreCase(mimeType)) { - findNavController(this).navigate(R.id.action_main_to_rtsp, params); - } else if ("video/MP2T".equalsIgnoreCase(mimeType)) { - findNavController(this).navigate(R.id.action_main_to_srt, params); - } - } else { - s = settings.getBroadcaster(preference.getKey()); - if (s != null) { - findNavController(this).navigate(R.id.action_main_to_broadcast, params); + switch (s.getMimeType()) { + case MJPEG: + findNavController(this).navigate(R.id.action_main_to_mjpeg, params); + break; + case RTSP: + findNavController(this).navigate(R.id.action_main_to_rtsp, params); + break; + case SRT: + findNavController(this).navigate(R.id.action_main_to_srt, params); + break; + case RTMP: + findNavController(this).navigate(R.id.action_main_to_broadcast, params); + break; } } } @@ -59,48 +58,37 @@ public boolean onPreferenceTreeClick(final Preference preference) { @Override public void onBindService() { - addPreviewServerList(); - addBroadcasterList(); + addEncoderList(); } private boolean isNotExistPreference(String key) { return findPreference(key) == null; } - private void addPreviewServerList() { + private void addEncoderList() { HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); - List previewServerList = settings.getPreviewServerList(); + List encoderList = settings.getEncoderIdList(); - PreferenceCategory preferenceCategory = findPreference("recorder_settings_preview_server"); - for (String previewServerId : previewServerList) { - HostMediaRecorder.StreamingSettings s = settings.getPreviewServer(previewServerId); - if (isNotExistPreference(previewServerId)) { + PreferenceCategory previewCategory = findPreference("recorder_settings_preview_server"); + PreferenceCategory broadcasterCategory = findPreference("recorder_settings_broadcaster"); + for (String encoderId : encoderList) { + HostMediaRecorder.EncoderSettings encoderSetting = settings.getEncoderSetting(encoderId); + if (isNotExistPreference(encoderId)) { Preference preference = new Preference(requireContext()); - preference.setTitle(s.getName()); - preference.setKey(previewServerId); + preference.setTitle(encoderSetting.getName()); + preference.setKey(encoderId); preference.setIconSpaceReserved(false); - preferenceCategory.addPreference(preference); + if (encoderSetting.getMimeType() == HostMediaRecorder.MimeType.RTMP) { + broadcasterCategory.addPreference(preference); + } else { + previewCategory.addPreference(preference); + } } } - } - private void addBroadcasterList() { - HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - List broadcasterList = settings.getBroadcasterList(); - - PreferenceCategory preferenceCategory = findPreference("recorder_settings_broadcaster"); - for (String broadcasterId : broadcasterList) { - HostMediaRecorder.StreamingSettings s = settings.getBroadcaster(broadcasterId); - if (isNotExistPreference(broadcasterId)) { - Preference preference = new Preference(requireContext()); - preference.setTitle(s.getName()); - preference.setKey(broadcasterId); - preference.setIconSpaceReserved(false); - preferenceCategory.addPreference(preference); - } - } + previewCategory.setVisible(true); + broadcasterCategory.setVisible(true); } private void startManager() { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java index e6f4b7a277..23864e790f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -28,7 +28,7 @@ public class SettingsRTSPFragment extends SettingsParameterFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getSettingName()); + getPreferenceManager().setSharedPreferencesName(getEncoderId()); setPreferencesFromResource(R.xml.settings_host_recorder_rtsp, rootKey); } @@ -36,7 +36,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); setPreviewServerPort(); setPreviewServerUrl(settings.getPort()); @@ -57,9 +57,9 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } - private HostMediaRecorder.StreamingSettings getStreamingSetting() { + private HostMediaRecorder.EncoderSettings getStreamingSetting() { HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getPreviewServer(getSettingName()); + return s.getEncoderSetting(getEncoderId()); } private void setPreviewServerPort() { @@ -120,7 +120,7 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { List previewSizes = getSupportedPreviewSizes(); @@ -150,7 +150,7 @@ private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settin * * @param settings レコーダ設定 */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewVideoEncoderPreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("preview_encoder"); if (pref != null) { List list = settings.getSupportedVideoEncoders(); @@ -170,19 +170,19 @@ private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSetting * エンコーダのプロファイルとレベルを設定します. * * @param settings レコーダ設定 - * @param encoderName エンコーダ + * @param videoCodec エンコーダ * @param reset リセットフラグ */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSettings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + private void setPreviewProfileLevelPreference(HostMediaRecorder.EncoderSettings settings, HostMediaRecorder.VideoCodec videoCodec, boolean reset) { ListPreference pref = findPreference("preview_profile_level"); if (pref != null) { - List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + List list = CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType()); if (!list.isEmpty()) { List entryValues = new ArrayList<>(); entryValues.add("none"); for (HostMediaRecorder.ProfileLevel pl : list) { - String value = getProfileLevel(encoderName, pl); + String value = getProfileLevel(videoCodec, pl); if (value != null) { entryValues.add(value); } @@ -197,7 +197,7 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSetting } else { HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); if (pl != null) { - pref.setValue(getProfileLevel(encoderName, pl)); + pref.setValue(getProfileLevel(videoCodec, pl)); } } @@ -272,7 +272,7 @@ private Size getSizeFromValue(String value) { * @param pl プロファイルとレベル * @return 文字列 */ - private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { + private String getProfileLevel(HostMediaRecorder.VideoCodec encoderName, HostMediaRecorder.ProfileLevel pl) { switch (encoderName) { case H264: { H264Profile p = H264Profile.valueOf(pl.getProfile()); @@ -301,7 +301,7 @@ private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, H * @param value 変換する文字列 * @return プロファイルとレベル */ - private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoCodec encoderName, String value) { String[] t = value.split("-"); if (t.length == 2) { try { @@ -358,7 +358,7 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { @@ -375,8 +375,8 @@ private Integer getDrawingRange(String key) { } catch (Exception e) { return false; } - HostMediaRecorder.VideoEncoderName encoderName = - HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); + HostMediaRecorder.VideoCodec encoderName = + HostMediaRecorder.VideoCodec.nameOf((String) newValue); setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { try { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index c109fa117d..ed23fa49eb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -29,7 +29,7 @@ public class SettingsSRTFragment extends SettingsParameterFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getSettingName()); + getPreferenceManager().setSharedPreferencesName(getEncoderId()); setPreferencesFromResource(R.xml.settings_host_recorder_srt, rootKey); setSummaryOptionAuto(getString(R.string.pref_key_settings_srt_inputbw)); @@ -48,7 +48,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); setPreviewServerPort(); setPreviewServerUrl(settings.getPort()); @@ -69,9 +69,9 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } - private HostMediaRecorder.StreamingSettings getStreamingSetting() { + private HostMediaRecorder.EncoderSettings getStreamingSetting() { HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getPreviewServer(getSettingName()); + return s.getEncoderSetting(getEncoderId()); } private void setSummaryOptionAuto(String name) { @@ -147,7 +147,7 @@ private void setPreviewCutOutReset() { * * @param settings レコーダの設定 */ - private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("camera_preview_size"); if (pref != null) { List previewSizes = getSupportedPreviewSizes(); @@ -177,7 +177,7 @@ private void setPreviewSizePreference(HostMediaRecorder.StreamingSettings settin * * @param settings レコーダ設定 */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSettings settings) { + private void setPreviewVideoEncoderPreference(HostMediaRecorder.EncoderSettings settings) { ListPreference pref = findPreference("preview_encoder"); if (pref != null) { List list = settings.getSupportedVideoEncoders(); @@ -197,19 +197,19 @@ private void setPreviewVideoEncoderPreference(HostMediaRecorder.StreamingSetting * エンコーダのプロファイルとレベルを設定します. * * @param settings レコーダ設定 - * @param encoderName エンコーダ + * @param videoCodec エンコーダ * @param reset リセットフラグ */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSettings settings, HostMediaRecorder.VideoEncoderName encoderName, boolean reset) { + private void setPreviewProfileLevelPreference(HostMediaRecorder.EncoderSettings settings, HostMediaRecorder.VideoCodec videoCodec, boolean reset) { ListPreference pref = findPreference("preview_profile_level"); if (pref != null) { - List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); + List list = CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType()); if (!list.isEmpty()) { List entryValues = new ArrayList<>(); entryValues.add("none"); for (HostMediaRecorder.ProfileLevel pl : list) { - String value = getProfileLevel(encoderName, pl); + String value = getProfileLevel(videoCodec, pl); if (value != null) { entryValues.add(value); } @@ -224,7 +224,7 @@ private void setPreviewProfileLevelPreference(HostMediaRecorder.StreamingSetting } else { HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); if (pl != null) { - pref.setValue(getProfileLevel(encoderName, pl)); + pref.setValue(getProfileLevel(videoCodec, pl)); } } @@ -299,7 +299,7 @@ private Size getSizeFromValue(String value) { * @param pl プロファイルとレベル * @return 文字列 */ - private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, HostMediaRecorder.ProfileLevel pl) { + private String getProfileLevel(HostMediaRecorder.VideoCodec encoderName, HostMediaRecorder.ProfileLevel pl) { switch (encoderName) { case H264: { H264Profile p = H264Profile.valueOf(pl.getProfile()); @@ -328,7 +328,7 @@ private String getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, H * @param value 変換する文字列 * @return プロファイルとレベル */ - private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoEncoderName encoderName, String value) { + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoCodec encoderName, String value) { String[] t = value.split("-"); if (t.length == 2) { try { @@ -385,7 +385,7 @@ private Integer getDrawingRange(String key) { return false; } - HostMediaRecorder.StreamingSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { @@ -402,8 +402,8 @@ private Integer getDrawingRange(String key) { } catch (Exception e) { return false; } - HostMediaRecorder.VideoEncoderName encoderName = - HostMediaRecorder.VideoEncoderName.nameOf((String) newValue); + HostMediaRecorder.VideoCodec encoderName = + HostMediaRecorder.VideoCodec.nameOf((String) newValue); setPreviewProfileLevelPreference(settings, encoderName, true); } else if ("preview_profile_level".equalsIgnoreCase(key)) { try { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java index 32e80fce43..1fd9df4e84 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java @@ -40,6 +40,7 @@ public void onBindService() { setPictureSizePreference(settings); setPreviewSizePreference(settings); + setPreviewFps(settings); setPreviewAutoFocusPreference(settings); setPreviewWhiteBalancePreference(settings); setPreviewWhiteBalanceTemperaturePreference(settings); @@ -117,6 +118,35 @@ private void setPreviewSizePreference(HostMediaRecorder.Settings settings) { } } + private void setPreviewFps(HostMediaRecorder.Settings settings) { + ListPreference pref = findPreference("camera_fps"); + if (pref != null) { + List> fpsList = settings.getSupportedFps(); + if (!fpsList.isEmpty()) { + List entryValues = new ArrayList<>(); + for (Range fps : fpsList) { + entryValues.add(fps.getLower() + "-" + fps.getUpper()); + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + Range previewFps = settings.getPreviewFps(); + if (previewFps != null) { + pref.setValue(previewFps.getLower() + "-" + previewFps.getUpper()); + } +// int previewSize = settings.getPreviewMaxFrameRate(); +// if (previewSize != null) { +// pref.setValue(getValueFromSize(previewSize)); +// } + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + /** * 自動フォーカスモードの設定を行います. * @@ -480,6 +510,20 @@ private Size getSizeFromValue(String value) { return null; } + private Range getRangeFromValue(String value) { + String[] t = value.split("-"); + if (t.length == 2) { + try { + int w = Integer.parseInt(t[0].trim()); + int h = Integer.parseInt(t[1].trim()); + return new Range<>(w, h); + } catch (Exception e) { + return null; + } + } + return null; + } + /** * 設定が変更された時に呼び出されるリスナー. */ @@ -501,6 +545,11 @@ private Size getSizeFromValue(String value) { if (size != null) { settings.setPreviewSize(size); } + } else if ("camera_fps".equals(key)) { + Range fps = getRangeFromValue((String) newValue); + if (fps != null) { + settings.setPreviewFps(fps); + } } return true; }; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java index ce0606f05e..69763f2bb5 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java @@ -3,14 +3,14 @@ import android.content.Intent; import android.os.Bundle; import android.util.Log; +import android.util.Range; import android.util.Size; import org.deviceconnect.android.BuildConfig; -import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; import org.deviceconnect.android.deviceplugin.host.recorder.BroadcasterProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorderManager; -import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServer; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; import org.deviceconnect.android.event.Event; import org.deviceconnect.android.event.EventError; import org.deviceconnect.android.event.EventManager; @@ -22,6 +22,7 @@ import org.deviceconnect.android.profile.api.PutApi; import org.deviceconnect.message.DConnectMessage; +import java.util.ArrayList; import java.util.List; public class HostLiveStreamingProfile extends DConnectProfile { @@ -59,7 +60,7 @@ public void onConfigChanged(HostMediaRecorder recorder) { } @Override - public void onPreviewStarted(HostMediaRecorder recorder, List servers) { + public void onPreviewStarted(HostMediaRecorder recorder, List servers) { } @Override @@ -71,17 +72,21 @@ public void onPreviewError(HostMediaRecorder recorder, Exception e) { } @Override - public void onBroadcasterStarted(HostMediaRecorder recorder, Broadcaster broadcaster) { - postOnStart(broadcaster); + public void onBroadcasterStarted(HostMediaRecorder recorder, List broadcasters) { + if (mHostMediaRecorder == null) { + mHostMediaRecorder = recorder; + } + postOnStart(broadcasters); } @Override - public void onBroadcasterStopped(HostMediaRecorder recorder, Broadcaster broadcaster) { - postOnStop(broadcaster); + public void onBroadcasterStopped(HostMediaRecorder recorder) { + postOnStop(recorder.getBroadcasterProvider().getLiveStreamingList()); + mHostMediaRecorder = null; } @Override - public void onBroadcasterError(HostMediaRecorder recorder, Broadcaster broadcaster, Exception e) { + public void onBroadcasterError(HostMediaRecorder recorder, LiveStreaming broadcaster, Exception e) { postOnError(broadcaster); } @@ -158,16 +163,47 @@ public boolean onRequest(final Intent request, final Intent response) { HostMediaRecorder.Settings settings = recorder.getSettings(); try { + if (broadcastURI != null) { + for (String encoderId : recorder.getSettings().getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings != null && encoderSettings.getMimeType().equals(HostMediaRecorder.MimeType.RTMP)) { + encoderSettings.setBroadcastURI(broadcastURI); + } + } + } + if (width != null && height != null) { settings.setPreviewSize(new Size(width, height)); + + for (String encoderId : recorder.getSettings().getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings != null && encoderSettings.getMimeType().equals(HostMediaRecorder.MimeType.RTMP)) { + encoderSettings.setPreviewSize(new Size(width, height)); + } + } } -// if (bitrate != null) { -// settings.setPreviewBitRate(bitrate); -// } + if (bitrate != null) { + for (String encoderId : recorder.getSettings().getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings != null && encoderSettings.getMimeType().equals(HostMediaRecorder.MimeType.RTMP)) { + encoderSettings.setPreviewBitRate(bitrate); + } + } + } if (frameRate != null) { - settings.setPreviewMaxFrameRate(frameRate); + Range fps = recorder.getSettings().getPreviewFpsFromFrameRate(frameRate); + if (fps != null) { + settings.setPreviewFps(fps); + } + + for (String encoderId : recorder.getSettings().getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings != null && encoderSettings.getMimeType().equals(HostMediaRecorder.MimeType.RTMP)) { + encoderSettings.setPreviewMaxFrameRate(frameRate); + } + } } // 映像が有効な時の音声設定を行う @@ -186,8 +222,8 @@ public boolean onRequest(final Intent request, final Intent response) { public void onAllowed() { mHostMediaRecorder = recorder; - Broadcaster broadcaster = recorder.startBroadcaster(broadcastURI); - if (broadcaster != null) { + List broadcasters = recorder.startBroadcaster(broadcastURI); + if (!broadcasters.isEmpty() ) { setResult(response, DConnectMessage.RESULT_OK); } else { MessageUtils.setUnknownError(response, "Failed to start a live streaming."); @@ -242,18 +278,16 @@ public String getAttribute() { @Override public boolean onRequest(final Intent request, final Intent response) { - Bundle streaming; + List streaming = new ArrayList<>(); if (mHostMediaRecorder != null) { BroadcasterProvider provider = mHostMediaRecorder.getBroadcasterProvider(); - if (provider != null && provider.isRunning()) { - streaming = createStreamingBundle(provider.getBroadcaster(), "streaming"); - } else { - streaming = createStreamingBundle(null, "stop"); + for (LiveStreaming broadcaster : provider.getLiveStreamingList()) { + streaming.add(createStreamingBundle(broadcaster, broadcaster.isRunning() ? "streaming" : "stop")); } } else { - streaming = createStreamingBundle(null, "stop"); + streaming.add(createStreamingBundle(null, "stop")); } - response.putExtra(PARAM_KEY_STREAMING, streaming); + response.putExtra(PARAM_KEY_STREAMING, streaming.toArray(new Bundle[0])); setResult(response, DConnectMessage.RESULT_OK); return true; @@ -396,10 +430,10 @@ private HostMediaRecorder getHostMediaRecorder(String video, String audio) { return mHostMediaRecorderManager.getRecorder(audio); } - private Bundle createStreamingBundle(Broadcaster broadcaster, String status) { + private Bundle createStreamingBundle(LiveStreaming broadcaster, String status) { Bundle streaming = new Bundle(); if (broadcaster != null) { - streaming.putString(PARAM_KEY_URI, broadcaster.getBroadcastURI()); + streaming.putString(PARAM_KEY_URI, broadcaster.getUri()); } streaming.putString(PARAM_KEY_STATUS, status); @@ -410,36 +444,46 @@ private Bundle createStreamingBundle(Broadcaster broadcaster, String status) { video.putInt(PARAM_KEY_WIDTH, settings.getPreviewSize().getWidth()); video.putInt(PARAM_KEY_HEIGHT, settings.getPreviewSize().getHeight()); // video.putInt(PARAM_KEY_BITRATE, settings.getPreviewBitRate()); - video.putInt(PARAM_KEY_FRAME_RATE, settings.getPreviewMaxFrameRate()); +// video.putInt(PARAM_KEY_FRAME_RATE, settings.getPreviewMaxFrameRate()); video.putString(PARAM_KEY_MIME_TYPE, broadcaster.getMimeType()); streaming.putParcelable(PARAM_KEY_VIDEO, video); } return streaming; } - private void postOnStart(Broadcaster broadcaster) { + private void postOnStart(List broadcasters) { List evtList = EventManager.INSTANCE.getEventList(getService().getId(), PROFILE_NAME, null, AT_ON_STATUS_CHANGE); + List b = new ArrayList<>(); + for (LiveStreaming broadcaster : broadcasters) { + b.add(createStreamingBundle(broadcaster, "normal")); + } + for (Event event : evtList) { Intent intent = EventManager.createEventMessage(event); - intent.putExtra(PARAM_KEY_STREAMING, createStreamingBundle(broadcaster, "normal")); + intent.putExtra(PARAM_KEY_STREAMING, b.toArray(new Bundle[0])); sendEvent(intent, event.getAccessToken()); } } - private void postOnStop(Broadcaster broadcaster) { + private void postOnStop(List broadcasters) { List evtList = EventManager.INSTANCE.getEventList(getService().getId(), PROFILE_NAME, null, AT_ON_STATUS_CHANGE); + List b = new ArrayList<>(); + for (LiveStreaming broadcaster : broadcasters) { + b.add(createStreamingBundle(broadcaster, "stop")); + } + for (Event event : evtList) { Intent intent = EventManager.createEventMessage(event); - intent.putExtra(PARAM_KEY_STREAMING, createStreamingBundle(broadcaster, "stop")); + intent.putExtra(PARAM_KEY_STREAMING, b.toArray(new Bundle[0])); sendEvent(intent, event.getAccessToken()); } } - private void postOnError(Broadcaster broadcaster) { + private void postOnError(LiveStreaming broadcaster) { List evtList = EventManager.INSTANCE.getEventList(getService().getId(), PROFILE_NAME, null, AT_ON_STATUS_CHANGE); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 8eb2e076ff..b8a83b58fa 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -17,13 +17,13 @@ import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; -import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; +import org.deviceconnect.android.deviceplugin.host.recorder.AbstractMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.CropInterface; import org.deviceconnect.android.deviceplugin.host.recorder.HostDevicePhotoRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.HostDeviceStreamRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorderManager; -import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServer; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; import org.deviceconnect.android.deviceplugin.host.recorder.camera.Camera2Recorder; import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.event.Event; @@ -31,7 +31,6 @@ import org.deviceconnect.android.event.EventManager; import org.deviceconnect.android.message.MessageUtils; import org.deviceconnect.android.profile.MediaStreamRecordingProfile; -import org.deviceconnect.android.profile.api.DConnectApi; import org.deviceconnect.android.profile.api.DeleteApi; import org.deviceconnect.android.profile.api.GetApi; import org.deviceconnect.android.profile.api.PostApi; @@ -61,1190 +60,1333 @@ public class HostMediaStreamingRecordingProfile extends MediaStreamRecordingProf */ private final FileManager mFileManager; - // GET /gotapi/mediaStreamRecording/mediaRecorder - private final DConnectApi mGetMediaRecorderApi = new GetApi() { + private final HostMediaRecorderManager.OnEventListener mOnEventListener = new HostMediaRecorderManager.OnEventListener() { @Override - public String getAttribute() { - return ATTRIBUTE_MEDIARECORDER; + public void onMuteChanged(HostMediaRecorder recorder, boolean mute) { } @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); - - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } - - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - List recorders = new LinkedList<>(); - for (HostMediaRecorder recorder : mRecorderMgr.getRecorders()) { - HostMediaRecorder.Settings settings = recorder.getSettings(); - - Bundle info = new Bundle(); - setRecorderId(info, recorder.getId()); - setRecorderName(info, recorder.getName()); - setRecorderMIMEType(info, recorder.getMimeType()); + public void onConfigChanged(HostMediaRecorder recorder) { + } - if (recorder.getState() == HostMediaRecorder.State.RECORDING) { - setRecorderState(info, RecorderState.RECORDING); - } else { - setRecorderState(info, RecorderState.INACTIVE); - } + @Override + public void onPreviewStarted(HostMediaRecorder recorder, List servers) { + } - if (recorder.getMimeType().startsWith("image/") || recorder.getMimeType().startsWith("video/")) { - // 静止画の設定 -// Size pictureSize = settings.getPictureSize(); -// if (pictureSize != null) { -// setRecorderImageWidth(info, pictureSize.getWidth()); -// setRecorderImageHeight(info, pictureSize.getHeight()); -// } -// -// // プレビュー設定 -// Size previewSize = settings.getPreviewSize(); -// if (previewSize != null) { -// setRecorderPreviewWidth(info, previewSize.getWidth()); -// setRecorderPreviewHeight(info, previewSize.getHeight()); -// setRecorderPreviewMaxFrameRate(info, settings.getPreviewMaxFrameRate()); -// info.putInt("previewBitRate", settings.getPreviewBitRate() / 1024); -// info.putInt("previewKeyFrameInterval", settings.getPreviewKeyFrameInterval()); -// info.putString("previewEncoder", settings.getPreviewEncoder()); -// HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); -// if (pl != null) { -// switch (HostMediaRecorder.VideoEncoderName.nameOf(settings.getPreviewEncoder())) { -// case H264: -// info.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName()); -// info.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName()); -// break; -// case H265: -// info.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName()); -// info.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName()); -// break; -// } -// } -// info.putFloat("previewJpegQuality", settings.getPreviewQuality() / 100.0f); -// -// Bundle status = new Bundle(); -// status.putBoolean("preview", recorder.isPreviewRunning()); -// status.putBoolean("broadcast", recorder.isBroadcasterRunning()); -// status.putBoolean("recording", recorder.getState() == HostMediaRecorder.State.RECORDING); -// info.putParcelable("status", status); -// } -// -// // 切り抜き設定 -// Rect rect = settings.getCropRect(); -// if (rect != null) { -// Bundle drawingRect = new Bundle(); -// drawingRect.putInt("left", rect.left); -// drawingRect.putInt("top", rect.top); -// drawingRect.putInt("right", rect.right); -// drawingRect.putInt("bottom", rect.bottom); -// info.putBundle("previewClip", drawingRect); -// } - } else if (recorder.getMimeType().startsWith("audio/")) { - // 音声の設定 - } + @Override + public void onPreviewStopped(HostMediaRecorder recorder) { + } - setRecorderConfig(info, ""); - recorders.add(info); - } - setRecorders(response, recorders.toArray(new Bundle[recorders.size()])); - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); - } + @Override + public void onPreviewError(HostMediaRecorder recorder, Exception e) { + } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); - } - }); - return false; + @Override + public void onBroadcasterStarted(HostMediaRecorder recorder, List broadcasters) { } - }; - // PUT /gotapi/mediaStreamRecording/crop - private final DConnectApi mPutCropApi = new PutApi() { @Override - public String getAttribute() { - return "crop"; + public void onBroadcasterStopped(HostMediaRecorder recorder) { } @Override - public boolean onRequest(Intent request, Intent response) { - String target = getTarget(request); - String mimeType = getMIMEType(request); - Integer previewClipLeft = parseInteger(request, "left"); - Integer previewClipTop = parseInteger(request, "top"); - Integer previewClipRight = parseInteger(request, "right"); - Integer previewClipBottom = parseInteger(request, "bottom"); - Integer previewClipDuration = parseInteger(request, "duration"); - - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } + public void onBroadcasterError(HostMediaRecorder recorder, LiveStreaming broadcaster, Exception e) { + } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { -// HostMediaRecorder.StreamingSettings settings = recorder.getSettings().getPreviewServer(mimeType); -// -// int duration = previewClipDuration != null ? previewClipDuration : 0; -// Rect start = settings.getCropRect(); -// if (start == null) { -// int width = recorder.getSettings().getPreviewSize().getWidth(); -// int height = recorder.getSettings().getPreviewSize().getHeight(); -// start = new Rect(0, 0, width, height); -// } -// Rect end = new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom); -// -// PreviewServer ps = recorder.getServerProvider().getServerByMimeType("video/x-rtp"); -// if (ps instanceof CropInterface) { -// ((CropInterface) ps).moveCropRect(start, end, duration); -// } + @Override + public void onTakePhoto(HostMediaRecorder recorder, String uri, String filePath, String mimeType) { + sendEventForTakePhoto(getService().getId(), uri, filePath, mimeType); + } - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); - } + @Override + public void onRecordingStarted(HostMediaRecorder recorder, String fileName) { + sendEventForRecordingChange(getService().getId(), recorder.getState(), + mFileManager.getContentUri() + "/" + fileName, + "/" + fileName, recorder.getMimeType(), null); + } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); - } - }); + @Override + public void onRecordingPause(HostMediaRecorder recorder) { + } - return false; + @Override + public void onRecordingResume(HostMediaRecorder recorder) { } - }; - // DELETE /gotapi/mediaStreamRecording/crop - private final DConnectApi mDeleteCropApi = new DeleteApi() { @Override - public String getAttribute() { - return "crop"; + public void onRecordingStopped(HostMediaRecorder recorder, String fileName) { + sendEventForRecordingChange(getService().getId(), recorder.getState(), + mFileManager.getContentUri() + "/" + fileName, + "/" + fileName, recorder.getMimeType(), null); } @Override - public boolean onRequest(Intent request, Intent response) { - String target = getTarget(request); - String mimeType = getMIMEType(request); + public void onError(HostMediaRecorder recorder, Exception e) { + } + }; - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; + public HostMediaStreamingRecordingProfile(final HostMediaRecorderManager mgr, final FileManager fileMgr) { + mFileManager = fileMgr; + mRecorderMgr = mgr; + mRecorderMgr.addOnEventListener(mOnEventListener); + + // GET /gotapi/mediaStreamRecording/mediaRecorder + addApi(new GetApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_MEDIARECORDER; } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - PreviewServer ps = recorder.getServerProvider().getServerByMimeType(mimeType); - if (ps instanceof CropInterface) { - ((CropInterface) ps).setCropRect(null); - } - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; } - }); - return false; - } - }; + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + List recorders = new LinkedList<>(); + for (HostMediaRecorder recorder : mRecorderMgr.getRecorders()) { + HostMediaRecorder.Settings settings = recorder.getSettings(); + + Bundle info = new Bundle(); + setRecorderId(info, recorder.getId()); + setRecorderName(info, recorder.getName()); + setRecorderMIMEType(info, recorder.getMimeType()); + + if (recorder.getState() == HostMediaRecorder.State.RECORDING) { + setRecorderState(info, RecorderState.RECORDING); + } else { + setRecorderState(info, RecorderState.INACTIVE); + } + // 静止画の解像度 + Size pictureSize = settings.getPictureSize(); + if (pictureSize != null) { + setRecorderImageWidth(info, pictureSize.getWidth()); + setRecorderImageHeight(info, pictureSize.getHeight()); + } - // GET /gotapi/mediaStreamRecording/options - private final DConnectApi mGetOptionsApi = new GetApi() { - @Override - public String getAttribute() { - return ATTRIBUTE_OPTIONS; - } + // プレビュー解像度 + Size previewSize = settings.getPreviewSize(); + if (previewSize != null) { + setRecorderPreviewWidth(info, previewSize.getWidth()); + setRecorderPreviewHeight(info, previewSize.getHeight()); + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); + // カメラのフレームレート + Range previewFps = settings.getPreviewFps(); + if (previewFps != null) { + info.putString("previewFps", previewFps.getLower() + "-" + previewFps.getUpper()); + info.putInt("previewMaxFrameRate", previewFps.getUpper()); + } - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } + // エンコーダ設定 + List encoders = new ArrayList<>(); + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(encoderId); + if (s != null) { + encoders.add(createVideoEncoder(s)); + } + } + info.putParcelableArray("encoders", encoders.toArray(new Bundle[0])); + + info.putString("audioSource", settings.getPreviewAudioSource().getValue()); + info.putInt("audioBitrate", settings.getPreviewAudioBitRate() / 1024); + info.putInt("audioSampleRate", settings.getPreviewSampleRate()); + info.putInt("audioChannel", settings.getPreviewChannel()); + info.putBoolean("audioEchoCanceler", settings.isUseAEC()); - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - HostMediaRecorder.Settings settings = recorder.getSettings(); - if (recorder.getMimeType().startsWith("image/") || recorder.getMimeType().startsWith("video/")) { - // 映像系の設定 - setSupportedImageSizes(response, settings.getSupportedPictureSizes()); - setSupportedPreviewSizes(response, settings.getSupportedPreviewSizes()); - setSupportedVideoEncoders(response, settings.getSupportedVideoEncoders()); - setSupportedFps(response, settings.getSupportedFps()); - } else if (recorder.getMimeType().startsWith("audio/")) { - // 音声系の設定 + setRecorderConfig(info, ""); + recorders.add(info); + } + setRecorders(response, recorders.toArray(new Bundle[0])); + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); } - setResult(response, DConnectMessage.RESULT_OK); - setMIMEType(response, recorder.getSupportedMimeTypes()); + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + return false; + } + }); - sendResponse(response); - } + // GET /gotapi/mediaStreamRecording/options + addApi(new GetApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_OPTIONS; + } + + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; } - }); - return false; - } - }; + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + HostMediaRecorder.Settings settings = recorder.getSettings(); + if (recorder.getMimeType().startsWith("image/") || recorder.getMimeType().startsWith("video/")) { + // 映像系の設定 + setSupportedImageSizes(response, settings.getSupportedPictureSizes()); + setSupportedPreviewSizes(response, settings.getSupportedPreviewSizes()); + setSupportedVideoEncoders(response, settings.getSupportedVideoEncoders()); + setSupportedFps(response, settings.getSupportedFps()); + } else if (recorder.getMimeType().startsWith("audio/")) { + // 音声系の設定 + } - // PUT /gotapi/mediaStreamRecording/options - private final DConnectApi mPutOptionsApi = new PutApi() { - private void setOptions(final Intent request, final Intent response) { - String target = getTarget(request); - String mimeType = getMIMEType(request); - Integer imageWidth = getImageWidth(request); - Integer imageHeight = getImageHeight(request); - Integer previewWidth = getPreviewWidth(request); - Integer previewHeight = getPreviewHeight(request); - Double previewMaxFrameRate = getPreviewMaxFrameRate(request); - Integer previewBitRate = parseInteger(request, "previewBitRate"); - Integer previewKeyFrameInterval = parseInteger(request, "previewKeyFrameInterval"); - String previewEncoder = request.getStringExtra("previewEncoder"); - String previewProfile = request.getStringExtra("previewProfile"); - String previewLevel = request.getStringExtra("previewLevel"); - Integer previewIntraRefresh = parseInteger("previewIntraRefresh"); - Double previewJpegQuality = parseDouble(request, "previewJpegQuality"); - Integer previewClipLeft = parseInteger(request, "previewClipLeft"); - Integer previewClipTop = parseInteger(request, "previewClipTop"); - Integer previewClipRight = parseInteger(request, "previewClipRight"); - Integer previewClipBottom = parseInteger(request, "previewClipBottom"); - Boolean previewClipReset = parseBoolean(request, "previewClipReset"); - HostMediaRecorder.ProfileLevel profileLevel = null; - - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return; - } + setResult(response, DConnectMessage.RESULT_OK); + setMIMEType(response, recorder.getSupportedMimeTypes()); - HostMediaRecorder.Settings settings = recorder.getSettings(); + sendResponse(response); + } - if (!isSupportedMimeType(recorder, mimeType)) { - MessageUtils.setInvalidRequestParameterError(response, "MIME-Type " + mimeType + " is unsupported."); - return; + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; } + }); - if (recorder.getState() != HostMediaRecorder.State.INACTIVE - && recorder.getState() != HostMediaRecorder.State.PREVIEW) { - MessageUtils.setInvalidRequestParameterError(response, "settings of active target cannot be changed."); - return; + // PUT /gotapi/mediaStreamRecording/options + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_OPTIONS; } - // 値の妥当性チェック + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - if (imageWidth != null || imageHeight != null) { - if (imageWidth == null) { - MessageUtils.setInvalidRequestParameterError(response, "imageWidth is not set."); - return; + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; } - if (imageHeight == null) { - MessageUtils.setInvalidRequestParameterError(response, "imageHeight is not set."); - return; - } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + setOptions(request, response); + sendResponse(response); + } - if (!settings.isSupportedPictureSize(imageWidth, imageHeight)) { - MessageUtils.setInvalidRequestParameterError(response, "Unsupported image size: imageWidth = " - + imageWidth + ", imageHeight = " + imageHeight); - return; - } + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + return false; } + }); - if (previewWidth != null || previewHeight != null) { - if (previewWidth == null) { - MessageUtils.setInvalidRequestParameterError(response, "previewWidth is not set."); - return; - } - - if (previewHeight == null) { - MessageUtils.setInvalidRequestParameterError(response, "previewHeight is not set."); - return; - } + // PUT /gotapi/mediaStreamRecording/onPhoto + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_ON_PHOTO; + } - if (!settings.isSupportedPreviewSize(previewWidth, previewHeight)) { - MessageUtils.setInvalidRequestParameterError(response, "Unsupported preview size: previewWidth = " - + previewWidth + ", previewHeight = " + previewHeight); - return; + @Override + public boolean onRequest(final Intent request, final Intent response) { + EventError error = EventManager.INSTANCE.addEvent(request); + if (error == EventError.NONE) { + setResult(response, DConnectMessage.RESULT_OK); + } else { + setResult(response, DConnectMessage.RESULT_ERROR); } + return true; } + }); - if (previewEncoder != null) { - if (!settings.isSupportedVideoEncoder(previewEncoder)) { - MessageUtils.setInvalidRequestParameterError(response, - "Unsupported preview encoder: " + previewEncoder); - return; - } + // DELETE /gotapi/mediaStreamRecording/onPhoto + addApi(new DeleteApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_ON_PHOTO; } - if (previewProfile != null || previewLevel != null) { - if (previewProfile == null) { - MessageUtils.setInvalidRequestParameterError(response, - "previewProfile is not set."); - return; + @Override + public boolean onRequest(final Intent request, final Intent response) { + EventError error = EventManager.INSTANCE.removeEvent(request); + if (error == EventError.NONE) { + setResult(response, DConnectMessage.RESULT_OK); + } else { + setResult(response, DConnectMessage.RESULT_ERROR); } + return true; + } + }); - if (previewLevel == null) { - MessageUtils.setInvalidRequestParameterError(response, - "previewLevel is not set."); - return; - } - -// switch (settings.getPreviewEncoderName()) { -// case H264: { -// H264Profile p = H264Profile.nameOf(previewProfile); -// H264Level l = H264Level.nameOf(previewLevel); -// if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) { -// MessageUtils.setInvalidRequestParameterError(response, -// "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); -// return; -// } -// profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); -// } break; -// case H265: { -// H265Profile p = H265Profile.nameOf(previewProfile); -// H265Level l = H265Level.nameOf(previewLevel); -// if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) { -// MessageUtils.setInvalidRequestParameterError(response, -// "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); -// return; -// } -// profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); -// } break; -// } - } - - if (previewJpegQuality != null) { - if (previewJpegQuality < 0.0 || previewJpegQuality > 1.0) { - MessageUtils.setInvalidRequestParameterError(response, - "previewJpegQuality is invalid. value=" + previewJpegQuality); - return; - } + // POST /gotapi/mediaStreamRecording/takePhoto + addApi(new PostApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_TAKE_PHOTO; } - if (previewClipLeft != null || previewClipTop != null - || previewClipRight != null || previewClipBottom != null) { - if (previewClipLeft == null) { - MessageUtils.setInvalidRequestParameterError(response, "previewClipLeft is not set."); - return; - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - if (previewClipTop == null) { - MessageUtils.setInvalidRequestParameterError(response, "previewClipTop is not set."); - return; + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; } - if (previewClipRight == null) { - MessageUtils.setInvalidRequestParameterError(response, "previewClipRight is not set."); - return; + // TODO 他のカメラとは排他的に処理を行うようにします。 + if (!mRecorderMgr.canUseRecorder(recorder)) { + // 他のカメラが使用中の場合はエラーを返却 + MessageUtils.setIllegalDeviceStateError(response, "Other cameras are being used."); + return true; } - if (previewClipBottom == null) { - MessageUtils.setInvalidRequestParameterError(response, "previewClipBottom is not set."); - return; - } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + // TODO 他のカメラとは排他的に処理を行うようにします。 + if (recorder instanceof Camera2Recorder) { + // 使用する予定のレコーダがカメラの場合は、使用していない他のカメラを停止する + mRecorderMgr.stopCameraRecorder(recorder); + } - if (previewClipLeft < 0) { - MessageUtils.setInvalidRequestParameterError(response, - "previewClipLeft cannot set a negative value."); - return; - } + recorder.takePhoto(new HostDevicePhotoRecorder.OnPhotoEventListener() { + @Override + public void onTakePhoto(final String uri, final String filePath, final String mimeType) { + setResult(response, DConnectMessage.RESULT_OK); + setUri(response, uri); + setPath(response, filePath); + sendResponse(response); + } - if (previewClipBottom < 0) { - MessageUtils.setInvalidRequestParameterError(response, - "previewClipBottom cannot set a negative value."); - return; - } + @Override + public void onFailedTakePhoto(final String errorMessage) { + MessageUtils.setUnknownError(response, errorMessage); + sendResponse(response); + } + }); + } - if (previewClipLeft >= previewClipRight) { - MessageUtils.setInvalidRequestParameterError(response, - "previewClipLeft is larger than previewClipRight."); - return; - } + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); - if (previewClipTop >= previewClipBottom) { - MessageUtils.setInvalidRequestParameterError(response, - "previewClipTop is larger than previewClipBottom."); - return; - } - } - - // 値の設定 - - if (imageWidth != null && imageHeight != null) { - settings.setPictureSize(new Size(imageWidth, imageHeight)); - } - - if (previewWidth != null && previewHeight != null) { - settings.setPreviewSize(new Size(previewWidth, previewHeight)); - } - - if (previewMaxFrameRate != null) { - settings.setPreviewMaxFrameRate(previewMaxFrameRate.intValue()); - } - -// if (previewBitRate != null) { -// settings.setPreviewBitRate(previewBitRate * 1024); -// } -// -// if (previewKeyFrameInterval != null) { -// settings.setPreviewKeyFrameInterval(previewKeyFrameInterval); -// } -// -// if (previewEncoder != null) { -// settings.setPreviewEncoder(previewEncoder); -// // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする -// settings.setProfileLevel(null); -// } -// -// if (profileLevel != null) { -// settings.setProfileLevel(profileLevel); -// } -// -// if (previewIntraRefresh != null) { -// settings.setIntraRefresh(previewIntraRefresh); -// } -// -// if (previewJpegQuality != null) { -// settings.setPreviewQuality((int) (previewJpegQuality * 100)); -// } -// -// if (previewClipReset != null && previewClipReset) { -// settings.setCropRect(null); -// } else if (previewClipLeft != null) { -// settings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); -// } - - try { - recorder.onConfigChange(); - } catch (Exception e) { - MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config."); - return; + return false; } + }); - setResult(response, DConnectMessage.RESULT_OK); - } - - @Override - public String getAttribute() { - return ATTRIBUTE_OPTIONS; - } - - @Override - public boolean onRequest(final Intent request, final Intent response) { - final String target = getTarget(request); - - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; + // POST /gotapi/mediaStreamRecording/record + addApi(new PostApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_RECORD; } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - setOptions(request, response); - sendResponse(response); - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; } - }); - return false; - } - }; - // POST /gotapi/mediaStreamRecording/preview/requestKeyFrame - private final DConnectApi mPostPreviewRequestKeyFrameApi = new PostApi() { + // TODO 他のカメラとは排他的に処理を行うようにします。 + if (!mRecorderMgr.canUseRecorder(recorder)) { + // 他のカメラが使用中の場合はエラーを返却 + MessageUtils.setIllegalDeviceStateError(response, "Other cameras are being used."); + return true; + } - @Override - public String getInterface() { - return "preview"; - } + // 撮影がすでに行われている場合はエラーを返却 + if (recorder.getState() != HostMediaRecorder.State.INACTIVE + && recorder.getState() != HostMediaRecorder.State.PREVIEW) { + MessageUtils.setIllegalDeviceStateError(response, + recorder.getName() + " is already running."); + return true; + } - @Override - public String getAttribute() { - return "requestKeyFrame"; - } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + recorder.startRecording(new HostDeviceStreamRecorder.RecordingCallback() { + @Override + public void onRecorded(final HostDeviceStreamRecorder streamRecorder, final String fileName) { + setResult(response, DConnectMessage.RESULT_OK); + setPath(response, "/" + fileName); + setUri(response, mFileManager.getContentUri() + "/" + fileName); + sendResponse(response); + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - final String target = getTarget(request); + @Override + public void onFailed(final HostDeviceStreamRecorder recorder, final String errorMessage) { + MessageUtils.setIllegalServerStateError(response, errorMessage); + sendResponse(response); + } + }); + } - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + return false; + } + }); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; + // PUT /gotapi/mediaStreamRecording/stop + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_STOP; } - if (!recorder.isBroadcasterRunning() && !recorder.isPreviewRunning()) { - MessageUtils.setIllegalServerStateError(response, "Recorder has not started previewing."); - } else { - recorder.requestKeyFrame(); - setResult(response, DConnectMessage.RESULT_OK); + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); + + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + if (recorder.getState() == HostMediaRecorder.State.INACTIVE) { + MessageUtils.setIllegalDeviceStateError(response, "recorder is stopped already."); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + recorder.stopRecording(new HostDeviceStreamRecorder.StoppingCallback() { + @Override + public void onStopped(HostDeviceStreamRecorder streamRecorder, String fileName) { + setResult(response, DConnectMessage.RESULT_OK); + setPath(response, "/" + fileName); + setUri(response, mFileManager.getContentUri() + "/" + fileName); + sendResponse(response); + } + + @Override + public void onFailed(HostDeviceStreamRecorder recorder, String errorMessage) { + MessageUtils.setIllegalServerStateError(response, errorMessage); + sendResponse(response); + } + }); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; } + }); - return true; - } - }; + // PUT /gotapi/mediaStreamRecording/pause + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_PAUSE; + } - // PUT /gotapi/mediaStreamRecording/onPhoto - private final DConnectApi mPutOnPhotoApi = new PutApi() { + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public String getAttribute() { - return ATTRIBUTE_ON_PHOTO; - } + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - EventError error = EventManager.INSTANCE.addEvent(request); - if (error == EventError.NONE) { - setResult(response, DConnectMessage.RESULT_OK); - } else { - setResult(response, DConnectMessage.RESULT_ERROR); + if (!recorder.canPauseRecording()) { + MessageUtils.setNotSupportAttributeError(response); + return true; + } + + if (recorder.getState() != HostMediaRecorder.State.RECORDING) { + MessageUtils.setIllegalDeviceStateError(response); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + recorder.pauseRecording(); + + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; } - return true; - } - }; + }); - // DELETE /gotapi/mediaStreamRecording/onPhoto - private final DConnectApi mDeleteOnPhotoApi = new DeleteApi() { + // PUT /gotapi/mediaStreamRecording/resume + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_RESUME; + } - @Override - public String getAttribute() { - return ATTRIBUTE_ON_PHOTO; - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public boolean onRequest(final Intent request, final Intent response) { - EventError error = EventManager.INSTANCE.removeEvent(request); - if (error == EventError.NONE) { - setResult(response, DConnectMessage.RESULT_OK); - } else { - setResult(response, DConnectMessage.RESULT_ERROR); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + if (!recorder.canPauseRecording()) { + MessageUtils.setNotSupportAttributeError(response); + return true; + } + + if (recorder.getState() != HostMediaRecorder.State.PAUSED) { + MessageUtils.setIllegalDeviceStateError(response); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + recorder.resumeRecording(); + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; } - return true; - } - }; + }); - // PUT /gotapi/mediaStreamRecording/onRecordingChange - private final DConnectApi mPutOnRecordingChangeApi = new PutApi() { + // PUT /gotapi/mediaStreamRecording/onRecordingChange + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_ON_RECORDING_CHANGE; + } - @Override - public String getAttribute() { - return ATTRIBUTE_ON_RECORDING_CHANGE; - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + EventError error = EventManager.INSTANCE.addEvent(request); + if (error == EventError.NONE) { + setResult(response, DConnectMessage.RESULT_OK); + } else { + setResult(response, DConnectMessage.RESULT_ERROR); + } + return true; + } + }); - @Override - public boolean onRequest(final Intent request, final Intent response) { - EventError error = EventManager.INSTANCE.addEvent(request); - if (error == EventError.NONE) { - setResult(response, DConnectMessage.RESULT_OK); - } else { - setResult(response, DConnectMessage.RESULT_ERROR); + // DELETE /gotapi/mediaStreamRecording/onRecordingChange + addApi(new DeleteApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_ON_RECORDING_CHANGE; } - return true; - } - }; - // DELETE /gotapi/mediaStreamRecording/onRecordingChange - private final DConnectApi mDeleteOnRecordingChangeApi = new DeleteApi() { + @Override + public boolean onRequest(final Intent request, final Intent response) { + EventError error = EventManager.INSTANCE.removeEvent(request); + if (error == EventError.NONE) { + setResult(response, DConnectMessage.RESULT_OK); + } else { + setResult(response, DConnectMessage.RESULT_ERROR); + } + return true; + } + }); + + // PUT /gotapi/mediaStreamRecording/preview + addApi(new PutApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_PREVIEW; + } - @Override - public String getAttribute() { - return ATTRIBUTE_ON_RECORDING_CHANGE; - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public boolean onRequest(final Intent request, final Intent response) { - EventError error = EventManager.INSTANCE.removeEvent(request); - if (error == EventError.NONE) { - setResult(response, DConnectMessage.RESULT_OK); - } else { - setResult(response, DConnectMessage.RESULT_ERROR); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + // TODO 他のカメラとは排他的に処理を行うようにします。 + if (!mRecorderMgr.canUseRecorder(recorder)) { + // 他のカメラが使用中の場合はエラーを返却 + MessageUtils.setIllegalDeviceStateError(response, "Other cameras are being used."); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + HostDevicePlugin plugin = getHostDevicePlugin(); + plugin.getSSLContext(sslContext -> { + recorder.setSSLContext(sslContext); + + List servers = recorder.startPreview(); + if (servers.isEmpty()) { + MessageUtils.setIllegalServerStateError(response, "Failed to start web server."); + } else { + String defaultUri = null; + List streams = new ArrayList<>(); + for (LiveStreaming server : servers) { + // Motion-JPEG をデフォルトの値として使用します + if (defaultUri == null && server.getUri().startsWith("http://") + && "video/x-mjpeg".equals(server.getMimeType())) { + defaultUri = server.getUri(); + } + + Bundle stream = new Bundle(); + stream.putString("mimeType", server.getMimeType()); + stream.putString("uri", server.getUri()); + streams.add(stream); + } + setResult(response, DConnectMessage.RESULT_OK); + setUri(response, defaultUri != null ? defaultUri : ""); + response.putExtra("streams", streams.toArray(new Bundle[0])); + } + + sendResponse(response); + }); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + + return false; } - return true; - } - }; + }); - // POST /gotapi/mediaStreamRecording/takePhoto - private final DConnectApi mPostTakePhotoApi = new PostApi() { + // DELETE /gotapi/mediaStreamRecording/preview + addApi(new DeleteApi() { + @Override + public String getAttribute() { + return ATTRIBUTE_PREVIEW; + } - @Override - public String getAttribute() { - return ATTRIBUTE_TAKE_PHOTO; - } + @Override + public boolean onRequest(final Intent request, final Intent response) { + String target = getTarget(request); - @Override - public boolean onRequest(final Intent request, final Intent response) { - final String target = getTarget(request); - final String serviceId = getServiceID(request); + final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + recorder.stopPreview(); + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + return false; + } + }); + + // POST /gotapi/mediaStreamRecording/preview/requestKeyFrame + addApi(new PostApi() { + @Override + public String getInterface() { + return "preview"; } - // TODO 他のカメラとは排他的に処理を行うようにします。 - if (!mRecorderMgr.canUseRecorder(recorder)) { - // 他のカメラが使用中の場合はエラーを返却 - MessageUtils.setIllegalDeviceStateError(response, "Other cameras are being used."); + @Override + public String getAttribute() { + return "requestKeyFrame"; + } + + @Override + public boolean onRequest(final Intent request, final Intent response) { + final String target = getTarget(request); + + final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + if (!recorder.isBroadcasterRunning() && !recorder.isPreviewRunning()) { + MessageUtils.setIllegalServerStateError(response, "Recorder has not started previewing."); + } else { + recorder.requestKeyFrame(); + setResult(response, DConnectMessage.RESULT_OK); + } + return true; } + }); + + // PUT /gotapi/mediaStreamRecording/preview/mute + addApi(new PutApi() { + @Override + public String getInterface() { return ATTRIBUTE_PREVIEW; } + @Override + public String getAttribute() { + return "mute"; + } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - // TODO 他のカメラとは排他的に処理を行うようにします。 - if (recorder instanceof Camera2Recorder) { - // 使用する予定のレコーダがカメラの場合は、使用していない他のカメラを停止する - mRecorderMgr.stopCameraRecorder(recorder); + @Override + public boolean onRequest(final Intent request, final Intent response) { + return setMute(true, request, response); + } + }); + + // DELETE /gotapi/mediaStreamRecording/preview/mute + addApi(new DeleteApi() { + + @Override + public String getInterface() { return ATTRIBUTE_PREVIEW; } + @Override + public String getAttribute() { + return "mute"; + } + + @Override + public boolean onRequest(final Intent request, final Intent response) { + return setMute(false, request, response); + } + }); + + // POST /gotapi/mediaStreamRecording/encoder + addApi(new PostApi() { + @Override + public String getAttribute() { + return "encoder"; + } + + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String name = request.getStringExtra("name"); + String mimeTypeValue = request.getStringExtra("mimeType"); + Integer port = parseInteger(request, "port"); + Integer width = parseInteger(request, "width"); + Integer height = parseInteger(request, "height"); + Integer frameRate = parseInteger(request, "frameRate"); + Integer bitRate = parseInteger(request, "bitRate"); + Integer keyFrameInterval = parseInteger(request, "keyFrameInterval"); + String codec = request.getStringExtra("codec"); + String profile = request.getStringExtra("profile"); + String level = request.getStringExtra("level"); + Integer intraRefresh = parseInteger("intraRefresh"); + Boolean useSoftwareEncoder = parseBoolean("useSoftwareEncoder"); + Double jpegQuality = parseDouble(request, "jpegQuality"); + String broadcastUri = request.getStringExtra("broadcastUri"); + Integer retryCount = parseInteger(request, "retryCount"); + Integer retryInterval = parseInteger(request, "retryInterval"); + + if (name == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is not set."); + return true; + } + + if (mimeTypeValue == null) { + MessageUtils.setInvalidRequestParameterError(response, "mimeType is not set."); + return true; + } + + HostMediaRecorder.MimeType mimeType = HostMediaRecorder.MimeType.typeOf(mimeTypeValue); + if (mimeType == HostMediaRecorder.MimeType.UNKNOWN) { + MessageUtils.setInvalidRequestParameterError(response, "mimeType is unknown."); + return true; + } + + final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); + return true; + } + + String encoderId = recorder.getId() + "-" + name; + + List encoderIdList = recorder.getSettings().getEncoderIdList(); + if (encoderIdList.contains(encoderId)) { + MessageUtils.setInvalidRequestParameterError(response, "name is already exists."); + return true; + } + + if (width != null && width < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "width is invalid. value=" + width); + return true; + } + + if (height != null && height < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "height is invalid. value=" + height); + return true; + } + + if (frameRate != null && frameRate <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "frameRate is invalid. value=" + frameRate); + return true; + } + + if (bitRate != null && bitRate <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "bitRate. value=" + bitRate); + return true; + } + + if (keyFrameInterval != null && keyFrameInterval <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "keyFrameInterval. value=" + keyFrameInterval); + return true; + } + + if (intraRefresh != null && intraRefresh < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "intraRefresh is invalid. value=" + intraRefresh); + return true; + } + + if (jpegQuality != null) { + if (jpegQuality < 0.0 || jpegQuality > 1.0) { + MessageUtils.setInvalidRequestParameterError(response, + "jpegQuality is invalid. value=" + jpegQuality); + return true; } + } - recorder.takePhoto(new HostDevicePhotoRecorder.OnPhotoEventListener() { - @Override - public void onTakePhoto(final String uri, final String filePath, final String mimeType) { - setResult(response, DConnectMessage.RESULT_OK); - setUri(response, uri); - setPath(response, filePath); - sendResponse(response); + if (retryCount != null) { + if (retryCount < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "retryCount is invalid. value=" + retryCount); + return true; + } + } + + if (retryInterval != null) { + if (retryInterval <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "retryInterval is invalid. value=" + retryInterval); + return true; + } + } + + HostMediaRecorder.ProfileLevel profileLevel = convertProfileLevel(recorder, codec, profile, level); + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + HostMediaRecorder.EncoderSettings encoderSettings = new HostMediaRecorder.EncoderSettings(getContext(), encoderId); + encoderSettings.setName(name); + encoderSettings.setMimeType(mimeType); + + if (width != null && height != null) { + encoderSettings.setPreviewSize(new Size(width, height)); + } else { + encoderSettings.setPreviewSize(new Size(640, 480)); } - @Override - public void onFailedTakePhoto(final String errorMessage) { - MessageUtils.setUnknownError(response, errorMessage); - sendResponse(response); + if (port != null) { + encoderSettings.setPort(port); + } else { + encoderSettings.setPort(14000); } - }); - } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); - } - }); + if (frameRate != null) { + encoderSettings.setPreviewMaxFrameRate(frameRate); + } else { + encoderSettings.setPreviewMaxFrameRate(30); + } - return false; - } - }; + if (bitRate != null) { + encoderSettings.setPreviewBitRate(bitRate * 1024); + } else { + encoderSettings.setPreviewBitRate(2 * 1024 * 1024); + } - // PUT /gotapi/mediaStreamRecording/preview - private final DConnectApi mPutPreviewApi = new PutApi() { + if (keyFrameInterval != null) { + encoderSettings.setPreviewKeyFrameInterval(keyFrameInterval); + } else { + encoderSettings.setPreviewKeyFrameInterval(5); + } - @Override - public String getAttribute() { - return ATTRIBUTE_PREVIEW; - } + if (codec != null) { + encoderSettings.setPreviewEncoder(codec); + // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする + encoderSettings.setProfileLevel(null); + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); + if (profileLevel != null) { + encoderSettings.setProfileLevel(profileLevel); + } + + if (intraRefresh != null) { + encoderSettings.setIntraRefresh(intraRefresh); + } - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (useSoftwareEncoder != null) { + encoderSettings.setUseSoftwareEncoder(useSoftwareEncoder); + } + + if (jpegQuality != null) { + encoderSettings.setPreviewQuality((int) (jpegQuality * 100)); + } else { + encoderSettings.setPreviewQuality(80); + } + + if (broadcastUri != null) { + encoderSettings.setBroadcastURI(broadcastUri); + } else { + encoderSettings.setBroadcastURI("rtmp://localhost:1935"); + } + + if (retryCount != null) { + encoderSettings.setRetryCount(retryCount); + } else { + encoderSettings.setRetryCount(0); + } + + if (retryInterval != null) { + encoderSettings.setRetryInterval(retryInterval); + } else { + encoderSettings.setRetryInterval(3000); + } + + ((AbstractMediaRecorder) recorder).addEncoder(encoderId, encoderSettings); + + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; + return false; } + }); - // TODO 他のカメラとは排他的に処理を行うようにします。 - if (!mRecorderMgr.canUseRecorder(recorder)) { - // 他のカメラが使用中の場合はエラーを返却 - MessageUtils.setIllegalDeviceStateError(response, "Other cameras are being used."); - return true; + // PUT /gotapi/mediaStreamRecording/encoder + addApi(new PutApi() { + @Override + public String getAttribute() { + return "encoder"; } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - HostDevicePlugin plugin = getHostDevicePlugin(); - plugin.getSSLContext(sslContext -> { - recorder.setSSLContext(sslContext); - - List servers = recorder.startPreview(); - if (servers.isEmpty()) { - MessageUtils.setIllegalServerStateError(response, "Failed to start web server."); - } else { - String defaultUri = null; - List streams = new ArrayList<>(); - for (PreviewServer server : servers) { - // Motion-JPEG をデフォルトの値として使用します - if (defaultUri == null && server.getUri().startsWith("http://") - && "video/x-mjpeg".equals(server.getMimeType())) { - defaultUri = server.getUri(); - } - - Bundle stream = new Bundle(); - stream.putString("mimeType", server.getMimeType()); - stream.putString("uri", server.getUri()); - streams.add(stream); - } - setResult(response, DConnectMessage.RESULT_OK); - setUri(response, defaultUri != null ? defaultUri : ""); - response.putExtra("streams", streams.toArray(new Bundle[0])); - } - - sendResponse(response); - }); + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String name = request.getStringExtra("name"); + Integer width = parseInteger(request, "width"); + Integer height = parseInteger(request, "height"); + Integer frameRate = parseInteger(request, "frameRate"); + Integer bitRate = parseInteger(request, "bitRate"); + Integer keyFrameInterval = parseInteger(request, "keyFrameInterval"); + String codec = request.getStringExtra("codec"); + String profile = request.getStringExtra("profile"); + String level = request.getStringExtra("level"); + Integer intraRefresh = parseInteger("intraRefresh"); + Boolean useSoftwareEncoder = parseBoolean("useSoftwareEncoder"); + Double jpegQuality = parseDouble(request, "jpegQuality"); + String broadcastUri = request.getStringExtra("broadcastUri"); + Integer retryCount = parseInteger(request, "retryCount"); + Integer retryInterval = parseInteger(request, "retryInterval"); + + if (name == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is not set."); + return true; } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); + return true; } - }); - - return false; - } - }; - - // DELETE /gotapi/mediaStreamRecording/preview - private final DConnectApi mDeletePreviewApi = new DeleteApi() { - - @Override - public String getAttribute() { - return ATTRIBUTE_PREVIEW; - } - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); + String encoderId = recorder.getId() + "-" + name; - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (width != null && width < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "width is invalid. value=" + width); + return true; + } - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - recorder.stopPreview(); - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); + if (height != null && height < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "height is invalid. value=" + height); + return true; } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + if (frameRate != null && frameRate <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "frameRate is invalid. value=" + frameRate); + return true; } - }); - return false; - } - }; - // PUT /gotapi/mediaStreamRecording/preview/mute - private final DConnectApi mPutPreviewMuteApi = new PutApi() { + if (bitRate != null && bitRate <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "bitRate. value=" + bitRate); + return true; + } - @Override - public String getInterface() { return ATTRIBUTE_PREVIEW; } - @Override - public String getAttribute() { - return "mute"; - } + if (keyFrameInterval != null && keyFrameInterval <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "keyFrameInterval. value=" + keyFrameInterval); + return true; + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - return setMute(true, request, response); - } - }; + if (intraRefresh != null && intraRefresh < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "intraRefresh is invalid. value=" + intraRefresh); + return true; + } - // DELETE /gotapi/mediaStreamRecording/preview/mute - private final DConnectApi mDeletePreviewMuteApi = new DeleteApi() { + if (jpegQuality != null) { + if (jpegQuality < 0.0 || jpegQuality > 1.0) { + MessageUtils.setInvalidRequestParameterError(response, + "jpegQuality is invalid. value=" + jpegQuality); + return true; + } + } - @Override - public String getInterface() { return ATTRIBUTE_PREVIEW; } - @Override - public String getAttribute() { - return "mute"; - } + if (retryCount != null) { + if (retryCount < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "retryCount is invalid. value=" + retryCount); + return true; + } + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - return setMute(false, request, response); - } - }; + if (retryInterval != null) { + if (retryInterval <= 0) { + MessageUtils.setInvalidRequestParameterError(response, + "retryInterval is invalid. value=" + retryInterval); + return true; + } + } - /** - * RecorderのMute状態を切り返す. - * RTSPをサポートしているRecorderのみ対応する. - * @param muted true: mute状態にする。 false: mute解除状態にする。 - * @param request リクエスト - * @param response レスポンス - * @return true: 同期 false: 非同期 - */ - private boolean setMute(final boolean muted, final Intent request, final Intent response) { - String target = getTarget(request); + HostMediaRecorder.ProfileLevel profileLevel = convertProfileLevel(recorder, codec, profile, level); - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); - if (!recorder.getSettings().isAudioEnabled()) { - MessageUtils.setNotSupportAttributeError(response, "mute is not supported."); - return true; - } + if (width != null && height != null) { + encoderSettings.setPreviewSize(new Size(width, height)); + } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - recorder.setMute(muted); - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); - } + if (frameRate != null) { + encoderSettings.setPreviewMaxFrameRate(frameRate); + } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); - } - }); - return false; - } + if (bitRate != null) { + encoderSettings.setPreviewBitRate(bitRate * 1024); + } - // POST /gotapi/mediaStreamRecording/record - private final DConnectApi mPostRecordApi = new PostApi() { + if (keyFrameInterval != null) { + encoderSettings.setPreviewKeyFrameInterval(keyFrameInterval); + } - @Override - public String getAttribute() { - return ATTRIBUTE_RECORD; - } + if (codec != null) { + encoderSettings.setPreviewEncoder(codec); + // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする. + encoderSettings.setProfileLevel(null); + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); + if (profileLevel != null) { + encoderSettings.setProfileLevel(profileLevel); + } - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } + if (intraRefresh != null) { + encoderSettings.setIntraRefresh(intraRefresh); + } - // TODO 他のカメラとは排他的に処理を行うようにします。 - if (!mRecorderMgr.canUseRecorder(recorder)) { - // 他のカメラが使用中の場合はエラーを返却 - MessageUtils.setIllegalDeviceStateError(response, "Other cameras are being used."); - return true; - } + if (useSoftwareEncoder != null) { + encoderSettings.setUseSoftwareEncoder(useSoftwareEncoder); + } - // 撮影がすでに行われている場合はエラーを返却 - if (recorder.getState() != HostMediaRecorder.State.INACTIVE - && recorder.getState() != HostMediaRecorder.State.PREVIEW) { - MessageUtils.setIllegalDeviceStateError(response, - recorder.getName() + " is already running."); - return true; - } + if (jpegQuality != null) { + encoderSettings.setPreviewQuality((int) (jpegQuality * 100)); + } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - recorder.startRecording(new HostDeviceStreamRecorder.RecordingCallback() { - @Override - public void onRecorded(final HostDeviceStreamRecorder streamRecorder, final String fileName) { - setResult(response, DConnectMessage.RESULT_OK); - setPath(response, "/" + fileName); - setUri(response, mFileManager.getContentUri() + "/" + fileName); - sendResponse(response); + if (broadcastUri != null) { + encoderSettings.setBroadcastURI(broadcastUri); } - @Override - public void onFailed(final HostDeviceStreamRecorder recorder, final String errorMessage) { - MessageUtils.setIllegalServerStateError(response, errorMessage); - sendResponse(response); + if (retryCount != null) { + encoderSettings.setRetryCount(retryCount); } - }); - } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); - } - }); - return false; - } - }; + if (retryInterval != null) { + encoderSettings.setRetryInterval(retryInterval); + } - // PUT /gotapi/mediaStreamRecording/stop - private final DConnectApi mPutStopApi = new PutApi() { + try { + recorder.onConfigChange(); + setResult(response, DConnectMessage.RESULT_OK); + } catch (Exception e) { + MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config."); + } - @Override - public String getAttribute() { - return ATTRIBUTE_STOP; - } + sendResponse(response); + } - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; + return false; } + }); - if (recorder.getState() == HostMediaRecorder.State.INACTIVE) { - MessageUtils.setIllegalDeviceStateError(response, "recorder is stopped already."); - return true; + // DELETE /gotapi/mediaStreamRecording/encoder + addApi(new DeleteApi() { + @Override + public String getAttribute() { + return "encoder"; } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - recorder.stopRecording(new HostDeviceStreamRecorder.StoppingCallback() { - @Override - public void onStopped(HostDeviceStreamRecorder streamRecorder, String fileName) { - setResult(response, DConnectMessage.RESULT_OK); - setPath(response, "/" + fileName); - setUri(response, mFileManager.getContentUri() + "/" + fileName); - sendResponse(response); - } + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String name = request.getStringExtra("name"); - @Override - public void onFailed(HostDeviceStreamRecorder recorder, String errorMessage) { - MessageUtils.setIllegalServerStateError(response, errorMessage); - sendResponse(response); - } - }); + if (name == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is not set."); + return true; } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); + return true; } - }); - - return false; - } - }; - - // PUT /gotapi/mediaStreamRecording/pause - private final DConnectApi mPutPauseApi = new PutApi() { - - @Override - public String getAttribute() { - return ATTRIBUTE_PAUSE; - } - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); + String encoderId = recorder.getId() + "-" + name; - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } + ((AbstractMediaRecorder) recorder).removeEncoder(encoderId); - if (!recorder.canPauseRecording()) { - MessageUtils.setNotSupportAttributeError(response); + setResult(response, DConnectMessage.RESULT_OK); return true; } + }); - if (recorder.getState() != HostMediaRecorder.State.RECORDING) { - MessageUtils.setIllegalDeviceStateError(response); - return true; + // PUT /gotapi/mediaStreamRecording/crop + addApi(new PutApi() { + @Override + public String getAttribute() { + return "crop"; } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - recorder.pauseRecording(); + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String name = request.getStringExtra("name"); + Integer left = parseInteger(request, "left"); + Integer top = parseInteger(request, "top"); + Integer right = parseInteger(request, "right"); + Integer bottom = parseInteger(request, "bottom"); + Integer duration = parseInteger(request, "duration"); - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + if (name == null) { + MessageUtils.setInvalidRequestParameterError(response, "name is not set."); + return true; } - }); - - return false; - } - }; - - // PUT /gotapi/mediaStreamRecording/resume - private final DConnectApi mPutResumeApi = new PutApi() { - - @Override - public String getAttribute() { - return ATTRIBUTE_RESUME; - } - - @Override - public boolean onRequest(final Intent request, final Intent response) { - String target = getTarget(request); - - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } - - if (!recorder.canPauseRecording()) { - MessageUtils.setNotSupportAttributeError(response); - return true; - } - - if (recorder.getState() != HostMediaRecorder.State.PAUSED) { - MessageUtils.setIllegalDeviceStateError(response); - return true; - } - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - recorder.resumeRecording(); - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); + if (left == null) { + MessageUtils.setInvalidRequestParameterError(response, "left is not set."); + return true; } - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); + if (top == null) { + MessageUtils.setInvalidRequestParameterError(response, "top is not set."); + return true; } - }); - return false; - } - }; + if (right == null) { + MessageUtils.setInvalidRequestParameterError(response, "right is not set."); + return true; + } - private final HostMediaRecorderManager.OnEventListener mOnEventListener = new HostMediaRecorderManager.OnEventListener() { - @Override - public void onMuteChanged(HostMediaRecorder recorder, boolean mute) { - } + if (bottom == null) { + MessageUtils.setInvalidRequestParameterError(response, "bottom is not set."); + return true; + } - @Override - public void onConfigChanged(HostMediaRecorder recorder) { - } + String encoderId = recorder.getId() + "-" + name; - @Override - public void onPreviewStarted(HostMediaRecorder recorder, List servers) { - } + if (left >= right || top >= bottom) { + MessageUtils.setInvalidRequestParameterError(response, "parameter is invalid."); + return true; + } - @Override - public void onPreviewStopped(HostMediaRecorder recorder) { - } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + Rect start = encoderSettings.getCropRect(); + if (start == null) { + int width = recorder.getSettings().getPreviewSize().getWidth(); + int height = recorder.getSettings().getPreviewSize().getHeight(); + start = new Rect(0, 0, width, height); + } - @Override - public void onPreviewError(HostMediaRecorder recorder, Exception e) { - } + Rect end = new Rect(left, top, right, bottom); - @Override - public void onBroadcasterStarted(HostMediaRecorder recorder, Broadcaster broadcaster) { - } + for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { + if (encoderId.equals(previewServer.getId())) { + ((CropInterface) previewServer).moveCropRect(start, end, duration); + } + } - @Override - public void onBroadcasterStopped(HostMediaRecorder recorder, Broadcaster broadcaster) { - } + for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { + if (encoderId.equals(broadcaster.getId())) { + ((CropInterface) broadcaster).moveCropRect(start, end, duration); + } + } - @Override - public void onBroadcasterError(HostMediaRecorder recorder, Broadcaster broadcaster, Exception e) { - } + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } - @Override - public void onTakePhoto(HostMediaRecorder recorder, String uri, String filePath, String mimeType) { - sendEventForTakePhoto(getService().getId(), uri, filePath, mimeType); - } + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); - @Override - public void onRecordingStarted(HostMediaRecorder recorder, String fileName) { - sendEventForRecordingChange(getService().getId(), recorder.getState(), - mFileManager.getContentUri() + "/" + fileName, - "/" + fileName, recorder.getMimeType(), null); - } + return false; + } + }); - @Override - public void onRecordingPause(HostMediaRecorder recorder) { - } + // DELETE /gotapi/mediaStreamRecording/crop + addApi(new DeleteApi() { + @Override + public String getAttribute() { + return "crop"; + } - @Override - public void onRecordingResume(HostMediaRecorder recorder) { - } + @Override + public boolean onRequest(Intent request, Intent response) { + String target = getTarget(request); + String name = request.getStringExtra("name"); - @Override - public void onRecordingStopped(HostMediaRecorder recorder, String fileName) { - sendEventForRecordingChange(getService().getId(), recorder.getState(), - mFileManager.getContentUri() + "/" + fileName, - "/" + fileName, recorder.getMimeType(), null); - } + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } - @Override - public void onError(HostMediaRecorder recorder, Exception e) { - } - }; + String encoderId = recorder.getId() + "-" + name; - public HostMediaStreamingRecordingProfile(final HostMediaRecorderManager mgr, final FileManager fileMgr) { - mFileManager = fileMgr; - mRecorderMgr = mgr; - mRecorderMgr.addOnEventListener(mOnEventListener); + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { + if (encoderId.equals(previewServer.getId())) { + ((CropInterface) previewServer).setCropRect(null); + } + } - addApi(mGetMediaRecorderApi); + for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { + if (encoderId.equals(broadcaster.getId())) { + ((CropInterface) broadcaster).setCropRect(null); + } + } - addApi(mPutCropApi); - addApi(mDeleteCropApi); + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } - addApi(mGetOptionsApi); - addApi(mPutOptionsApi); + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); - addApi(mPutOnPhotoApi); - addApi(mDeleteOnPhotoApi); - addApi(mPostTakePhotoApi); - addApi(mPostRecordApi); - addApi(mPutStopApi); - addApi(mPutPauseApi); - addApi(mPutResumeApi); - addApi(mPutOnRecordingChangeApi); - addApi(mDeleteOnRecordingChangeApi); + return false; + } + }); - addApi(mPutPreviewApi); - addApi(mDeletePreviewApi); - addApi(mPostPreviewRequestKeyFrameApi); - addApi(mPutPreviewMuteApi); - addApi(mDeletePreviewMuteApi); // PUT /gotapi/mediaStreamRecording/broadcast addApi(new PutApi() { @@ -1291,8 +1433,15 @@ public void onAllowed() { mRecorderMgr.stopCameraRecorder(recorder); } - Broadcaster b = recorder.startBroadcaster(broadcastURI); - if (b != null) { + for (String encoderId : recorder.getSettings().getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings != null && encoderSettings.getMimeType() == HostMediaRecorder.MimeType.RTMP) { + encoderSettings.setBroadcastURI(broadcastURI); + } + } + + List broadcasters = recorder.startBroadcaster(broadcastURI); + if (!broadcasters.isEmpty()) { setResult(response, DConnectMessage.RESULT_OK); } else { MessageUtils.setIllegalServerStateError(response, "Failed to start a broadcast."); @@ -1352,6 +1501,53 @@ private HostDevicePlugin getHostDevicePlugin() { return (HostDevicePlugin) getContext(); } + private Bundle createVideoEncoder(HostMediaRecorder.EncoderSettings s) { + Bundle bundle = new Bundle(); + bundle.putString("name", s.getName()); + bundle.putString("mimeType", s.getMimeType().getValue()); + bundle.putInt("width", s.getPreviewSize().getWidth()); + bundle.putInt("height", s.getPreviewSize().getHeight()); + bundle.putInt("bitrate", s.getPreviewBitRate() / 1024); + + if ("video/x-mjpeg".equals(s.getMimeType().getValue())) { + bundle.putFloat("jpegQuality", s.getPreviewQuality() / 100.0f); + } else if (s.getMimeType().getValue().startsWith("video/")) { + bundle.putInt("keyFrameInterval", s.getPreviewKeyFrameInterval()); + bundle.putString("encoder", s.getPreviewEncoder()); + HostMediaRecorder.ProfileLevel pl = s.getProfileLevel(); + if (pl != null) { + switch (HostMediaRecorder.VideoCodec.nameOf(s.getPreviewEncoder())) { + case H264: + bundle.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName()); + bundle.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName()); + break; + case H265: + bundle.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName()); + bundle.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName()); + break; + } + } + bundle.putBoolean("useSoftwareEncoder", s.isUseSoftwareEncoder()); + Integer intraRefresh = s.getIntraRefresh(); + if (intraRefresh != null) { + bundle.putInt("intraRefresh", intraRefresh); + } + } + + // 切り抜き設定 + Rect rect = s.getCropRect(); + if (rect != null) { + Bundle drawingRect = new Bundle(); + drawingRect.putInt("left", rect.left); + drawingRect.putInt("top", rect.top); + drawingRect.putInt("right", rect.right); + drawingRect.putInt("bottom", rect.bottom); + bundle.putBundle("crop", drawingRect); + } + + return bundle; + } + /** * サポートしている静止画の解像度をレスポンスに格納します. * @@ -1397,10 +1593,15 @@ private static void setSupportedPreviewSizes(final Intent response, final List encoderNames) { List encoders = new ArrayList<>(); for (String name : encoderNames) { - HostMediaRecorder.VideoEncoderName encoderName = HostMediaRecorder.VideoEncoderName.nameOf(name); + HostMediaRecorder.VideoCodec videoCodec = HostMediaRecorder.VideoCodec.nameOf(name); + Size maxSize = CapabilityUtil.getSupportedMaxSize(videoCodec.getMimeType()); Bundle encoder = new Bundle(); encoder.putString("name", name); - encoder.putParcelableArray("profileLevel", getProfileLevels(encoderName)); + encoder.putParcelableArray("profileLevel", getProfileLevels(videoCodec)); + if (maxSize != null) { + encoder.putInt("maxWidth", maxSize.getWidth()); + encoder.putInt("maxHeight", maxSize.getHeight()); + } encoders.add(encoder); } response.putExtra("encoder", encoders.toArray(new Bundle[0])); @@ -1409,13 +1610,13 @@ private static void setSupportedVideoEncoders(Intent response, List enco /** * エンコーダがサポートしているプロファイルとレベルを格納した Bundle の配列を取得します. * - * @param encoderName エンコーダ + * @param videoCodec エンコーダ * @return プロファイルとレベルを格納した Bundle の配列 */ - private static Bundle[] getProfileLevels(HostMediaRecorder.VideoEncoderName encoderName) { + private static Bundle[] getProfileLevels(HostMediaRecorder.VideoCodec videoCodec) { List list = new ArrayList<>(); - for (HostMediaRecorder.ProfileLevel pl : CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType())) { - switch (encoderName) { + for (HostMediaRecorder.ProfileLevel pl : CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType())) { + switch (videoCodec) { case H264: { H264Profile p = H264Profile.valueOf(pl.getProfile()); H264Level l = H264Level.valueOf(pl.getLevel()); @@ -1441,6 +1642,363 @@ private static Bundle[] getProfileLevels(HostMediaRecorder.VideoEncoderName enco return list.toArray(new Bundle[0]); } + /** + * RecorderのMute状態を切り返す. + * RTSPをサポートしているRecorderのみ対応する. + * @param muted true: mute状態にする。 false: mute解除状態にする。 + * @param request リクエスト + * @param response レスポンス + * @return true: 同期 false: 非同期 + */ + private boolean setMute(final boolean muted, final Intent request, final Intent response) { + String target = getTarget(request); + + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return true; + } + + if (!recorder.getSettings().isAudioEnabled()) { + MessageUtils.setNotSupportAttributeError(response, "mute is not supported."); + return true; + } + + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { + @Override + public void onAllowed() { + recorder.setMute(muted); + setResult(response, DConnectMessage.RESULT_OK); + sendResponse(response); + } + + @Override + public void onDisallowed() { + MessageUtils.setUnknownError(response, "Permission for camera is not granted."); + sendResponse(response); + } + }); + return false; + } + + private void setOptions(final Intent request, final Intent response) { + String target = getTarget(request); + String mimeType = getMIMEType(request); + Integer imageWidth = getImageWidth(request); + Integer imageHeight = getImageHeight(request); + Integer previewWidth = getPreviewWidth(request); + Integer previewHeight = getPreviewHeight(request); + Double previewMaxFrameRate = getPreviewMaxFrameRate(request); + Integer previewBitRate = parseInteger(request, "previewBitRate"); + Integer previewKeyFrameInterval = parseInteger(request, "previewKeyFrameInterval"); + String previewEncoder = request.getStringExtra("previewEncoder"); + String previewProfile = request.getStringExtra("previewProfile"); + String previewLevel = request.getStringExtra("previewLevel"); + Integer previewIntraRefresh = parseInteger("previewIntraRefresh"); + Double previewJpegQuality = parseDouble(request, "previewJpegQuality"); + Integer previewClipLeft = parseInteger(request, "previewClipLeft"); + Integer previewClipTop = parseInteger(request, "previewClipTop"); + Integer previewClipRight = parseInteger(request, "previewClipRight"); + Integer previewClipBottom = parseInteger(request, "previewClipBottom"); + Boolean previewClipReset = parseBoolean(request, "previewClipReset"); + HostMediaRecorder.ProfileLevel profileLevel = null; + Range fps = null; + + HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); + if (recorder == null) { + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + return; + } + + HostMediaRecorder.Settings settings = recorder.getSettings(); + + if (!isSupportedMimeType(recorder, mimeType)) { + MessageUtils.setInvalidRequestParameterError(response, "MIME-Type " + mimeType + " is unsupported."); + return; + } + + if (recorder.getState() != HostMediaRecorder.State.INACTIVE + && recorder.getState() != HostMediaRecorder.State.PREVIEW) { + MessageUtils.setInvalidRequestParameterError(response, "settings of active target cannot be changed."); + return; + } + + // 値の妥当性チェック + + if (imageWidth != null || imageHeight != null) { + if (imageWidth == null) { + MessageUtils.setInvalidRequestParameterError(response, "imageWidth is not set."); + return; + } + + if (imageHeight == null) { + MessageUtils.setInvalidRequestParameterError(response, "imageHeight is not set."); + return; + } + + if (!settings.isSupportedPictureSize(imageWidth, imageHeight)) { + MessageUtils.setInvalidRequestParameterError(response, "Unsupported image size: imageWidth = " + + imageWidth + ", imageHeight = " + imageHeight); + return; + } + } + + if (previewWidth != null || previewHeight != null) { + if (previewWidth == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewWidth is not set."); + return; + } + + if (previewHeight == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewHeight is not set."); + return; + } + + if (!settings.isSupportedPreviewSize(previewWidth, previewHeight)) { + MessageUtils.setInvalidRequestParameterError(response, "Unsupported preview size: previewWidth = " + + previewWidth + ", previewHeight = " + previewHeight); + return; + } + } + + if (previewMaxFrameRate != null) { + fps = recorder.getSettings().getPreviewFpsFromFrameRate(previewMaxFrameRate.intValue()); + if (fps == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewMaxFrameRate is not supported."); + } + } + + if (previewEncoder != null) { + if (!settings.isSupportedVideoEncoder(previewEncoder)) { + MessageUtils.setInvalidRequestParameterError(response, + "Unsupported preview encoder: " + previewEncoder); + return; + } + } + + if (previewProfile != null || previewLevel != null) { + if (previewProfile == null) { + MessageUtils.setInvalidRequestParameterError(response, + "previewProfile is not set."); + return; + } + + if (previewLevel == null) { + MessageUtils.setInvalidRequestParameterError(response, + "previewLevel is not set."); + return; + } + + HostMediaRecorder.VideoCodec codec = HostMediaRecorder.VideoCodec.nameOf(previewEncoder); + switch (codec) { + case H264: { + H264Profile p = H264Profile.nameOf(previewProfile); + H264Level l = H264Level.nameOf(previewLevel); + if (p == null || l == null || !settings.isSupportedProfileLevel(codec, p.getValue(), l.getValue())) { + MessageUtils.setInvalidRequestParameterError(response, + "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); + return; + } + profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } break; + case H265: { + H265Profile p = H265Profile.nameOf(previewProfile); + H265Level l = H265Level.nameOf(previewLevel); + if (p == null || l == null || !settings.isSupportedProfileLevel(codec, p.getValue(), l.getValue())) { + MessageUtils.setInvalidRequestParameterError(response, + "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel); + return; + } + profileLevel = new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } break; + } + } + + if (previewJpegQuality != null) { + if (previewJpegQuality < 0.0 || previewJpegQuality > 1.0) { + MessageUtils.setInvalidRequestParameterError(response, + "previewJpegQuality is invalid. value=" + previewJpegQuality); + return; + } + } + + if (previewClipLeft != null || previewClipTop != null + || previewClipRight != null || previewClipBottom != null) { + if (previewClipLeft == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewClipLeft is not set."); + return; + } + + if (previewClipTop == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewClipTop is not set."); + return; + } + + if (previewClipRight == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewClipRight is not set."); + return; + } + + if (previewClipBottom == null) { + MessageUtils.setInvalidRequestParameterError(response, "previewClipBottom is not set."); + return; + } + + if (previewClipLeft < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "previewClipLeft cannot set a negative value."); + return; + } + + if (previewClipBottom < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "previewClipBottom cannot set a negative value."); + return; + } + + if (previewClipLeft >= previewClipRight) { + MessageUtils.setInvalidRequestParameterError(response, + "previewClipLeft is larger than previewClipRight."); + return; + } + + if (previewClipTop >= previewClipBottom) { + MessageUtils.setInvalidRequestParameterError(response, + "previewClipTop is larger than previewClipBottom."); + return; + } + } + + // 値の設定 + + if (imageWidth != null && imageHeight != null) { + settings.setPictureSize(new Size(imageWidth, imageHeight)); + } + + if (previewWidth != null && previewHeight != null) { + settings.setPreviewSize(new Size(previewWidth, previewHeight)); + } + + if (fps != null) { + settings.setPreviewFps(fps); + + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setPreviewMaxFrameRate(previewMaxFrameRate.intValue()); + } + } + } + + if (previewBitRate != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setPreviewBitRate(previewBitRate * 1024); + } + } + } + + if (previewKeyFrameInterval != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setPreviewKeyFrameInterval(previewKeyFrameInterval); + } + } + } + + if (previewEncoder != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setPreviewEncoder(previewEncoder); + encoderSettings.setProfileLevel(null); + } + } + } + + if (profileLevel != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setProfileLevel(profileLevel); + } + } + } + + if (previewIntraRefresh != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setIntraRefresh(previewIntraRefresh); + } + } + } + + if (previewJpegQuality != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setPreviewQuality((int) (previewJpegQuality * 100)); + } + } + } + + if (previewClipReset != null && previewClipReset) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setCropRect(null); + } + } + } else if (previewClipLeft != null) { + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); + } + } + } + + try { + recorder.onConfigChange(); + } catch (Exception e) { + MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config."); + return; + } + + setResult(response, DConnectMessage.RESULT_OK); + } + + private HostMediaRecorder.ProfileLevel convertProfileLevel(HostMediaRecorder recorder, String codec, String profile, String level) { + if (profile != null && level != null) { + HostMediaRecorder.VideoCodec codec1 = HostMediaRecorder.VideoCodec.H264; + if (codec != null) { + codec1 = HostMediaRecorder.VideoCodec.nameOf(codec); + } + switch (codec1) { + case H264: { + H264Profile p = H264Profile.nameOf(profile); + H264Level l = H264Level.nameOf(level); + if (p == null || l == null || !recorder.getSettings().isSupportedProfileLevel(codec1, p.getValue(), l.getValue())) { + return null; + } + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + case H265: { + H265Profile p = H265Profile.nameOf(profile); + H265Level l = H265Level.nameOf(level); + if (p == null || l == null || !recorder.getSettings().isSupportedProfileLevel(codec1, p.getValue(), l.getValue())) { + return null; + } + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + } + return null; + } + /** * カメラがサポートしている Fps のリストをレスポンスに格納します. * @@ -1448,13 +2006,11 @@ private static Bundle[] getProfileLevels(HostMediaRecorder.VideoEncoderName enco * @param supportedFps サポートしている fps のリスト */ private static void setSupportedFps(Intent response, List> supportedFps) { - List fpsList = new ArrayList<>(); + List fpsList = new ArrayList<>(); for (Range fps : supportedFps) { - if (!fpsList.contains(fps.getUpper())) { - fpsList.add(fps.getUpper()); - } + fpsList.add(fps.getLower() + "-" + fps.getUpper()); } - response.putExtra("frameRate", fpsList.toArray(new Integer[0])); + response.putExtra("frameRate", fpsList.toArray(new String[0])); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java index df584347b9..5a0c197418 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java @@ -13,200 +13,40 @@ import org.deviceconnect.android.deviceplugin.host.R; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -public abstract class AbstractBroadcastProvider implements BroadcasterProvider { - /** - * 映像を配信するクラス. - */ - private Broadcaster mBroadcaster; - - /** - * イベントを通知するリスナー. - */ - private OnEventListener mOnEventListener; - - private final Context mContext; - private final HostMediaRecorder mRecorder; +public abstract class AbstractBroadcastProvider extends AbstractLiveStreamingProvider implements BroadcasterProvider { public AbstractBroadcastProvider(Context context, HostMediaRecorder recorder) { - mContext = context; - mRecorder = recorder; - } - - @Override - public void setOnEventListener(OnEventListener listener) { - mOnEventListener = listener; - } - - @Override - public Broadcaster getBroadcaster() { - return mBroadcaster; - } - - @Override - public boolean isRunning() { - return mBroadcaster != null && mBroadcaster.isRunning(); - } - - @Override - public Broadcaster startBroadcaster(String broadcastURI) { - if (broadcastURI == null) { - return null; - } - - if (mBroadcaster != null && mBroadcaster.isRunning()) { - return mBroadcaster; - } - - CountDownLatch latch = new CountDownLatch(1); - AtomicBoolean result = new AtomicBoolean(false); - - mBroadcaster = createBroadcaster(broadcastURI); - if (mBroadcaster == null) { - return null; - } - mBroadcaster.setOnEventListener(new Broadcaster.OnEventListener() { - @Override - public void onStarted() { - postBroadcastStarted(mBroadcaster); - } - - @Override - public void onStopped() { - hideNotification(mRecorder.getId()); - postBroadcastStopped(mBroadcaster); - } - - @Override - public void onError(Exception e) { - postBroadcastError(mBroadcaster, e); - } - }); - - mBroadcaster.start(new Broadcaster.OnStartCallback() { - @Override - public void onSuccess() { - result.set(true); - latch.countDown(); - } - - @Override - public void onFailed(Exception e) { - result.set(false); - latch.countDown(); - } - }); - - try { - latch.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - return null; - } - - if (!result.get()) { - mBroadcaster.stop(); - mBroadcaster = null; - } else { - sendNotification(mRecorder.getId(), mRecorder.getName()); - } - - return mBroadcaster; - } - - @Override - public void stopBroadcaster() { - hideNotification(mRecorder.getId()); - - if (mBroadcaster != null) { - mBroadcaster.stop(); - mBroadcaster = null; - } + super(context, recorder); } @Override - public void onConfigChange() { - if (mBroadcaster != null) { - mBroadcaster.onConfigChange(); - } - } - - @Override - public void setMute(boolean mute) { - if (mBroadcaster != null) { - mBroadcaster.setMute(mute); - } - } - - /** - * Broadcaster のインスタンスを作成します. - * - * @param broadcastURI 配信先の URI - * @return Broadcaster のインスタンス - */ - public abstract Broadcaster createBroadcaster(String broadcastURI); - - private void postBroadcastStarted(Broadcaster broadcaster) { - if (mOnEventListener != null) { - mOnEventListener.onStarted(broadcaster); - } - } - - private void postBroadcastStopped(Broadcaster broadcaster) { - if (mOnEventListener != null) { - mOnEventListener.onStopped(broadcaster); - } - } - - private void postBroadcastError(Broadcaster broadcaster, Exception e) { - if (mOnEventListener != null) { - mOnEventListener.onError(broadcaster, e); - } - } - - /** - * Notification の Id を取得します. - * - * @return Notification の Id - */ - private int getNotificationId() { - return 1000 + mRecorder.getId().hashCode(); - } - - /** - * プレビュー配信サーバ停止用の Notification を削除します. - * - * @param id notification を識別する ID - */ - private void hideNotification(String id) { - NotificationManager manager = (NotificationManager) mContext + protected void hideNotification(String id) { + NotificationManager manager = (NotificationManager) getContext() .getSystemService(Service.NOTIFICATION_SERVICE); if (manager != null) { manager.cancel(id, getNotificationId()); } } - /** - * プレビュー配信サーバ停止用の Notification を送信します. - * - * @param id notification を識別する ID - * @param name 名前 - */ - private void sendNotification(String id, String name) { + @Override + protected void sendNotification(String id, String name) { PendingIntent contentIntent = createPendingIntent(id); Notification notification = createNotification(contentIntent, null, name); - NotificationManager manager = (NotificationManager) mContext + NotificationManager manager = (NotificationManager) getContext() .getSystemService(Service.NOTIFICATION_SERVICE); if (manager != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - String channelId = mContext.getResources().getString(R.string.overlay_preview_channel_id); + String channelId = getContext().getResources().getString(R.string.overlay_preview_channel_id); NotificationChannel channel = new NotificationChannel( channelId, - mContext.getResources().getString(R.string.host_notification_recorder_broadcast), + getContext().getResources().getString(R.string.host_notification_recorder_broadcast), NotificationManager.IMPORTANCE_LOW); - channel.setDescription(mContext.getResources().getString(R.string.host_notification_recorder_broadcast_content)); + channel.setDescription(getContext().getResources().getString(R.string.host_notification_recorder_broadcast_content)); manager.createNotificationChannel(channel); notification = createNotification(contentIntent, channelId, name); } @@ -214,6 +54,15 @@ private void sendNotification(String id, String name) { } } + /** + * Notification の Id を取得します. + * + * @return Notification の Id + */ + private int getNotificationId() { + return 1000 + getRecorder().getId().hashCode(); + } + /** * Notificationを作成する. * @@ -224,25 +73,25 @@ private void sendNotification(String id, String name) { */ private Notification createNotification(final PendingIntent pendingIntent, final String channelId, String name) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext.getApplicationContext()); + NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext().getApplicationContext()); builder.setContentIntent(pendingIntent); - builder.setTicker(mContext.getString(R.string.host_notification_recorder_broadcast_ticker)); + builder.setTicker(getContext().getString(R.string.host_notification_recorder_broadcast_ticker)); builder.setSmallIcon(R.drawable.dconnect_icon); - builder.setContentTitle(mContext.getString(R.string.host_notification_recorder_broadcast, name)); - builder.setContentText(mContext.getString(R.string.host_notification_recorder_broadcast_content)); + builder.setContentTitle(getContext().getString(R.string.host_notification_recorder_broadcast, name)); + builder.setContentText(getContext().getString(R.string.host_notification_recorder_broadcast_content)); builder.setWhen(System.currentTimeMillis()); builder.setAutoCancel(true); builder.setOngoing(true); return builder.build(); } else { - Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext()); + Notification.Builder builder = new Notification.Builder(getContext().getApplicationContext()); builder.setContentIntent(pendingIntent); - builder.setTicker(mContext.getString(R.string.overlay_preview_ticker)); + builder.setTicker(getContext().getString(R.string.overlay_preview_ticker)); int iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.dconnect_icon : R.drawable.dconnect_icon_lollipop; builder.setSmallIcon(iconType); - builder.setContentTitle(mContext.getString(R.string.host_notification_recorder_broadcast, name)); - builder.setContentText(mContext.getString(R.string.host_notification_recorder_broadcast_content)); + builder.setContentTitle(getContext().getString(R.string.host_notification_recorder_broadcast, name)); + builder.setContentText(getContext().getString(R.string.host_notification_recorder_broadcast_content)); builder.setWhen(System.currentTimeMillis()); builder.setAutoCancel(true); builder.setOngoing(true); @@ -264,6 +113,6 @@ private PendingIntent createPendingIntent(String id) { Intent intent = new Intent(); intent.setAction(HostMediaRecorderManager.ACTION_STOP_BROADCAST); intent.putExtra(HostMediaRecorderManager.KEY_RECORDER_ID, id); - return PendingIntent.getBroadcast(mContext, getNotificationId(), intent, 0); + return PendingIntent.getBroadcast(getContext(), getNotificationId(), intent, 0); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java index 57620dd589..2f4107ffec 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcaster.java @@ -1,216 +1,23 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -import android.graphics.Rect; -import android.media.AudioAttributes; -import android.media.AudioFormat; -import android.media.AudioPlaybackCaptureConfiguration; -import android.media.projection.MediaProjection; -import android.os.Build; -import android.util.Size; - -import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; -import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder; -import org.deviceconnect.android.libmedia.streaming.audio.MicAudioQuality; -import org.deviceconnect.android.libmedia.streaming.audio.filter.HighPassFilter; -import org.deviceconnect.android.libmedia.streaming.audio.filter.LowPassFilter; -import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; -import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; -import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; - -public abstract class AbstractBroadcaster implements Broadcaster, CropInterface { - /** - * 配信先の URI. - */ - private final String mBroadcastURI; - - /** - * カメラを操作するレコーダ. - */ - private final HostMediaRecorder mRecorder; - - /** - * 配信名. - */ - private final String mName; - - /** - * 切り抜き範囲移動用スレッド. - */ - private MovingRectThread mMovingRectThread; - - /** - * 切り抜き範囲移動用スレッドからのイベントを受け取るリスナー. - */ - private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(new Rect(rect)); - } - getStreamingSettings().setCropRect(rect); - postOnMoved(rect); - }; - public AbstractBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { - mRecorder = recorder; - mBroadcastURI = broadcastURI; - mName = name; - startMovingRectThread(); - } - - @Override - public String getName() { - return mName; - } +public abstract class AbstractBroadcaster extends AbstractLiveStreaming implements Broadcaster { - @Override - public String getMimeType() { - return "video/x-rtmp"; + public AbstractBroadcaster(HostMediaRecorder recorder, String id) { + super(recorder, id); } @Override public String getBroadcastURI() { - return mBroadcastURI; - } - - @Override - public void onConfigChange() { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - setVideoQuality(videoQuality); - } - - AudioQuality audioQuality = getAudioQuality(); - if (audioQuality != null) { - setAudioQuality(audioQuality); - - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - setMute(settings.isMute()); - } - } - - @Override - public void release() { - stopMovingRectThread(); - } - - // CropInterface implements - - private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); - - @Override - public void moveCropRect(Rect start, Rect end, int duration) { - if (end != null) { - if (getStreamingSettings().getCropRect() == null) { - postOnAdded(end); - } - } else { - postOnRemove(); - } - - if (mMovingRectThread != null) { - mMovingRectThread.move(start, end, duration); - } else { - getStreamingSettings().setCropRect(end); - - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(end); - } - } - } - - @Override - public void setCropRect(Rect rect) { - if (rect != null) { - if (getStreamingSettings().getCropRect() == null) { - postOnAdded(rect); - } - } else { - postOnRemove(); - } - - if (rect == null || mMovingRectThread == null) { - getStreamingSettings().setCropRect(rect); - - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(rect); - } - } else { - mMovingRectThread.set(rect); - } - } - - @Override - public Rect getCropRect() { - return getStreamingSettings().getCropRect(); + return getEncoderSettings().getBroadcastURI(); } @Override - public void addOnEventListener(CropInterface.OnEventListener listener) { - mOnEventListeners.add(listener); - } - - @Override - public void removeOnEventListener(CropInterface.OnEventListener listener) { - mOnEventListeners.remove(listener); - } - - private void postOnAdded(Rect rect) { - for (CropInterface.OnEventListener l : mOnEventListeners.get()) { - l.onAdded(this, rect); - } - } - - private void postOnRemove() { - for (CropInterface.OnEventListener l : mOnEventListeners.get()) { - l.onRemoved(this); - } - } - - private void postOnMoved(Rect rect) { - for (CropInterface.OnEventListener l : mOnEventListeners.get()) { - l.onMoved(this, rect); - } - } - - private void startMovingRectThread() { - if (mMovingRectThread != null) { - return; - } - - mMovingRectThread = new MovingRectThread(); - mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); - mMovingRectThread.start(); - } - - private void stopMovingRectThread() { - if (mMovingRectThread != null) { - mMovingRectThread.stop(); - mMovingRectThread = null; - } - } - - /** - * Broadcaster で使用するレコーダを取得します. - * - * @return Broadcaster で使用するレコーダ - */ - public HostMediaRecorder getRecorder() { - return mRecorder; - } - - /** - * Broadcaster の設定を取得します. - * - * @return 設定 - */ - public HostMediaRecorder.StreamingSettings getStreamingSettings() { - return mRecorder.getSettings().getBroadcaster(getName()); + public String getUri() { + return getEncoderSettings().getBroadcastURI(); } /** @@ -234,119 +41,4 @@ protected AudioEncoder createAudioEncoder() { } return null; } - - /** - * 映像の設定を取得します. - * - * 映像が使用しない場合は null を返却すること。 - * - * @return 映像の設定 - */ - protected VideoQuality getVideoQuality() { - return null; - } - - /** - * 音声の設定を取得します. - * - * 音声が使用しない場合は null を返却すること。 - * - * @return 音声の設定 - */ - protected AudioQuality getAudioQuality() { - return null; - } - - /** - * VideoEncoder の設定に、HostMediaRecorder の設定を反映します. - * - * @param videoQuality 設定を行う VideoEncoder の VideoQuality - */ - public void setVideoQuality(VideoQuality videoQuality) { - HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); - - EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); - int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); - int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); - videoQuality.setVideoWidth(w); - videoQuality.setVideoHeight(h); - videoQuality.setCropRect(settings.getCropRect()); - videoQuality.setBitRate(settings.getPreviewBitRate()); - videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); - videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); - videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); - videoQuality.setIntraRefresh(settings.getIntraRefresh()); - videoQuality.setProfile(settings.getProfile()); - videoQuality.setLevel(settings.getLevel()); - if (settings.getPreviewBitRateMode() != null) { - switch (settings.getPreviewBitRateMode()) { - default: - case VBR: - videoQuality.setBitRateMode(VideoQuality.BitRateMode.VBR); - break; - case CBR: - videoQuality.setBitRateMode(VideoQuality.BitRateMode.CBR); - break; - case CQ: - videoQuality.setBitRateMode(VideoQuality.BitRateMode.CQ); - break; - } - } else { - videoQuality.setBitRateMode(null); - } - } - - /** - * AudioEncoder の設定に、HostMediaRecorder の設定を反映します. - * - * @param audioQuality 設定を行う AudioEncoder の AudioQuality - */ - public void setAudioQuality(AudioQuality audioQuality) { - HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - - audioQuality.setChannel(settings.getPreviewChannel() == 1 ? - AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO); - audioQuality.setSamplingRate(settings.getPreviewSampleRate()); - audioQuality.setBitRate(settings.getPreviewAudioBitRate()); - audioQuality.setUseAEC(settings.isUseAEC()); - - if (settings.getAudioFilter() != null) { - float coeff = settings.getAudioCoefficient(); - switch (settings.getAudioFilter()) { - case LOW_PASS: - audioQuality.setFilter(new LowPassFilter(audioQuality, coeff)); - break; - case HIGH_PASS: - audioQuality.setFilter(new HighPassFilter(audioQuality, coeff)); - break; - default: - audioQuality.setFilter(null); - break; - } - } else { - audioQuality.setFilter(null); - } - - MicAudioQuality quality = (MicAudioQuality) audioQuality; - if (settings.getPreviewAudioSource() == HostMediaRecorder.AudioSource.APP) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - // アプリの録音機能 - MediaProjectionProvider provider = recorder.getMediaProjectionProvider(); - if (provider != null && provider.getMediaProjection() != null) { - MediaProjection mediaProjection = provider.getMediaProjection(); - AudioPlaybackCaptureConfiguration configuration = - new AudioPlaybackCaptureConfiguration.Builder(mediaProjection) - .addMatchingUsage(AudioAttributes.USAGE_GAME) - .addMatchingUsage(AudioAttributes.USAGE_MEDIA) - .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN) - .build(); - quality.setCaptureConfig(configuration); - } - } - quality.setSource(MicAudioQuality.Source.APP); - } - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java new file mode 100644 index 0000000000..ead23adcc1 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java @@ -0,0 +1,412 @@ +package org.deviceconnect.android.deviceplugin.host.recorder; + +import android.graphics.Rect; +import android.media.AudioAttributes; +import android.media.AudioFormat; +import android.media.AudioPlaybackCaptureConfiguration; +import android.media.projection.MediaProjection; +import android.os.Build; +import android.util.Size; + +import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; +import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; +import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; +import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; +import org.deviceconnect.android.libmedia.streaming.audio.MicAudioQuality; +import org.deviceconnect.android.libmedia.streaming.audio.filter.HighPassFilter; +import org.deviceconnect.android.libmedia.streaming.audio.filter.LowPassFilter; +import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; +import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; +import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; +import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; + +import javax.net.ssl.SSLContext; + +public abstract class AbstractLiveStreaming implements LiveStreaming, CropInterface { + /** + * カメラを操作するレコーダ. + */ + private final HostMediaRecorder mRecorder; + + /** + * SSLContext のインスタンス. + */ + private SSLContext mSSLContext; + + /** + * ミュート設定. + */ + private boolean mMute; + + /** + * プレビュー配信サーバID. + */ + private final String mId; + + /** + * 切り抜き範囲移動用スレッド. + */ + private MovingRectThread mMovingRectThread; + + /** + * 切り抜き範囲移動用スレッドからのイベントを受け取るリスナー. + */ + private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(new Rect(rect)); + } + getEncoderSettings().setCropRect(rect); + postOnMoved(rect); + }; + + /** + * 切り抜き範囲のイベントを通知するリスナー. + */ + private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); + + public AbstractLiveStreaming(HostMediaRecorder recorder, String id) { + mRecorder = recorder; + mId = id; + mMute = true; + startMovingRectThread(); + } + + @Override + public String getId() { + return mId; + } + + @Override + public void setOnEventListener(LiveStreaming.OnEventListener listener) { + + } + + @Override + public void onConfigChange() { + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + setVideoQuality(videoQuality); + } + + AudioQuality audioQuality = getAudioQuality(); + if (audioQuality != null) { + setAudioQuality(audioQuality); + + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + setMute(settings.isMute()); + } + } + + @Override + public void release() { + stopMovingRectThread(); + } + + @Override + public String getUri() { + return null; + } + + @Override + public void setMute(boolean mute) { + mMute = mute; + } + + @Override + public boolean isMuted() { + return mMute; + } + + @Override + public boolean requestSyncFrame() { + VideoEncoder videoEncoder = getVideoEncoder(); + if (videoEncoder != null) { + videoEncoder.requestSyncKeyFrame(); + return true; + } + return false; + } + + @Override + public long getBPS() { + return 0; + } + + @Override + public boolean useSSLContext() { + return getEncoderSettings().isUseSSL(); + } + + @Override + public void setSSLContext(final SSLContext sslContext) { + mSSLContext = sslContext; + } + + @Override + public SSLContext getSSLContext() { + return mSSLContext; + } + + // CropInterface implements + + @Override + public String getName() { + return getEncoderSettings().getName(); + } + + @Override + public void moveCropRect(Rect start, Rect end, int duration) { + checkCropRect(end); + + if (end == null || mMovingRectThread == null) { + setCropRectInternal(end); + } else { + mMovingRectThread.move(start, end, duration); + } + } + + @Override + public void setCropRect(Rect rect) { + checkCropRect(rect); + + if (rect == null || mMovingRectThread == null) { + setCropRectInternal(rect); + } else { + mMovingRectThread.set(rect); + } + } + + @Override + public Rect getCropRect() { + return getEncoderSettings().getCropRect(); + } + + @Override + public void addOnEventListener(CropInterface.OnEventListener listener) { + mOnEventListeners.add(listener); + } + + @Override + public void removeOnEventListener(CropInterface.OnEventListener listener) { + mOnEventListeners.remove(listener); + } + + private void checkCropRect(Rect rect) { + if (rect != null) { + if (getEncoderSettings().getCropRect() == null) { + postOnAdded(rect); + } + } else { + postOnRemove(); + } + } + + private void setCropRectInternal(Rect rect) { + getEncoderSettings().setCropRect(rect); + + VideoQuality videoQuality = getVideoQuality(); + if (videoQuality != null) { + videoQuality.setCropRect(rect); + } + } + + private void postOnAdded(Rect rect) { + for (CropInterface.OnEventListener l : mOnEventListeners.get()) { + l.onAdded(this, rect); + } + } + + private void postOnRemove() { + for (CropInterface.OnEventListener l : mOnEventListeners.get()) { + l.onRemoved(this); + } + } + + private void postOnMoved(Rect rect) { + for (CropInterface.OnEventListener l : mOnEventListeners.get()) { + l.onMoved(this, rect); + } + } + + private void startMovingRectThread() { + if (mMovingRectThread != null) { + return; + } + + mMovingRectThread = new MovingRectThread(); + mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); + mMovingRectThread.start(); + } + + private void stopMovingRectThread() { + if (mMovingRectThread != null) { + mMovingRectThread.stop(); + mMovingRectThread = null; + } + } + + /** + * プレビューを表示するレコーダー. + * + * @return レコーダー + */ + public HostMediaRecorder getRecorder() { + return mRecorder; + } + + /** + * Broadcaster の設定を取得します. + * + * @return サーバの設定 + */ + public HostMediaRecorder.EncoderSettings getEncoderSettings() { + return mRecorder.getSettings().getEncoderSetting(mId); + } + + /** + * 映像のエンコーダを取得します. + * + * 映像が使用されていない場合は null を返却すること。 + * + * @return 映像のエンコーダ + */ + protected VideoEncoder getVideoEncoder() { + return null; + } + + /** + * 音声のエンコーダを取得します. + * + * 音声が使用されていない場合は null を返却すること。 + * + * @return 音声のエンコーダ + */ + protected AudioEncoder getAudioEncoder() { + return null; + } + + /** + * 映像の設定を取得します. + * + * 映像が使用されていない場合は null を返却すること。 + * + * @return 映像の設定 + */ + protected VideoQuality getVideoQuality() { + VideoEncoder videoEncoder = getVideoEncoder(); + if (videoEncoder != null) { + return videoEncoder.getVideoQuality(); + } + return null; + } + + /** + * 音声の設定を取得します. + * + * 音声が使用されていない場合は null を返却すること。 + * + * @return 音声の設定 + */ + protected AudioQuality getAudioQuality() { + AudioEncoder audioEncoder = getAudioEncoder(); + if (audioEncoder != null) { + return audioEncoder.getAudioQuality(); + } + return null; + } + + /** + * VideoEncoder の設定に、HostMediaRecorder の設定を反映します. + * + * @param videoQuality 設定を行う VideoEncoder の VideoQuality + */ + public void setVideoQuality(VideoQuality videoQuality) { + HostMediaRecorder recorder = getRecorder(); + HostMediaRecorder.EncoderSettings settings = getEncoderSettings(); + + EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); + Size previewSize = settings.getPreviewSize(); + int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); + int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); + videoQuality.setVideoWidth(w); + videoQuality.setVideoHeight(h); + videoQuality.setCropRect(settings.getCropRect()); + videoQuality.setBitRate(settings.getPreviewBitRate()); + videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); + videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); + videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); + videoQuality.setIntraRefresh(settings.getIntraRefresh()); + videoQuality.setProfile(settings.getProfile()); + videoQuality.setLevel(settings.getLevel()); + if (settings.getPreviewBitRateMode() != null) { + switch (settings.getPreviewBitRateMode()) { + default: + case VBR: + videoQuality.setBitRateMode(VideoQuality.BitRateMode.VBR); + break; + case CBR: + videoQuality.setBitRateMode(VideoQuality.BitRateMode.CBR); + break; + case CQ: + videoQuality.setBitRateMode(VideoQuality.BitRateMode.CQ); + break; + } + } else { + videoQuality.setBitRateMode(null); + } + } + + /** + * AudioEncoder の設定に、HostMediaRecorder の設定を反映します. + * + * @param audioQuality 設定を行う AudioEncoder の AudioQuality + */ + public void setAudioQuality(AudioQuality audioQuality) { + HostMediaRecorder recorder = getRecorder(); + HostMediaRecorder.Settings settings = recorder.getSettings(); + + mMute = settings.isMute(); + audioQuality.setChannel(settings.getPreviewChannel() == 1 ? + AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO); + audioQuality.setSamplingRate(settings.getPreviewSampleRate()); + audioQuality.setBitRate(settings.getPreviewAudioBitRate()); + audioQuality.setUseAEC(settings.isUseAEC()); + + if (settings.getAudioFilter() != null) { + float coeff = settings.getAudioCoefficient(); + switch (settings.getAudioFilter()) { + case LOW_PASS: + audioQuality.setFilter(new LowPassFilter(audioQuality, coeff)); + break; + case HIGH_PASS: + audioQuality.setFilter(new HighPassFilter(audioQuality, coeff)); + break; + default: + audioQuality.setFilter(null); + break; + } + } else { + audioQuality.setFilter(null); + } + + MicAudioQuality quality = (MicAudioQuality) audioQuality; + if (settings.getPreviewAudioSource() == HostMediaRecorder.AudioSource.APP) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // アプリの録音機能 + MediaProjectionProvider provider = recorder.getMediaProjectionProvider(); + if (provider != null && provider.getMediaProjection() != null) { + MediaProjection mediaProjection = provider.getMediaProjection(); + AudioPlaybackCaptureConfiguration configuration = + new AudioPlaybackCaptureConfiguration.Builder(mediaProjection) + .addMatchingUsage(AudioAttributes.USAGE_GAME) + .addMatchingUsage(AudioAttributes.USAGE_MEDIA) + .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN) + .build(); + quality.setCaptureConfig(configuration); + } + } + quality.setSource(MicAudioQuality.Source.APP); + } + } +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java new file mode 100644 index 0000000000..e730c7592c --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java @@ -0,0 +1,222 @@ +package org.deviceconnect.android.deviceplugin.host.recorder; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractLiveStreamingProvider implements LiveStreamingProvider { + /** + * コンテキスト. + */ + private final Context mContext; + + /** + * プレビュー配信サーバーのリスト. + */ + private final List mLiveStreamingList = new ArrayList<>(); + + /** + * プレビュー配信を行うレコーダ. + */ + private final HostMediaRecorder mRecorder; + + /** + * Notification 表示フラグ. + */ + private boolean mIsRunning; + + /** + * プレビュー配信サーバのイベントを通知するリスナー. + */ + private OnEventListener mOnEventListener; + + public AbstractLiveStreamingProvider(Context context, HostMediaRecorder recorder) { + mContext = context; + mRecorder = recorder; + init(); + } + + @Override + public void addLiveStreaming(LiveStreaming streaming) { + if (streaming != null) { + mLiveStreamingList.add(streaming); + } + } + + @Override + public void removeLiveStreaming(String encoderId) { + for (LiveStreaming streaming : mLiveStreamingList) { + if (streaming.getId().equals(encoderId)) { + streaming.stop(); + streaming.release(); + mLiveStreamingList.remove(streaming); + return; + } + } + } + + @Override + public List getLiveStreamingList() { + return mLiveStreamingList; + } + + @Override + public boolean isRunning() { + return mIsRunning; + } + + @Override + public List start() { + List results = new ArrayList<>(); + + CountDownLatch latch = new CountDownLatch(mLiveStreamingList.size()); + for (LiveStreaming streaming : mLiveStreamingList) { + streaming.setOnEventListener(new Broadcaster.OnEventListener() { + @Override + public void onStarted() { + } + + @Override + public void onStopped() { + } + + @Override + public void onError(Exception e) { + postPreviewError(streaming, e); + } + }); + streaming.start(new LiveStreaming.OnStartCallback() { + @Override + public void onSuccess() { + results.add(streaming); + latch.countDown(); + } + + @Override + public void onFailed(Exception e) { + latch.countDown(); + } + }); + } + + try { + latch.await(5, TimeUnit.SECONDS); + if (results.size() > 0) { + mIsRunning = true; + sendNotification(mRecorder.getId(), mRecorder.getName()); + postPreviewStarted(results); + } + } catch (InterruptedException e) { + // ignore. + } + return results; + } + + @Override + public void stop() { + hideNotification(mRecorder.getId()); + + for (LiveStreaming streaming : getLiveStreamingList()) { + streaming.stop(); + } + + if (mIsRunning) { + mIsRunning = false; + postPreviewStopped(); + } + } + + @Override + public List requestSyncFrame() { + List result = new ArrayList<>(); + for (LiveStreaming streaming : getLiveStreamingList()) { + if (streaming.requestSyncFrame()) { + result.add(streaming); + } + } + return result; + } + + @Override + public void onConfigChange() { + for (LiveStreaming streaming : getLiveStreamingList()) { + streaming.onConfigChange(); + } + } + + @Override + public void setMute(boolean mute) { + for (LiveStreaming streaming : getLiveStreamingList()) { + streaming.setMute(mute); + } + } + + @Override + public void setOnEventListener(OnEventListener listener) { + mOnEventListener = listener; + } + + @Override + public void release() { + for (LiveStreaming streaming : getLiveStreamingList()) { + streaming.release(); + } + } + + /** + * Notification を送信します. + * + * @param id notification を識別する ID + * @param name 名前 + */ + protected abstract void sendNotification(String id, String name); + + /** + * Notification 非表示にします. + * + * @param id notification を識別する ID + */ + protected abstract void hideNotification(String id); + + private void init() { + HostMediaRecorder.Settings settings = getRecorder().getSettings(); + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSetting = settings.getEncoderSetting(encoderId); + if (encoderSetting != null) { + LiveStreaming streaming = createLiveStreaming(encoderId, encoderSetting); + if (streaming != null) { + addLiveStreaming(streaming); + } + } + } + } + + public Context getContext() { + return mContext; + } + + public HostMediaRecorder getRecorder() { + return mRecorder; + } + + protected void postPreviewStarted(List servers) { + if (mOnEventListener != null) { + mOnEventListener.onStarted(servers); + } + } + + protected void postPreviewStopped() { + if (mOnEventListener != null) { + mOnEventListener.onStopped(); + } + } + + protected void postPreviewError(LiveStreaming server, Exception e) { + if (mOnEventListener != null) { + mOnEventListener.onError(server, e); + } + } +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index 2fa0e6ce3f..19242e76b5 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -29,8 +29,8 @@ public abstract class AbstractMJPEGPreviewServer extends AbstractPreviewServer { */ private MJPEGServer mMJPEGServer; - public AbstractMJPEGPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { - super(context, recorder, recorder.getId() + "-mjpeg", useSSL); + public AbstractMJPEGPreviewServer(HostMediaRecorder recorder, String encoderId) { + super(recorder, encoderId); } // PreviewServer @@ -46,17 +46,22 @@ public String getMimeType() { } @Override - public void startWebServer(final OnWebServerStartCallback callback) { + public boolean isRunning() { + return mMJPEGServer != null; + } + + @Override + public void start(final OnStartCallback callback) { if (mMJPEGServer == null) { SSLContext sslContext = getSSLContext(); if (useSSLContext() && sslContext == null) { - callback.onFail(); + callback.onFailed(new RuntimeException("Failed to create a SSLContext.")); return; } mMJPEGServer = new MJPEGServer(); mMJPEGServer.setServerName(SERVER_NAME); - mMJPEGServer.setServerPort(getPort()); + mMJPEGServer.setServerPort(getEncoderSettings().getPort()); mMJPEGServer.setCallback(mCallback); if (useSSLContext()) { mMJPEGServer.setSSLContext(sslContext); @@ -64,27 +69,21 @@ public void startWebServer(final OnWebServerStartCallback callback) { try { mMJPEGServer.start(); } catch (Exception e) { - callback.onFail(); + callback.onFailed(e); return; } } - callback.onStart(getUri()); + callback.onSuccess(); } @Override - public void stopWebServer() { + public void stop() { if (mMJPEGServer != null) { mMJPEGServer.stop(); mMJPEGServer = null; } } - @Override - public boolean requestSyncFrame() { - // 何もしない - return false; - } - @Override public long getBPS() { return mMJPEGServer != null ? mMJPEGServer.getBPS() : 0; @@ -128,7 +127,7 @@ private void restartEncoder() { */ private void setMJPEGQuality(MJPEGQuality quality) { HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); + HostMediaRecorder.EncoderSettings settings = getEncoderSettings(); EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); Size previewSize = settings.getPreviewSize(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java index def4ecb0bf..3c52ee03b7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java @@ -45,11 +45,6 @@ public abstract class AbstractMediaRecorder implements HostMediaRecorder { */ private static final boolean DEBUG = BuildConfig.DEBUG; - public static final String MIME_TYPE_MJPEG = "video/x-mjpeg"; - public static final String MIME_TYPE_RTSP = "video/x-rtp"; - public static final String MIME_TYPE_SRT = "video/MP2T"; - public static final String MIME_TYPE_RTMP = "video/x-rtmp"; - /** * コンテキスト. */ @@ -109,19 +104,19 @@ public AbstractMediaRecorder(Context context, FileManager fileManager, MediaProj public void initialize() { BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); if (broadcasterProvider != null) { - broadcasterProvider.setOnEventListener(new BroadcasterProvider.OnEventListener() { + broadcasterProvider.setOnEventListener(new LiveStreamingProvider.OnEventListener() { @Override - public void onStarted(Broadcaster broadcaster) { - postOnBroadcasterStarted(broadcaster); + public void onStarted(List broadcasters) { + postOnBroadcasterStarted(broadcasters); } @Override - public void onStopped(Broadcaster broadcaster) { - postOnBroadcasterStopped(broadcaster); + public void onStopped() { + postOnBroadcasterStopped(); } @Override - public void onError(Broadcaster broadcaster, Exception e) { + public void onError(LiveStreaming broadcaster, Exception e) { postOnBroadcasterError(broadcaster, e); } }); @@ -129,9 +124,9 @@ public void onError(Broadcaster broadcaster, Exception e) { PreviewServerProvider previewProvider = getServerProvider(); if (previewProvider != null) { - previewProvider.setOnEventListener(new PreviewServerProvider.OnEventListener() { + previewProvider.setOnEventListener(new LiveStreamingProvider.OnEventListener() { @Override - public void onStarted(List servers) { + public void onStarted(List servers) { postOnPreviewStarted(servers); } @@ -141,7 +136,7 @@ public void onStopped() { } @Override - public void onError(PreviewServer server, Exception e) { + public void onError(LiveStreaming server, Exception e) { postOnPreviewError(e); } }); @@ -155,6 +150,16 @@ public void clean() { @Override public void destroy() { + BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); + if (broadcasterProvider != null) { + broadcasterProvider.release(); + } + + PreviewServerProvider previewProvider = getServerProvider(); + if (previewProvider != null) { + previewProvider.release(); + } + mRequestHandler.getLooper().quit(); } @@ -210,13 +215,13 @@ public void onConfigChange() { } @Override - public List startPreview() { + public List startPreview() { PreviewServerProvider provider = getServerProvider(); if (provider == null) { return new ArrayList<>(); } - List servers = provider.startServers(); + List servers = provider.start(); if (!servers.isEmpty()) { provider.setMute(getSettings().isMute()); } @@ -227,7 +232,7 @@ public List startPreview() { public void stopPreview() { PreviewServerProvider provider = getServerProvider(); if (provider != null) { - provider.stopServers(); + provider.stop(); } } @@ -238,28 +243,24 @@ public boolean isBroadcasterRunning() { } @Override - public Broadcaster startBroadcaster(String uri) { - if (uri == null) { - return null; - } - + public List startBroadcaster(String uri) { BroadcasterProvider provider = getBroadcasterProvider(); if (provider == null) { - return null; + return new ArrayList<>(); } - Broadcaster broadcaster = provider.startBroadcaster(uri); - if (broadcaster != null) { + List broadcasters = provider.start(); + for (LiveStreaming broadcaster : broadcasters) { broadcaster.setMute(getSettings().isMute()); } - return broadcaster; + return broadcasters; } @Override public void stopBroadcaster() { BroadcasterProvider provider = getBroadcasterProvider(); if (provider != null) { - provider.stopBroadcaster(); + provider.stop(); } } @@ -269,6 +270,11 @@ public void requestKeyFrame() { if (previewProvider != null) { previewProvider.requestSyncFrame(); } + + BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); + if (broadcasterProvider != null) { + broadcasterProvider.requestSyncFrame(); + } } @Override @@ -298,8 +304,10 @@ public boolean isMute() { public void setSSLContext(SSLContext sslContext) { PreviewServerProvider previewProvider = getServerProvider(); if (previewProvider != null) { - for (PreviewServer server : previewProvider.getServers()) { - server.setSSLContext(sslContext); + for (LiveStreaming server : previewProvider.getLiveStreamingList()) { + if (server instanceof PreviewServer) { + ((PreviewServer) server).setSSLContext(sslContext); + } } } } @@ -311,16 +319,24 @@ public void setOnEventListener(OnEventListener listener) { @Override public long getBPS() { + long bps = 0; + PreviewServerProvider previewProvider = getServerProvider(); - if (previewProvider == null) { - return 0; + if (previewProvider != null) { + List servers = previewProvider.getLiveStreamingList(); + for (LiveStreaming streaming : servers) { + bps += streaming.getBPS(); + } } - long bps = 0; - List servers = previewProvider.getServers(); - for (PreviewServer previewServer : servers) { - bps += previewServer.getBPS(); + BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); + if (broadcasterProvider != null) { + List broadcasters = broadcasterProvider.getLiveStreamingList(); + for (LiveStreaming streaming : broadcasters) { + bps += streaming.getBPS(); + } } + return bps; } @@ -399,22 +415,60 @@ protected void postRequestHandler(Runnable run) { } /** - * Runnable を順番に実行します. + * レコーダの状態を設定します. * - * @param run 実行する Runnable - * @param delay 実行するまでの遅延 + * @param state レコーダの状態 */ - protected void postRequestHandler(Runnable run, long delay) { - mRequestHandler.postDelayed(run, delay); + protected void setState(State state) { + mState = state; } /** - * レコーダの状態を設定します. + * エンコーダを追加します. * - * @param state レコーダの状態 + * @param encoderId エンコーダID + * @param encoderSettings エンコーダの設定 */ - protected void setState(State state) { - mState = state; + public void addEncoder(String encoderId, EncoderSettings encoderSettings) { + getSettings().addEncoder(encoderId); + + switch (encoderSettings.getMimeType()) { + case MJPEG: + case RTSP: + case SRT: { + LiveStreaming streaming = getServerProvider().createLiveStreaming(encoderId, encoderSettings); + if (streaming != null) { + getServerProvider().addLiveStreaming(streaming); + } + } break; + case RTMP: { + LiveStreaming streaming = getBroadcasterProvider().createLiveStreaming(encoderId, encoderSettings); + if (streaming != null) { + getBroadcasterProvider().addLiveStreaming(streaming); + } + } break; + } + } + + /** + * エンコーダを削除します. + * + * @param encoderId エンコーダ + */ + public void removeEncoder(String encoderId) { + EncoderSettings encoderSettings = getSettings().getEncoderSetting(encoderId); + switch (encoderSettings.getMimeType()) { + case MJPEG: + case RTSP: + case SRT: + getServerProvider().removeLiveStreaming(encoderId); + break; + case RTMP: + getBroadcasterProvider().removeLiveStreaming(encoderId); + break; + } + + getSettings().removeEncoder(encoderId); } /** @@ -435,6 +489,12 @@ public FileManager getFileManager() { return mFileManager; } + /** + * 指定されたパーミッションの要求を行います. + * + * @param permissions パーミッションのリスト + * @param callback パーミッション要求の結果を通知するコールバック + */ protected void requestPermission(String[] permissions, PermissionCallback callback) { Handler handler = new Handler(Looper.getMainLooper()); PermissionUtility.requestPermissions(getContext(), handler, permissions, new PermissionUtility.PermissionRequestCallback() { @@ -450,6 +510,11 @@ public void onFail(@NonNull String deniedPermission) { }); } + /** + * MediaProjection の許可要求を行います. + * + * @param callback 要求の結果を通知するコールバック + */ protected void requestMediaProjection(PermissionCallback callback) { getMediaProjectionProvider().requestPermission(new MediaProjectionProvider.Callback() { @Override @@ -665,7 +730,7 @@ protected void postOnConfigChanged() { } } - protected void postOnPreviewStarted(List servers) { + protected void postOnPreviewStarted(List servers) { if (mOnEventListener != null) { mOnEventListener.onPreviewStarted(servers); } @@ -683,19 +748,19 @@ protected void postOnPreviewError(Exception e) { } } - protected void postOnBroadcasterStarted(Broadcaster broadcaster) { + protected void postOnBroadcasterStarted(List broadcasters) { if (mOnEventListener != null) { - mOnEventListener.onBroadcasterStarted(broadcaster); + mOnEventListener.onBroadcasterStarted(broadcasters); } } - protected void postOnBroadcasterStopped(Broadcaster broadcaster) { + protected void postOnBroadcasterStopped() { if (mOnEventListener != null) { - mOnEventListener.onBroadcasterStopped(broadcaster); + mOnEventListener.onBroadcasterStopped(); } } - protected void postOnBroadcasterError(Broadcaster broadcaster, Exception e) { + protected void postOnBroadcasterError(LiveStreaming broadcaster, Exception e) { if (mOnEventListener != null) { mOnEventListener.onBroadcasterError(broadcaster, e); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java index 13897faada..d76ae3ab25 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServer.java @@ -1,424 +1,27 @@ package org.deviceconnect.android.deviceplugin.host.recorder; import android.content.Context; -import android.graphics.Rect; -import android.media.AudioAttributes; -import android.media.AudioFormat; -import android.media.AudioPlaybackCaptureConfiguration; -import android.media.projection.MediaProjection; -import android.os.Build; -import android.util.Size; import org.deviceconnect.android.deviceplugin.host.BuildConfig; -import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.util.MovingRectThread; -import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; -import org.deviceconnect.android.libmedia.streaming.audio.MicAudioQuality; -import org.deviceconnect.android.libmedia.streaming.audio.filter.HighPassFilter; -import org.deviceconnect.android.libmedia.streaming.audio.filter.LowPassFilter; -import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; -import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; -import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; - -import javax.net.ssl.SSLContext; /** * プレビュー配信サーバ. */ -public abstract class AbstractPreviewServer implements PreviewServer, CropInterface { +public abstract class AbstractPreviewServer extends AbstractLiveStreaming implements PreviewServer { protected static final boolean DEBUG = BuildConfig.DEBUG; protected static final String TAG = "host.dplugin"; - /** - * コンテキスト. - */ - private final Context mContext; - - /** - * プレビュー再生を行うレコーダ. - */ - private final HostMediaRecorder mHostMediaRecorder; - - /** - * プレビュー配信サーバのポート番号. - */ - private int mPort; - - /** - * ミュート設定. - */ - private boolean mMute; - - /** - * SSLContext のインスタンス. - */ - private SSLContext mSSLContext; - - /** - * SSL の使用フラグ. - */ - private final boolean mUseSSL; - - /** - * プレビュー配信サーバ名. - */ - private final String mName; - - /** - * 切り抜き範囲移動用スレッド. - */ - private MovingRectThread mMovingRectThread; - - /** - * 切り抜き範囲移動用スレッドからのイベントを受け取るリスナー. - */ - private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(new Rect(rect)); - } - getStreamingSettings().setCropRect(rect); - postOnMoved(rect); - }; - /** * コンストラクタ. * *

* デフォルトでは、ミュート設定は true に設定しています。 - * デフォルトでは、mUseSSL は false に設定します。 *

* - * @param context コンテキスト * @param recorder プレビューで表示するレコーダ - * @param name サーバ名 - */ - public AbstractPreviewServer(Context context, HostMediaRecorder recorder, String name) { - this(context, recorder, name, false); - } - - /** - * コンストラクタ. - * - *

- * デフォルトでは、ミュート設定は true に設定しています。 - *

- * - * @param context コンテキスト - * @param recorder プレビューで表示するレコーダ - * @param name 名前 - * @param useSSL SSL使用フラグ - */ - public AbstractPreviewServer(Context context, HostMediaRecorder recorder, String name, boolean useSSL) { - mContext = context; - mHostMediaRecorder = recorder; - mUseSSL = useSSL; - mMute = true; - mName = name; - startMovingRectThread(); - } - - // Implements PreviewServer methods. - - @Override - public String getName() { - return mName; - } - - @Override - public int getPort() { - return mPort; - } - - @Override - public void setPort(int port) { - mPort = port; - } - - @Override - public void onConfigChange() { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - setVideoQuality(videoQuality); - } - - AudioQuality audioQuality = getAudioQuality(); - if (audioQuality != null) { - setAudioQuality(audioQuality); - - HostMediaRecorder.Settings settings = getRecorder().getSettings(); - setMute(settings.isMute()); - } - } - - @Override - public void setMute(boolean mute) { - mMute = mute; - } - - @Override - public boolean isMuted() { - return mMute; - } - - @Override - public boolean useSSLContext() { - return mUseSSL; - } - - @Override - public void setSSLContext(final SSLContext sslContext) { - mSSLContext = sslContext; - } - - @Override - public SSLContext getSSLContext() { - return mSSLContext; - } - - @Override - public void release() { - stopMovingRectThread(); - } - - // CropInterface implements - - private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); - - @Override - public void moveCropRect(Rect start, Rect end, int duration) { - if (end != null) { - if (getStreamingSettings().getCropRect() == null) { - postOnAdded(end); - } - } else { - postOnRemove(); - } - - if (mMovingRectThread != null) { - mMovingRectThread.move(start, end, duration); - } else { - getStreamingSettings().setCropRect(end); - - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(end); - } - } - } - - @Override - public void setCropRect(Rect rect) { - if (rect != null) { - if (getStreamingSettings().getCropRect() == null) { - postOnAdded(rect); - } - } else { - postOnRemove(); - } - - if (rect == null || mMovingRectThread == null) { - getStreamingSettings().setCropRect(rect); - - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - videoQuality.setCropRect(rect); - } - } else { - mMovingRectThread.set(rect); - } - } - - @Override - public Rect getCropRect() { - return getStreamingSettings().getCropRect(); - } - - @Override - public void addOnEventListener(OnEventListener listener) { - mOnEventListeners.add(listener); - } - - @Override - public void removeOnEventListener(OnEventListener listener) { - mOnEventListeners.remove(listener); - } - - private void postOnAdded(Rect rect) { - for (OnEventListener l : mOnEventListeners.get()) { - l.onAdded(this, rect); - } - } - - private void postOnRemove() { - for (OnEventListener l : mOnEventListeners.get()) { - l.onRemoved(this); - } - } - - private void postOnMoved(Rect rect) { - for (OnEventListener l : mOnEventListeners.get()) { - l.onMoved(this, rect); - } - } - - private void startMovingRectThread() { - if (mMovingRectThread != null) { - return; - } - - mMovingRectThread = new MovingRectThread(); - mMovingRectThread.addOnEventListener(mMovingRectThreadOnEventListener); - mMovingRectThread.start(); - } - - private void stopMovingRectThread() { - if (mMovingRectThread != null) { - mMovingRectThread.stop(); - mMovingRectThread = null; - } - } - - /** - * コンテキストを取得します. - * - * @return コンテキスト + * @param id 名前 */ - public Context getContext() { - return mContext; - } - - /** - * プレビューを表示するレコーダー. - * - * @return レコーダー - */ - public HostMediaRecorder getRecorder() { - return mHostMediaRecorder; - } - - /** - * プレビューの設定を取得します. - * - * @return プレビュー設定 - */ - public HostMediaRecorder.StreamingSettings getStreamingSettings() { - return mHostMediaRecorder.getSettings().getPreviewServer(getName()); - } - - /** - * 映像の設定を取得します. - * - * 映像が使用されていない場合は null を返却すること。 - * - * @return 映像の設定 - */ - protected VideoQuality getVideoQuality() { - return null; - } - - /** - * 音声の設定を取得します. - * - * 音声が使用されていない場合は null を返却すること。 - * - * @return 音声の設定 - */ - protected AudioQuality getAudioQuality() { - return null; - } - - /** - * VideoEncoder の設定に、HostMediaRecorder の設定を反映します. - * - * @param videoQuality 設定を行う VideoEncoder の VideoQuality - */ - public void setVideoQuality(VideoQuality videoQuality) { - HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); - - EGLSurfaceDrawingThread d = recorder.getSurfaceDrawingThread(); - Size previewSize = settings.getPreviewSize(); - int w = d.isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); - int h = d.isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); - videoQuality.setVideoWidth(w); - videoQuality.setVideoHeight(h); - videoQuality.setCropRect(settings.getCropRect()); - videoQuality.setBitRate(settings.getPreviewBitRate()); - videoQuality.setFrameRate(settings.getPreviewMaxFrameRate()); - videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval()); - videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder()); - videoQuality.setIntraRefresh(settings.getIntraRefresh()); - videoQuality.setProfile(settings.getProfile()); - videoQuality.setLevel(settings.getLevel()); - if (settings.getPreviewBitRateMode() != null) { - switch (settings.getPreviewBitRateMode()) { - default: - case VBR: - videoQuality.setBitRateMode(VideoQuality.BitRateMode.VBR); - break; - case CBR: - videoQuality.setBitRateMode(VideoQuality.BitRateMode.CBR); - break; - case CQ: - videoQuality.setBitRateMode(VideoQuality.BitRateMode.CQ); - break; - } - } else { - videoQuality.setBitRateMode(null); - } - } - - /** - * AudioEncoder の設定に、HostMediaRecorder の設定を反映します. - * - * @param audioQuality 設定を行う AudioEncoder の AudioQuality - */ - public void setAudioQuality(AudioQuality audioQuality) { - HostMediaRecorder recorder = getRecorder(); - HostMediaRecorder.Settings settings = recorder.getSettings(); - - mMute = settings.isMute(); - audioQuality.setChannel(settings.getPreviewChannel() == 1 ? - AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO); - audioQuality.setSamplingRate(settings.getPreviewSampleRate()); - audioQuality.setBitRate(settings.getPreviewAudioBitRate()); - audioQuality.setUseAEC(settings.isUseAEC()); - - if (settings.getAudioFilter() != null) { - float coeff = settings.getAudioCoefficient(); - switch (settings.getAudioFilter()) { - case LOW_PASS: - audioQuality.setFilter(new LowPassFilter(audioQuality, coeff)); - break; - case HIGH_PASS: - audioQuality.setFilter(new HighPassFilter(audioQuality, coeff)); - break; - default: - audioQuality.setFilter(null); - break; - } - } else { - audioQuality.setFilter(null); - } - - MicAudioQuality quality = (MicAudioQuality) audioQuality; - if (settings.getPreviewAudioSource() == HostMediaRecorder.AudioSource.APP) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - // アプリの録音機能 - MediaProjectionProvider provider = recorder.getMediaProjectionProvider(); - if (provider != null && provider.getMediaProjection() != null) { - MediaProjection mediaProjection = provider.getMediaProjection(); - AudioPlaybackCaptureConfiguration configuration = - new AudioPlaybackCaptureConfiguration.Builder(mediaProjection) - .addMatchingUsage(AudioAttributes.USAGE_GAME) - .addMatchingUsage(AudioAttributes.USAGE_MEDIA) - .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN) - .build(); - quality.setCaptureConfig(configuration); - } - } - quality.setSource(MicAudioQuality.Source.APP); - } + public AbstractPreviewServer(HostMediaRecorder recorder, String id) { + super(recorder, id); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServerProvider.java index 8a5681367e..eee047fb67 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractPreviewServerProvider.java @@ -15,55 +15,26 @@ import android.content.Intent; import android.os.Build; -import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import org.deviceconnect.android.deviceplugin.host.R; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * Host Device Preview Server. * * @author NTT DOCOMO, INC. */ -public abstract class AbstractPreviewServerProvider implements PreviewServerProvider { - /** - * コンテキスト. - */ - private final Context mContext; - - /** - * プレビュー配信サーバーのリスト. - */ - private final List mPreviewServers = new ArrayList<>(); - - /** - * プレビュー配信を行うレコーダ. - */ - private final HostMediaRecorder mRecorder; - - /** - * Notification 表示フラグ. - */ - private boolean mIsRunning; - - /** - * プレビュー配信サーバのイベントを通知するリスナー. - */ - private OnEventListener mOnEventListener; +public abstract class AbstractPreviewServerProvider extends AbstractLiveStreamingProvider implements PreviewServerProvider { /** * コンストラクタ. * @param context コンテキスト */ public AbstractPreviewServerProvider(final Context context, final HostMediaRecorder recorder) { - mContext = context; - mRecorder = recorder; - mIsRunning = false; + super(context, recorder); } // PreviewServerProvider @@ -71,173 +42,35 @@ public AbstractPreviewServerProvider(final Context context, final HostMediaRecor @Override public List getSupportedMimeType() { List mimeType = new ArrayList<>(); - for (PreviewServer server : getServers()) { + for (LiveStreaming server : getLiveStreamingList()) { mimeType.add(server.getMimeType()); } return mimeType; } @Override - public void addServer(PreviewServer server) { - mPreviewServers.add(server); - } - - @Override - public List getServers() { - return mPreviewServers; - } - - @Override - public PreviewServer getServerByMimeType(String mimeType) { - for (PreviewServer server : getServers()) { - if (server.getMimeType().equalsIgnoreCase(mimeType)) { - return server; - } - } - return null; - } - - @Override - public boolean isRunning() { - return mIsRunning; - } - - @Override - public List startServers() { - List results = new ArrayList<>(); - - CountDownLatch latch = new CountDownLatch(mPreviewServers.size()); - for (PreviewServer server : mPreviewServers) { - server.startWebServer(new PreviewServer.OnWebServerStartCallback() { - @Override - public void onStart(@NonNull String uri) { - results.add(server); - latch.countDown(); - } - - @Override - public void onFail() { - latch.countDown(); - } - }); - } - - try { - latch.await(5, TimeUnit.SECONDS); - if (results.size() > 0) { - mIsRunning = true; - sendNotification(mRecorder.getId(), mRecorder.getName()); - postPreviewStarted(results); - } - } catch (InterruptedException e) { - // ignore. - } - return results; - } - - @Override - public void stopServers() { - hideNotification(mRecorder.getId()); - - for (PreviewServer server : getServers()) { - server.stopWebServer(); - } - - if (mIsRunning) { - mIsRunning = false; - postPreviewStopped(); - } - } - - @Override - public List requestSyncFrame() { - List result = new ArrayList<>(); - for (PreviewServer server : getServers()) { - if (server.requestSyncFrame()) { - result.add(server); - } - } - return result; - } - - @Override - public void onConfigChange() { - for (PreviewServer server : getServers()) { - server.onConfigChange(); - } - } - - @Override - public void setMute(boolean mute) { - for (PreviewServer server : getServers()) { - server.setMute(mute); - } - } - - @Override - public void setOnEventListener(OnEventListener listener) { - mOnEventListener = listener; - } - - protected void postPreviewStarted(List servers) { - if (mOnEventListener != null) { - mOnEventListener.onStarted(servers); - } - } - - protected void postPreviewStopped() { - if (mOnEventListener != null) { - mOnEventListener.onStopped(); - } - } - - protected void postPreviewError(PreviewServer server, Exception e) { - if (mOnEventListener != null) { - mOnEventListener.onError(server, e); - } - } - - /** - * Notification の Id を取得します. - * - * @return Notification の Id - */ - protected int getNotificationId() { - return 100 + mRecorder.getId().hashCode(); - } - - /** - * プレビュー配信サーバ停止用の Notification を削除します. - * - * @param id notification を識別する ID - */ - private void hideNotification(String id) { - NotificationManager manager = (NotificationManager) mContext + protected void hideNotification(String id) { + NotificationManager manager = (NotificationManager) getContext() .getSystemService(Service.NOTIFICATION_SERVICE); if (manager != null) { manager.cancel(id, getNotificationId()); } } - /** - * プレビュー配信サーバ停止用の Notification を送信します. - * - * @param id notification を識別する ID - * @param name 名前 - */ - private void sendNotification(String id, String name) { + @Override + protected void sendNotification(String id, String name) { PendingIntent contentIntent = createPendingIntent(id); Notification notification = createNotification(contentIntent, null, name); - NotificationManager manager = (NotificationManager) mContext + NotificationManager manager = (NotificationManager) getContext() .getSystemService(Service.NOTIFICATION_SERVICE); if (manager != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - String channelId = mContext.getResources().getString(R.string.overlay_preview_channel_id); + String channelId = getContext().getResources().getString(R.string.overlay_preview_channel_id); NotificationChannel channel = new NotificationChannel( channelId, - mContext.getResources().getString(R.string.host_notification_recorder_preview), + getContext().getResources().getString(R.string.host_notification_recorder_preview), NotificationManager.IMPORTANCE_LOW); - channel.setDescription(mContext.getResources().getString(R.string.host_notification_recorder_preview_content)); + channel.setDescription(getContext().getResources().getString(R.string.host_notification_recorder_preview_content)); manager.createNotificationChannel(channel); notification = createNotification(contentIntent, channelId, name); } @@ -245,6 +78,15 @@ private void sendNotification(String id, String name) { } } + /** + * Notification の Id を取得します. + * + * @return Notification の Id + */ + protected int getNotificationId() { + return 100 + getRecorder().getId().hashCode(); + } + /** * Notificationを作成する. * @@ -255,25 +97,25 @@ private void sendNotification(String id, String name) { */ protected Notification createNotification(final PendingIntent pendingIntent, final String channelId, String name) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext.getApplicationContext()); + NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext().getApplicationContext()); builder.setContentIntent(pendingIntent); - builder.setTicker(mContext.getString(R.string.host_notification_recorder_preview_ticker)); + builder.setTicker(getContext().getString(R.string.host_notification_recorder_preview_ticker)); builder.setSmallIcon(R.drawable.dconnect_icon); - builder.setContentTitle(mContext.getString(R.string.host_notification_recorder_preview, name)); - builder.setContentText(mContext.getString(R.string.host_notification_recorder_preview_content)); + builder.setContentTitle(getContext().getString(R.string.host_notification_recorder_preview, name)); + builder.setContentText(getContext().getString(R.string.host_notification_recorder_preview_content)); builder.setWhen(System.currentTimeMillis()); builder.setAutoCancel(true); builder.setOngoing(true); return builder.build(); } else { - Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext()); + Notification.Builder builder = new Notification.Builder(getContext().getApplicationContext()); builder.setContentIntent(pendingIntent); - builder.setTicker(mContext.getString(R.string.overlay_preview_ticker)); + builder.setTicker(getContext().getString(R.string.overlay_preview_ticker)); int iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.dconnect_icon : R.drawable.dconnect_icon_lollipop; builder.setSmallIcon(iconType); - builder.setContentTitle(mContext.getString(R.string.host_notification_recorder_preview, name)); - builder.setContentText(mContext.getString(R.string.host_notification_recorder_preview_content)); + builder.setContentTitle(getContext().getString(R.string.host_notification_recorder_preview, name)); + builder.setContentText(getContext().getString(R.string.host_notification_recorder_preview_content)); builder.setWhen(System.currentTimeMillis()); builder.setAutoCancel(true); builder.setOngoing(true); @@ -295,6 +137,6 @@ private PendingIntent createPendingIntent(String id) { Intent intent = new Intent(); intent.setAction(HostMediaRecorderManager.ACTION_STOP_PREVIEW); intent.putExtra(HostMediaRecorderManager.KEY_RECORDER_ID, id); - return PendingIntent.getBroadcast(mContext, getNotificationId(), intent, 0); + return PendingIntent.getBroadcast(getContext(), getNotificationId(), intent, 0); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java index 9139c93ba5..0ef30a1317 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java @@ -2,13 +2,10 @@ import org.deviceconnect.android.libmedia.streaming.MediaEncoderException; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; -import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; import org.deviceconnect.android.libmedia.streaming.rtmp.RtmpClient; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; -import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; public abstract class AbstractRTMPBroadcaster extends AbstractBroadcaster { - /** * RTMP 配信クライアント. */ @@ -19,8 +16,13 @@ public abstract class AbstractRTMPBroadcaster extends AbstractBroadcaster { */ private Broadcaster.OnEventListener mOnBroadcasterEventListener; - public AbstractRTMPBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public AbstractRTMPBroadcaster(HostMediaRecorder recorder, String encoderId) { + super(recorder, encoderId); + } + + @Override + public String getMimeType() { + return "video/x-rtmp"; } @Override @@ -35,6 +37,12 @@ public boolean isRunning() { @Override public void start(OnStartCallback callback) { + String broadcastURI = getBroadcastURI(); + if (broadcastURI == null) { + callback.onFailed(new RuntimeException("broadcastURI is not set.")); + return; + } + VideoEncoder videoEncoder = createVideoEncoder(); if (videoEncoder != null) { setVideoQuality(videoEncoder.getVideoQuality()); @@ -45,9 +53,9 @@ public void start(OnStartCallback callback) { setAudioQuality(audioEncoder.getAudioQuality()); } - HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); + HostMediaRecorder.EncoderSettings settings = getEncoderSettings(); - mRtmpClient = new RtmpClient(getBroadcastURI()); + mRtmpClient = new RtmpClient(broadcastURI); mRtmpClient.setMaxRetryCount(settings.getRetryCount()); mRtmpClient.setRetryInterval(settings.getRetryInterval()); mRtmpClient.setVideoEncoder(videoEncoder); @@ -109,13 +117,15 @@ public void stop() { @Override public void setMute(boolean mute) { + super.setMute(mute); + if (mRtmpClient != null) { mRtmpClient.setMute(mute); } } @Override - public boolean isMute() { + public boolean isMuted() { return mRtmpClient != null && mRtmpClient.isMute(); } @@ -129,24 +139,12 @@ public void onConfigChange() { } @Override - protected VideoQuality getVideoQuality() { - if (mRtmpClient != null) { - VideoEncoder videoEncoder = mRtmpClient.getVideoEncoder(); - if (videoEncoder != null) { - return videoEncoder.getVideoQuality(); - } - } - return null; + protected VideoEncoder getVideoEncoder() { + return mRtmpClient != null ? mRtmpClient.getVideoEncoder() : null; } @Override - protected AudioQuality getAudioQuality() { - if (mRtmpClient != null) { - AudioEncoder audioEncoder = mRtmpClient.getAudioEncoder(); - if (audioEncoder != null) { - return audioEncoder.getAudioQuality(); - } - } - return null; + protected AudioEncoder getAudioEncoder() { + return mRtmpClient != null ? mRtmpClient.getAudioEncoder() : null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java index 536f7e6119..aa96d6baea 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java @@ -4,16 +4,14 @@ import android.util.Log; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; -import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; import org.deviceconnect.android.libmedia.streaming.rtsp.RtspServer; import org.deviceconnect.android.libmedia.streaming.rtsp.session.RtspSession; import org.deviceconnect.android.libmedia.streaming.rtsp.session.audio.AudioStream; import org.deviceconnect.android.libmedia.streaming.rtsp.session.audio.MicAACLATMStream; import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.VideoStream; -import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; +import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public abstract class AbstractRTSPPreviewServer extends AbstractPreviewServer { - /** * マイムタイプを定義します. */ @@ -29,17 +27,14 @@ public abstract class AbstractRTSPPreviewServer extends AbstractPreviewServer { */ private RtspServer mRtspServer; - public AbstractRTSPPreviewServer(Context context, HostMediaRecorder recorder) { - this(context, recorder, false); - } - public AbstractRTSPPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { - super(context, recorder, recorder.getId() + "-rtsp", useSSL); + public AbstractRTSPPreviewServer(HostMediaRecorder recorder, String encoderId) { + super(recorder, encoderId); } @Override public String getUri() { - return "rtsp://localhost:" + getPort(); + return "rtsp://localhost:" + getEncoderSettings().getPort(); } @Override @@ -48,46 +43,35 @@ public String getMimeType() { } @Override - public void startWebServer(final OnWebServerStartCallback callback) { + public boolean isRunning() { + return mRtspServer != null; + } + + @Override + public void start(final OnStartCallback callback) { if (mRtspServer == null) { mRtspServer = new RtspServer(); mRtspServer.setServerName(SERVER_NAME); - mRtspServer.setServerPort(getPort()); + mRtspServer.setServerPort(getEncoderSettings().getPort()); mRtspServer.setCallback(mCallback); try { mRtspServer.start(); } catch (Exception e) { - callback.onFail(); + callback.onFailed(e); return; } } - callback.onStart(getUri()); + callback.onSuccess(); } @Override - public void stopWebServer() { + public void stop() { if (mRtspServer != null) { mRtspServer.stop(); mRtspServer = null; } } - @Override - public boolean requestSyncFrame() { - RtspServer server = mRtspServer; - if (server != null) { - RtspSession session = server.getRtspSession(); - if (session != null) { - VideoStream videoStream = session.getVideoStream(); - if (videoStream != null) { - videoStream.getVideoEncoder().requestSyncKeyFrame(); - return true; - } - } - } - return false; - } - @Override public long getBPS() { return mRtspServer != null ? mRtspServer.getBPS() : 0; @@ -115,13 +99,13 @@ public void setMute(boolean mute) { } @Override - protected VideoQuality getVideoQuality() { + protected VideoEncoder getVideoEncoder() { if (mRtspServer != null) { RtspSession session = mRtspServer.getRtspSession(); if (session != null) { - VideoStream videoStream = session.getVideoStream(); - if (videoStream != null) { - return videoStream.getVideoEncoder().getVideoQuality(); + VideoStream stream = session.getVideoStream(); + if (stream != null) { + return stream.getVideoEncoder(); } } } @@ -129,13 +113,13 @@ protected VideoQuality getVideoQuality() { } @Override - protected AudioQuality getAudioQuality() { + protected AudioEncoder getAudioEncoder() { if (mRtspServer != null) { RtspSession session = mRtspServer.getRtspSession(); if (session != null) { - AudioStream audioStream = session.getAudioStream(); - if (audioStream != null) { - return audioStream.getAudioEncoder().getAudioQuality(); + AudioStream stream = session.getAudioStream(); + if (stream != null) { + return stream.getAudioEncoder(); } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java index 0b652d5235..f7ee58772d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java @@ -2,10 +2,7 @@ import org.deviceconnect.android.libmedia.streaming.MediaEncoderException; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; -import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; -import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; -import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; import org.deviceconnect.android.libsrt.broadcast.SRTClient; public abstract class AbstractSRTBroadcaster extends AbstractBroadcaster { @@ -19,8 +16,13 @@ public abstract class AbstractSRTBroadcaster extends AbstractBroadcaster { */ private Broadcaster.OnEventListener mOnBroadcasterEventListener; - public AbstractSRTBroadcaster(HostMediaRecorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public AbstractSRTBroadcaster(HostMediaRecorder recorder, String id) { + super(recorder, id); + } + + @Override + public String getMimeType() { + return "video/MP2T"; } @Override @@ -35,6 +37,12 @@ public boolean isRunning() { @Override public void start(OnStartCallback callback) { + String broadcastURI = getBroadcastURI(); + if (broadcastURI == null) { + callback.onFailed(new RuntimeException("broadcastURI is not set.")); + return; + } + VideoEncoder videoEncoder = createVideoEncoder(); if (videoEncoder != null) { setVideoQuality(videoEncoder.getVideoQuality()); @@ -45,7 +53,7 @@ public void start(OnStartCallback callback) { setAudioQuality(audioEncoder.getAudioQuality()); } - HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); + HostMediaRecorder.EncoderSettings settings = getEncoderSettings(); mSrtClient = new SRTClient(getBroadcastURI()); mSrtClient.setMaxRetryCount(settings.getRetryCount()); @@ -109,13 +117,15 @@ public void stop() { @Override public void setMute(boolean mute) { + super.setMute(mute); + if (mSrtClient != null) { mSrtClient.setMute(mute); } } @Override - public boolean isMute() { + public boolean isMuted() { return mSrtClient != null && mSrtClient.isMute(); } @@ -129,24 +139,12 @@ public void onConfigChange() { } @Override - protected VideoQuality getVideoQuality() { - if (mSrtClient != null) { - VideoEncoder videoEncoder = mSrtClient.getVideoEncoder(); - if (videoEncoder != null) { - return videoEncoder.getVideoQuality(); - } - } - return null; + protected VideoEncoder getVideoEncoder() { + return mSrtClient != null ? mSrtClient.getVideoEncoder() : null; } @Override - protected AudioQuality getAudioQuality() { - if (mSrtClient != null) { - AudioEncoder audioEncoder = mSrtClient.getAudioEncoder(); - if (audioEncoder != null) { - return audioEncoder.getAudioQuality(); - } - } - return null; + protected AudioEncoder getAudioEncoder() { + return mSrtClient != null ? mSrtClient.getAudioEncoder() : null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index e0d1e787c7..2deaf4cea1 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -5,10 +5,8 @@ import org.deviceconnect.android.deviceplugin.host.BuildConfig; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; -import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality; import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; -import org.deviceconnect.android.libmedia.streaming.video.VideoQuality; import org.deviceconnect.android.libsrt.server.SRTServer; import org.deviceconnect.android.libsrt.server.SRTSession; @@ -26,17 +24,13 @@ public abstract class AbstractSRTPreviewServer extends AbstractPreviewServer { */ private SRTServer mSRTServer; - public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder) { - this(context, recorder, false); - } - - public AbstractSRTPreviewServer(Context context, HostMediaRecorder recorder, boolean useSSL) { - super(context, recorder, recorder.getId() + "-srt", useSSL); + public AbstractSRTPreviewServer(HostMediaRecorder recorder, String encoderId) { + super(recorder, encoderId); } @Override public String getUri() { - return "srt://localhost:" + getPort(); + return "srt://localhost:" + getEncoderSettings().getPort(); } @Override @@ -45,48 +39,37 @@ public String getMimeType() { } @Override - public void startWebServer(final OnWebServerStartCallback callback) { + public boolean isRunning() { + return mSRTServer != null; + } + + @Override + public void start(final OnStartCallback callback) { if (mSRTServer == null) { try { - HostMediaRecorder.StreamingSettings settings = getStreamingSettings(); - mSRTServer = new SRTServer(getPort()); + HostMediaRecorder.EncoderSettings settings = getEncoderSettings(); + mSRTServer = new SRTServer(getEncoderSettings().getPort()); mSRTServer.setStatsInterval(BuildConfig.STATS_INTERVAL); mSRTServer.setShowStats(DEBUG); mSRTServer.setCallback(mCallback); mSRTServer.setSocketOptions(settings.getSRTSocketOptions()); mSRTServer.start(); } catch (Exception e) { - callback.onFail(); + callback.onFailed(e); return; } } - callback.onStart(getUri()); + callback.onSuccess(); } @Override - public void stopWebServer() { + public void stop() { if (mSRTServer != null) { mSRTServer.stop(); mSRTServer = null; } } - @Override - public boolean requestSyncFrame() { - SRTServer server = mSRTServer; - if (server != null) { - SRTSession session = server.getSRTSession(); - if (session != null) { - VideoEncoder videoEncoder = session.getVideoEncoder(); - if (videoEncoder != null) { - videoEncoder.requestSyncKeyFrame(); - return true; - } - } - } - return false; - } - @Override public long getBPS() { // TODO @@ -115,28 +98,22 @@ public void setMute(boolean mute) { } @Override - protected VideoQuality getVideoQuality() { + protected VideoEncoder getVideoEncoder() { if (mSRTServer != null) { SRTSession session = mSRTServer.getSRTSession(); if (session != null) { - VideoEncoder videoEncoder = session.getVideoEncoder(); - if (videoEncoder != null) { - return videoEncoder.getVideoQuality(); - } + return session.getVideoEncoder(); } } return null; } @Override - protected AudioQuality getAudioQuality() { + protected AudioEncoder getAudioEncoder() { if (mSRTServer != null) { SRTSession session = mSRTServer.getSRTSession(); if (session != null) { - AudioEncoder audioEncoder = session.getAudioEncoder(); - if (audioEncoder != null) { - return audioEncoder.getAudioQuality(); - } + return session.getAudioEncoder(); } } return null; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java index 4a53bda9bd..08aae3a90d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/Broadcaster.java @@ -1,112 +1,10 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -public interface Broadcaster { - /** - * 名前を取得します. - * - * @return 名前 - */ - String getName(); - - /** - * マイムタイプを取得します. - * - * @return マイムタイプ - */ - String getMimeType(); - +public interface Broadcaster extends LiveStreaming { /** * ブロードキャスト先の URI を取得します. * * @return ブロードキャスト先の URI */ String getBroadcastURI(); - - /** - * ブロードキャストのイベントを通知するリスナーを設定します. - * - * @param listener リスナー - */ - void setOnEventListener(OnEventListener listener); - - /** - * ブロードキャスト中か確認します. - * - * @return ブロードキャスト中の場合は true、それ以外は false - */ - boolean isRunning(); - - /** - * ブロードキャストを開始します. - */ - void start(OnStartCallback callback); - - /** - * ブロードキャストを停止します. - */ - void stop(); - - /** - * ミュート設定を行います. - * - * @param mute ミュートにする場合にはtrue、それ以外はfalse - */ - void setMute(boolean mute); - - /** - * ミュート設定を取得します. - * - * @return ミュートの場合はtrue、それ以外はfalse - */ - boolean isMute(); - - /** - * 設定が変更されたことを通知します. - */ - void onConfigChange(); - - /** - * 配信先を解放します. - */ - void release(); - - /** - * ブロードキャストの開始結果を通知するコールバック. - */ - interface OnStartCallback { - /** - * ブロードキャストに成功したことを通知します. - */ - void onSuccess(); - - /** - * ブロードキャストに失敗したことを通知します. - * - * @param e 失敗原因の例外 - */ - void onFailed(Exception e); - } - - /** - * ブロードキャストのイベントを通知するリスナー. - */ - interface OnEventListener { - - /** - * ブロードキャストが開始されたことを通知します. - */ - void onStarted(); - - /** - * ブロードキャストが停止されたことを通知します. - */ - void onStopped(); - - /** - * ブロードキャストでエラーが発生したことを通知します. - * - * @param e エラー原因の例外 - */ - void onError(Exception e); - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/BroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/BroadcasterProvider.java index 675653085e..a409614bdf 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/BroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/BroadcasterProvider.java @@ -1,73 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -public interface BroadcasterProvider { - /** - * ブロードキャスターのリストを取得します. - * - * @return ブロードキャスターのリスト - */ - Broadcaster getBroadcaster(); +public interface BroadcasterProvider extends LiveStreamingProvider { - /** - * ブロードキャスト中か確認します. - * - * @return ブロードキャスト中は true、それ以外は false - */ - boolean isRunning(); - - /** - * ブロードキャスターを開始します. - * - * @param broadcastURI 配信先の URI - */ - Broadcaster startBroadcaster(String broadcastURI); - - /** - * ブロードキャスターを停止します. - */ - void stopBroadcaster(); - - /** - * 設定が変更されたことを通知します. - */ - void onConfigChange(); - - /** - * Recorder をミュート状態にする. - */ - void setMute(boolean mute); - - /** - * BroadcasterProvider で発生したイベントを通知するリスナーを設定します. - * - * @param listener リスナー - */ - void setOnEventListener(OnEventListener listener); - - /** - * イベントを通知するリスナー. - */ - interface OnEventListener { - /** - * Broadcaster が開始されたことを通知します. - * - * @param broadcaster 開始した Broadcaster - */ - void onStarted(Broadcaster broadcaster); - - /** - * Broadcaster が停止されたことを通知します. - * - * @param broadcaster 停止した Broadcaster - */ - void onStopped(Broadcaster broadcaster); - - /** - * Broadcaster でエラーが発生したことを通知します. - * - * @param broadcaster エラーが発生した Broadcaster - * @param e エラー原因の例外 - */ - void onError(Broadcaster broadcaster, Exception e); - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java index 8202ec4584..4ca92972d1 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/CropInterface.java @@ -3,6 +3,14 @@ import android.graphics.Rect; public interface CropInterface { + + /** + * PreviewSurfaceView で枠を表示する時に表示する名前を取得します. + * + * @return 名前 + */ + String getName(); + /** * start で指定された矩形から end で指定された矩形に移動します. * diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index 26e2e8cb36..49d36b9e37 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -9,6 +9,7 @@ import android.content.Context; import android.graphics.Rect; import android.os.Build; +import android.util.Log; import android.util.Range; import android.util.Size; @@ -127,7 +128,7 @@ public interface HostMediaRecorder extends HostDevicePhotoRecorder, HostDeviceSt * * @return 開始したプレビュー配信サーバのリスト */ - List startPreview(); + List startPreview(); /** * プレビュー配信サーバを停止します. @@ -147,7 +148,7 @@ public interface HostMediaRecorder extends HostDevicePhotoRecorder, HostDeviceSt * @param broadcastURI ブロードキャスト先のURI * @return ブロードキャストしているクラス */ - Broadcaster startBroadcaster(String broadcastURI); + List startBroadcaster(String broadcastURI); /** * ブロードキャストを停止します. @@ -248,7 +249,8 @@ enum MimeType { MJPEG("video/x-mjpeg"), RTSP("video/x-rtp"), SRT("video/MP2T"), - RTMP("video/x-rtmp"); + RTMP("video/x-rtmp"), + UNKNOWN(""); private final String mValue; @@ -266,14 +268,15 @@ public static MimeType typeOf(String mimeType) { return type; } } - return null; + return UNKNOWN; } } enum AudioSource { DEFAULT("default"), MIC("mic"), - APP("app"); + APP("app"), + NONE("none"); private final String mSource; @@ -291,18 +294,18 @@ public static AudioSource typeOf(String source) { return audioSource; } } - return null; + return NONE; } } - enum VideoEncoderName { + enum VideoCodec { H264("h264", "video/avc"), H265("h265", "video/hevc"); private final String mName; private final String mMimeType; - VideoEncoderName(String name, String mimeType) { + VideoCodec(String name, String mimeType) { mName = name; mMimeType = mimeType; } @@ -315,8 +318,8 @@ public String getMimeType() { return mMimeType; } - public static VideoEncoderName nameOf(String name) { - for (VideoEncoderName encoder : values()) { + public static VideoCodec nameOf(String name) { + for (VideoCodec encoder : values()) { if (encoder.getName().equalsIgnoreCase(name)) { return encoder; } @@ -442,7 +445,7 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host * * @param servers 開始したプレビュー配信サーバ */ - void onPreviewStarted(List servers); + void onPreviewStarted(List servers); /** * プレビュー配信を停止した時に呼び出されます. @@ -459,16 +462,14 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host /** * ブロードキャストを開始した時に呼び出されます. * - * @param broadcaster 開始したブロードキャスト + * @param broadcasters 開始したブロードキャスト */ - void onBroadcasterStarted(Broadcaster broadcaster); + void onBroadcasterStarted(List broadcasters); /** * ブロードキャストを停止した時に呼び出されます. - * - * @param broadcaster 停止したブロードキャスト */ - void onBroadcasterStopped(Broadcaster broadcaster); + void onBroadcasterStopped(); /** * ブロードキャストでエラーが発生したときに呼び出されます. @@ -476,7 +477,7 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host * @param broadcaster エラーが発生した Broadcaster * @param e エラー原因の例外 */ - void onBroadcasterError(Broadcaster broadcaster, Exception e); + void onBroadcasterError(LiveStreaming broadcaster, Exception e); /** * レコーダで発生したエラーを通知します. @@ -486,16 +487,16 @@ interface OnEventListener extends HostDeviceStreamRecorder.OnEventListener, Host void onError(Exception e); } - class StreamingSettings { + class EncoderSettings { private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; - private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; + private static final String DEFAULT_PREVIEW_ENCODER = VideoCodec.H264.mName; private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; private final PropertyUtil mProperty; private final Context mContext; - public StreamingSettings(Context context, String name) { + public EncoderSettings(Context context, String name) { mContext = context; mProperty = new PropertyUtil(context, name); } @@ -530,8 +531,8 @@ public void setName(String name) { * * @return マイムタイプ */ - public String getMimeType() { - return mProperty.getString("mimeType", null); + public MimeType getMimeType() { + return MimeType.typeOf(mProperty.getString("mime_type", null)); } /** @@ -539,8 +540,11 @@ public String getMimeType() { * * @param mimeType マイムタイプ */ - public void setMimeType(String mimeType) { - mProperty.put("mimeType", mimeType); + public void setMimeType(MimeType mimeType) { + if (mimeType == null) { + throw new IllegalArgumentException("mimeType is null."); + } + mProperty.put("mime_type", mimeType.getValue()); } /** @@ -561,6 +565,24 @@ public void setPort(int port) { mProperty.put("port", port); } + /** + * SSL を使用するか確認します. + * + * @return SSL を使用する場合はtrue、それ以外はfalse + */ + public boolean isUseSSL() { + return mProperty.getBoolean("use_ssl", false); + } + + /** + * SSL 使用フラグを設定します. + * + * @param useSSL SSL 使用フラグ + */ + public void setUseSSL(boolean useSSL) { + mProperty.put("use_ssl", useSSL); + } + //// MediaCodec /** @@ -586,8 +608,8 @@ public void setPreviewSize(Size previewSize) { * * @return エンコード名 */ - public VideoEncoderName getPreviewEncoderName() { - return VideoEncoderName.nameOf(getPreviewEncoder()); + public VideoCodec getPreviewEncoderName() { + return VideoCodec.nameOf(getPreviewEncoder()); } /** @@ -862,6 +884,28 @@ public void setPreviewQuality(int quality) { mProperty.put("preview_jpeg_quality", quality); } + /** + * サポートしているエンコーダの解像度の最大値を取得します. + * + * @param mimeType マイムタイプ + * @return サポートしている解像度の最大値 + */ + public Size getSupportedPreviewSize(String mimeType) { + return CapabilityUtil.getSupportedMaxSize(mimeType); + } + + /** + * 指定されたサイズがサポートされているか確認します. + * + * @param mimeType マイムタイプ + * @param size 解像度 + * @return サポートされている場合はtrue、それ以外はfalse + */ + public boolean isSupportedPreviewSize(String mimeType, Size size) { + Size maxSize = getSupportedPreviewSize(mimeType); + return maxSize != null && (size.getWidth() <= maxSize.getWidth() && size.getHeight() <= maxSize.getHeight()); + } + /** * サポートしているエンコーダのリストを取得します. * @@ -870,7 +914,7 @@ public void setPreviewQuality(int quality) { public List getSupportedVideoEncoders() { List list = new ArrayList<>(); List supported = CapabilityUtil.getSupportedVideoEncoders(); - for (VideoEncoderName encoderName : VideoEncoderName.values()) { + for (VideoCodec encoderName : VideoCodec.values()) { if (supported.contains(encoderName.getMimeType())) { list.add(encoderName.getName()); } @@ -884,7 +928,7 @@ public List getSupportedVideoEncoders() { * @return サポートしているプロファイル・レベルの一覧 */ public List getSupportedProfileLevel() { - VideoEncoderName encoderName = getPreviewEncoderName(); + VideoCodec encoderName = getPreviewEncoderName(); return CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); } @@ -1068,11 +1112,6 @@ abstract class Settings { private final PropertyUtil mProperty; private final Context mContext; - private static final int DEFAULT_PREVIEW_MAX_FRAME_RATE = 30; - private static final int DEFAULT_PREVIEW_BITRATE = 2 * 1024 * 1024; - private static final String DEFAULT_PREVIEW_ENCODER = VideoEncoderName.H264.mName; - private static final int DEFAULT_PREVIEW_KEY_FRAME_INTERVAL = 1; - public Settings(Context context, HostMediaRecorder recorder) { mContext = context; mProperty = new PropertyUtil(context, recorder.getId()); @@ -1098,69 +1137,69 @@ public void finishInitialization() { * 保存データを初期化します. */ public void clear() { - for (String name : getPreviewServerList()) { - getPreviewServer(name).clear(); - } - for (String name : getBroadcasterList()) { - getBroadcaster(name).clear(); + for (String encoderId : getEncoderIdList()) { + getEncoderSetting(encoderId).clear(); } mProperty.clear(); } - public List getPreviewServerList() { - return mProperty.getArrayString("preview_server_list"); - } - - public StreamingSettings getPreviewServer(String name) { - List previewServerList = getPreviewServerList(); - if (previewServerList.contains(name)) { - return new StreamingSettings(mContext, name); + /** + * 指定された ID に対応するエンコーダの設定を取得します. + * + * @param encoderId 配信先の設定の ID + * @return エンコーダ設定 + */ + public EncoderSettings getEncoderSetting(String encoderId) { + List encoderSettingList = getEncoderIdList(); + if (encoderSettingList.contains(encoderId)) { + return new EncoderSettings(mContext, encoderId); } return null; } - public void addPreviewServer(String name) { - List previewServerList = getPreviewServerList(); - if (previewServerList.contains(name)) { - return; - } - previewServerList.add(name); - mProperty.put("preview_server_list", previewServerList); - } - - public void removePreviewServer(String name) { - List previewServerList = getPreviewServerList(); - previewServerList.remove(name); - mProperty.put("preview_server_list", previewServerList); - } - - public List getBroadcasterList() { - return mProperty.getArrayString("broadcaster_list"); + /** + * エンコーダの ID リストを取得します. + * + * @return エンコーダリスト + */ + public List getEncoderIdList() { + return mProperty.getArrayString("encoder_id_list"); } - public StreamingSettings getBroadcaster(String name) { - List broadcasterList = getBroadcasterList(); - if (broadcasterList.contains(name)) { - return new StreamingSettings(mContext, name); + /** + * エンコーダを追加します. + * + * 既に同じ ID が存在する場合には何も処理を行いません。 + * + * @param encoderId 追加するエンコーダ ID + */ + public void addEncoder(String encoderId) { + List encoderList = getEncoderIdList(); + if (encoderList.contains(encoderId)) { + return; } - return null; + encoderList.add(encoderId); + mProperty.put("encoder_id_list", encoderList); } - public void addBroadcaster(String name) { - List broadcasterList = getBroadcasterList(); - if (broadcasterList.contains(name)) { - return; + /** + * エンコーダを削除します. + * + * @param encoderId 削除するエンコーダ ID + */ + public void removeEncoder(String encoderId) { + EncoderSettings encoderSettings = getEncoderSetting(encoderId); + if (encoderId != null) { + encoderSettings.clear(); } - broadcasterList.add(name); - mProperty.put("broadcaster_list", broadcasterList); - } - public void removeBroadcaster(String name) { - List broadcasterList = getBroadcasterList(); - broadcasterList.remove(name); - mProperty.put("broadcaster_list", broadcasterList); - } + List encoderList = getEncoderIdList(); + Log.e("ABC", "#$$$$ " + encoderList); + encoderList.remove(encoderId); + Log.e("ABC", "#$$$$ end: " + encoderList); + mProperty.put("encoder_id_list", encoderList); + } // カメラ設定 @@ -1181,13 +1220,13 @@ public Size getPictureSize() { * @param pictureSize 写真サイズ */ public void setPictureSize(Size pictureSize) { + if (pictureSize == null) { + throw new IllegalArgumentException("pictureSize is not set."); + } if (!isSupportedPictureSize(pictureSize)) { throw new IllegalArgumentException("pictureSize is not supported."); } - mProperty.put( - "picture_size_width", - "picture_size_height", - pictureSize); + mProperty.put("picture_size_width", "picture_size_height", pictureSize); } /** @@ -1207,31 +1246,80 @@ public Size getPreviewSize() { * @param previewSize プレビューサイズ */ public void setPreviewSize(Size previewSize) { + if (previewSize == null) { + throw new IllegalArgumentException("previewSize is not set."); + } if (!isSupportedPreviewSize(previewSize)) { throw new IllegalArgumentException("previewSize is not supported."); } mProperty.put("preview_size_width", "preview_size_height", previewSize); } + /** + * 設定が近い fps を取得します. + * + * @param frameRate フレームレート + * @return fps + */ + public Range getPreviewFpsFromFrameRate(int frameRate) { + List> fpsList = getSupportedFps(); + for (Range fps : fpsList) { + if (frameRate == fps.getLower() && frameRate == fps.getUpper()) { + return fps; + } + } + Range t = null; + int diff = Integer.MAX_VALUE; + for (Range fps : fpsList) { + if (fps.getLower() < frameRate && frameRate <= fps.getUpper()) { + if (t != null) { + int a = fps.getUpper() - frameRate; + int b = fps.getLower() - frameRate; + int l = a * a + b * b; + if (l < diff) { + diff = l; + t = fps; + } + } else { + t = fps; + } + } + } + return t; + } + /** * フレームレートを取得します. * * @return フレームレート */ - public int getPreviewMaxFrameRate() { - return mProperty.getInteger("preview_framerate", DEFAULT_PREVIEW_MAX_FRAME_RATE); + public Range getPreviewFps() { + Integer lower = mProperty.getInteger("preview_fps_lower", null); + Integer upper = mProperty.getInteger("preview_fps_upper", null); + if (lower != null && upper != null) { + return new Range<>(lower, upper); + } + return null; } /** * フレームレートを設定します. * - * @param previewMaxFrameRate フレームレート + * サポートされていないフレームレート場合は IllegalArgumentException を発生させます。 + * + * @param fps フレームレート */ - public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) { - if (previewMaxFrameRate <= 0) { - throw new IllegalArgumentException("previewMaxFrameRate is zero or negative."); + public void setPreviewFps(Range fps) { + if (fps == null) { + mProperty.remove("preview_fps_lower"); + mProperty.remove("preview_fps_upper"); + } else { + if (!isSupportedFps(fps)) { + throw new IllegalArgumentException("previewFps is not supported."); + } + mProperty.put("preview_fps_lower", fps.getLower()); + mProperty.put("preview_fps_upper", fps.getUpper()); } - mProperty.put("preview_framerate", previewMaxFrameRate); } /** @@ -1462,10 +1550,23 @@ public void setNoiseReduction(Integer mode) { } } + /** + * 自動露出モードを取得します. + * + * @return 自動露出モード + */ public Integer getAutoExposureMode() { return mProperty.getInteger("preview_auto_exposure_mode", null); } + /** + * 自動露出モードを設定します. + * + * mode に null が指定された場合には設定を削除します。 + * サポートされていない値が指定された場合には、IllegalArgumentException が発生します。 + * + * @param mode 自動露出モード + */ public void setAutoExposureMode(Integer mode) { if (mode == null) { mProperty.remove("preview_auto_exposure_mode"); @@ -1477,10 +1578,23 @@ public void setAutoExposureMode(Integer mode) { } } + /** + * 露出時間を取得します. + * + * @return 露出時間 + */ public Long getSensorExposureTime() { return mProperty.getLong("preview_sensor_exposure_time", null); } + /** + * 露出時間を設定します. + * + * mode に null が指定された場合には設定を削除します。 + * サポートされていない値が指定された場合には、IllegalArgumentException が発生します。 + * + * @param exposureTime 露出時間 + */ public void setSensorExposureTime(Long exposureTime) { if (exposureTime == null) { mProperty.remove("preview_sensor_exposure_time"); @@ -1492,10 +1606,23 @@ public void setSensorExposureTime(Long exposureTime) { } } + /** + * ISO 感度を取得します. + * + * @return ISO 感度 + */ public Integer getSensorSensitivity() { return mProperty.getInteger("preview_sensor_sensitivity", null); } + /** + * ISO 感度を設定します. + * + * mode に null が指定された場合には設定を削除します。 + * サポートされていない値が指定された場合には、IllegalArgumentException が発生します。 + * + * @param sensitivity ISO 感度 + */ public void setSensorSensitivity(Integer sensitivity) { if (sensitivity == null) { mProperty.remove("preview_sensor_sensitivity"); @@ -1561,7 +1688,6 @@ public void setPreviewWhiteBalanceTemperature(Integer temperature) { } } - /// サポートしているデータサイズ /** @@ -1693,7 +1819,7 @@ public Range getSupportedWhiteBalanceTemperature() { public List getSupportedVideoEncoders() { List list = new ArrayList<>(); List supported = CapabilityUtil.getSupportedVideoEncoders(); - for (VideoEncoderName encoderName : VideoEncoderName.values()) { + for (VideoCodec encoderName : VideoCodec.values()) { if (supported.contains(encoderName.getMimeType())) { list.add(encoderName.getName()); } @@ -1706,22 +1832,50 @@ public List getSupportedVideoEncoders() { * * @return サポートしているプロファイル・レベルの一覧 */ - public List getSupportedProfileLevel(VideoEncoderName encoderName) { + public List getSupportedProfileLevel(VideoCodec encoderName) { return CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType()); } + /** + * サポートしている手ぶれ補正のリストを取得します. + * + * サポートしていない場合は、空のリストを返却します。 + * + * @return サポートしている手ぶれ補正のリスト + */ public List getSupportedStabilizationList() { return new ArrayList<>(); } + /** + * サポートしている光学手ぶれ補正のリストを取得します. + * + * サポートしていない場合は、空のリストを返却します。 + * + * @return サポートしている光学手ぶれ補正のリスト + */ public List getSupportedOpticalStabilizationList() { return new ArrayList<>(); } + /** + * サポートしているノイズ低減モートのリストを取得します. + * + * サポートしていない場合は、空のリストを返却します。 + * + * @return サポートしているノイズ低減モートのリスト + */ public List getSupportedNoiseReductionList() { return new ArrayList<>(); } + /** + * サポートしている焦点距離のリストを取得します. + * + * サポートしていない場合は、空のリストを返却します。 + * + * @return サポートしている焦点距離のリスト + */ public List getSupportedFocalLengthList() { return new ArrayList<>(); } @@ -1938,12 +2092,13 @@ public boolean isSupportedVideoEncoder(String encoder) { /** * 指定されたプロファイルとレベルがサポートされているか確認します. * + * @param codec コーデック * @param profile プロファイル * @param level レベル * @return サポートされている場合はtrue、それ以外はfalse */ - public boolean isSupportedProfileLevel(VideoEncoderName encoderName, int profile, int level) { - List list = getSupportedProfileLevel(encoderName); + public boolean isSupportedProfileLevel(VideoCodec codec, int profile, int level) { + List list = getSupportedProfileLevel(codec); if (list != null) { for (ProfileLevel pl : list) { if (profile == pl.getProfile() && level == pl.getLevel()) { @@ -2045,7 +2200,7 @@ public boolean isSupportedFocalLength(Float focalLength) { * @return プレビュー音声が有効の場合はtrue、それ以外はfalse */ public boolean isAudioEnabled() { - return getPreviewAudioSource() != null; + return getPreviewAudioSource() != AudioSource.NONE; } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java index 258ca86af8..d34b9c809b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java @@ -171,7 +171,7 @@ public void onConfigChanged() { } @Override - public void onPreviewStarted(List servers) { + public void onPreviewStarted(List servers) { postOnPreviewStarted(recorder, servers); } @@ -186,17 +186,17 @@ public void onPreviewError(Exception e) { } @Override - public void onBroadcasterStarted(Broadcaster broadcaster) { - postOnBroadcasterStarted(recorder, broadcaster); + public void onBroadcasterStarted(List broadcasters) { + postOnBroadcasterStarted(recorder, broadcasters); } @Override - public void onBroadcasterStopped(Broadcaster broadcaster) { - postOnBroadcasterStopped(recorder, broadcaster); + public void onBroadcasterStopped() { + postOnBroadcasterStopped(recorder); } @Override - public void onBroadcasterError(Broadcaster broadcaster, Exception e) { + public void onBroadcasterError(LiveStreaming broadcaster, Exception e) { postOnBroadcasterError(recorder, broadcaster, e); } @@ -584,7 +584,7 @@ private void postOnConfigChanged(HostMediaRecorder recorder) { } } - private void postOnPreviewStarted(HostMediaRecorder recorder, List servers) { + private void postOnPreviewStarted(HostMediaRecorder recorder, List servers) { for (OnEventListener l : mOnEventListeners) { l.onPreviewStarted(recorder, servers); } @@ -602,19 +602,19 @@ private void postOnPreviewError(HostMediaRecorder recorder, Exception e) { } } - private void postOnBroadcasterStarted(HostMediaRecorder recorder, Broadcaster broadcaster) { + private void postOnBroadcasterStarted(HostMediaRecorder recorder, List broadcasters) { for (OnEventListener l : mOnEventListeners) { - l.onBroadcasterStarted(recorder, broadcaster); + l.onBroadcasterStarted(recorder, broadcasters); } } - private void postOnBroadcasterStopped(HostMediaRecorder recorder, Broadcaster broadcaster) { + private void postOnBroadcasterStopped(HostMediaRecorder recorder) { for (OnEventListener l : mOnEventListeners) { - l.onBroadcasterStopped(recorder, broadcaster); + l.onBroadcasterStopped(recorder); } } - private void postOnBroadcasterError(HostMediaRecorder recorder, Broadcaster broadcaster, Exception e) { + private void postOnBroadcasterError(HostMediaRecorder recorder, LiveStreaming broadcaster, Exception e) { for (OnEventListener l : mOnEventListeners) { l.onBroadcasterError(recorder, broadcaster, e); } @@ -660,13 +660,13 @@ public interface OnEventListener { void onMuteChanged(HostMediaRecorder recorder, boolean mute); void onConfigChanged(HostMediaRecorder recorder); - void onPreviewStarted(HostMediaRecorder recorder, List servers); + void onPreviewStarted(HostMediaRecorder recorder, List servers); void onPreviewStopped(HostMediaRecorder recorder); void onPreviewError(HostMediaRecorder recorder, Exception e); - void onBroadcasterStarted(HostMediaRecorder recorder, Broadcaster broadcaster); - void onBroadcasterStopped(HostMediaRecorder recorder, Broadcaster broadcaster); - void onBroadcasterError(HostMediaRecorder recorder, Broadcaster broadcaster, Exception e); + void onBroadcasterStarted(HostMediaRecorder recorder, List broadcasters); + void onBroadcasterStopped(HostMediaRecorder recorder); + void onBroadcasterError(HostMediaRecorder recorder, LiveStreaming broadcaster, Exception e); void onTakePhoto(HostMediaRecorder recorder, String uri, String filePath, String mimeType); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java new file mode 100644 index 0000000000..c56d2db5a2 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java @@ -0,0 +1,148 @@ +package org.deviceconnect.android.deviceplugin.host.recorder; + +import javax.net.ssl.SSLContext; + +public interface LiveStreaming { + /** + * サーバのIDします. + * + * @return サーバID + */ + String getId(); + + /** + * サーバが配信するプレビューのマイムタイプを取得します. + * + * @return マイムタイプ + */ + String getMimeType(); + + /** + * サーバへの URL を取得します. + * + * @return サーバへの URL + */ + String getUri(); + + /** + * 起動中か確認します. + * + * @return 起動中の場合は true、それ以外は false + */ + boolean isRunning(); + + /** + * サーバを開始します. + * + * @param callback 開始結果を通知するコールバック + */ + void start(OnStartCallback callback); + + /** + * サーバを停止します. + */ + void stop(); + + /** + * 設定が変更されたことを通知します. + */ + void onConfigChange(); + + /** + * Recorder をミュート状態にする. + */ + void setMute(boolean mute); + + /** + * Recorder のミュート状態を返す. + * @return mute状態 + */ + boolean isMuted(); + + /** + * 映像のエンコーダーに対して sync frame の即時生成を要求する. + * + * @return 即時生成を受け付けた場合はtrue, そうでない場合はfalse + */ + boolean requestSyncFrame(); + + /** + * プレビューサーバから配信したデータの BPS を取得します. + * + * @return プレビューサーバから配信したデータの BPS + */ + long getBPS(); + + /** + * サーバを解放します. + */ + void release(); + + /** + * イベントを通知するリスナーを設定します. + * + * @param listener リスナー + */ + void setOnEventListener(OnEventListener listener); + + /** + * SSLContext を使用するかどうかのフラグを返す. + * + * @return SSLContext を使用する場合はtrue, そうでない場合はfalse + */ + boolean useSSLContext(); + + /** + * SSL コンテキストの設定を行います. + * + * @param sslContext SSL コンテキスト + */ + void setSSLContext(SSLContext sslContext); + + /** + * SSL コンテキストを取得します. + * + * @return SSL コンテキスト + */ + SSLContext getSSLContext(); + + /** + * 起動結果を通知するコールバック. + */ + interface OnStartCallback { + /** + * 起動成功したことを通知します. + */ + void onSuccess(); + + /** + * 起動失敗したことを通知します. + * + * @param e 失敗原因の例外 + */ + void onFailed(Exception e); + } + + /** + * イベントを通知するリスナー. + */ + interface OnEventListener { + + /** + * 開始されたことを通知します. + */ + void onStarted(); + + /** + * 停止されたことを通知します. + */ + void onStopped(); + + /** + * エラーが発生したことを通知します. + * + * @param e エラー原因の例外 + */ + void onError(Exception e); + } +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java new file mode 100644 index 0000000000..22dc177b06 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java @@ -0,0 +1,109 @@ +package org.deviceconnect.android.deviceplugin.host.recorder; + +import java.util.List; + +public interface LiveStreamingProvider { + /** + * 配信用のサーバを作成します. + * + * @param encoderId サーバの識別子 + * @param encoderSettings エンコード設定 + * @return サーバ + */ + LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings); + + /** + * 追加します. + * + * @param liveStreaming 追加するプレビュー配信サーバ + */ + void addLiveStreaming(LiveStreaming liveStreaming); + + /** + * 削除します. + * + * @param encoderId サーバの識別子 + */ + void removeLiveStreaming(String encoderId); + + /** + * サポートしているプレビュー配信用サーバのリストを取得します. + * @return プレビュー配信用サーバのリスト + */ + List getLiveStreamingList(); + + /** + * プレビューサーバが動作している確認します. + * + * @return 動作中の場合は true、それ以外は false + */ + boolean isRunning(); + + /** + * 全てのプレビュー配信サーバを開始します. + * + * レスポンスのリストが空の場合には、全てのプレビュー配信サーバの起動に失敗しています。 + * + * @return 起動に成功したプレビュー配信サーバのリスト + */ + List start(); + + /** + * 全てのプレビュー配信サーバを停止します. + */ + void stop(); + + /** + * 全てのサーバの映像のエンコーダーに対して sync frame の即時生成を要求する. + * + * @return 実際に即時生成を受け付けたサーバのリスト + */ + List requestSyncFrame(); + + /** + * 設定が変更されたことを通知します. + */ + void onConfigChange(); + + /** + * Recorder をミュート状態にする. + */ + void setMute(boolean mute); + + /** + * イベントを通知するリスナーを設定します. + * + * @param listener リスナー + */ + void setOnEventListener(OnEventListener listener); + + /** + * 配信用のサーバを開放します. + */ + void release(); + + /** + * プレビュー配信サーバのイベントを通知するリスナー. + */ + interface OnEventListener { + /** + * プレビュー配信サーバを開始したことを通知します. + * + * @param servers 開始したサーバのリスト + */ + void onStarted(List servers); + + /** + * プレビュー配信サーバを停止したことを通知します. + */ + void onStopped(); + + /** + * プレビュー配信サーバでエラーが発生したことを通知します. + * + * @param server エラーが発生したサーバ + * @param e エラー原因の例外 + */ + void onError(LiveStreaming server, Exception e); + } +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java index 8091aced4b..8daa347c4a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServer.java @@ -6,129 +6,9 @@ */ package org.deviceconnect.android.deviceplugin.host.recorder; -import javax.net.ssl.SSLContext; - /** * プレビュー配信用サーバを定義するインターフェース. */ -public interface PreviewServer { - /** - * サーバ名を取得します. - * - * @return サーバ名 - */ - String getName(); - - /** - * サーバが配信するプレビューのマイムタイプを取得します. - * - * @return マイムタイプ - */ - String getMimeType(); - - /** - * サーバへの URL を取得します. - * - * @return サーバへの URL - */ - String getUri(); - - /** - * プレビュー配信サーバのポート番号を取得します. - * - * @return ポート番号 - */ - int getPort(); - - /** - * プレビュー配信サーバのポート番号を設定します. - * - * @param port ポート番号 - */ - void setPort(int port); - - /** - * サーバを開始します. - * - * @param callback 開始結果を通知するコールバック - */ - void startWebServer(OnWebServerStartCallback callback); - - /** - * サーバを停止します. - */ - void stopWebServer(); - - /** - * 設定が変更されたことを通知します. - */ - void onConfigChange(); - - /** - * Recorder をミュート状態にする. - */ - void setMute(boolean mute); - - /** - * Recorder のミュート状態を返す. - * @return mute状態 - */ - boolean isMuted(); - - /** - * 映像のエンコーダーに対して sync frame の即時生成を要求する. - * - * @return 即時生成を受け付けた場合はtrue, そうでない場合はfalse - */ - boolean requestSyncFrame(); - - /** - * SSLContext を使用するかどうかのフラグを返す. - * - * @return SSLContext を使用する場合はtrue, そうでない場合はfalse - */ - boolean useSSLContext(); - - /** - * SSL コンテキストの設定を行います. - * - * @param sslContext SSL コンテキスト - */ - void setSSLContext(SSLContext sslContext); - - /** - * SSL コンテキストを取得します. - * - * @return SSL コンテキスト - */ - SSLContext getSSLContext(); - - /** - * プレビューサーバから配信したデータの BPS を取得します. - * - * @return プレビューサーバから配信したデータの BPS - */ - long getBPS(); - - /** - * サーバを解放します. - */ - void release(); - - /** - * Callback interface used to receive the result of starting a web server. - */ - interface OnWebServerStartCallback { - /** - * Called when a web server successfully started. - * - * @param uri An ever-updating, static image URI. - */ - void onStart(String uri); +public interface PreviewServer extends LiveStreaming { - /** - * Called when a web server failed to start. - */ - void onFail(); - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java index c8d63fdeae..d9eaa36fcc 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java @@ -8,106 +8,11 @@ import java.util.List; -public interface PreviewServerProvider { +public interface PreviewServerProvider extends LiveStreamingProvider { /** * プレビューで配信するマイムタイプを取得します. * * @return プレビューで配信するマイムタイプ */ List getSupportedMimeType(); - - /** - * サポートしているプレビュー配信サーバを追加します. - * - * @param server 追加するプレビュー配信サーバ - */ - void addServer(PreviewServer server); - - /** - * サポートしているプレビュー配信用サーバのリストを取得します. - * @return プレビュー配信用サーバのリスト - */ - List getServers(); - - /** - * 指定されたマイムタイプに対応するプレビュー配信サーバを取得します. - * - *

- * マイムタイプに対応したプレビュー配信サーバが存在しない場合は null を返却します。 - *

- * - * @param mimeType マイムタイプ - * @return プレビュー配信サーバ - */ - PreviewServer getServerByMimeType(String mimeType); - - /** - * プレビューサーバが動作している確認します. - * - * @return 動作中の場合は true、それ以外は false - */ - boolean isRunning(); - - /** - * 全てのプレビュー配信サーバを開始します. - * - * レスポンスのリストが空の場合には、全てのプレビュー配信サーバの起動に失敗しています。 - * - * @return 起動に成功したプレビュー配信サーバのリスト - */ - List startServers(); - - /** - * 全てのプレビュー配信サーバを停止します. - */ - void stopServers(); - - /** - * 全てのサーバの映像のエンコーダーに対して sync frame の即時生成を要求する. - * - * @return 実際に即時生成を受け付けたサーバのリスト - */ - List requestSyncFrame(); - - /** - * 設定が変更されたことを通知します. - */ - void onConfigChange(); - - /** - * Recorder をミュート状態にする. - */ - void setMute(boolean mute); - - /** - * イベントを通知するリスナーを設定します. - * - * @param listener リスナー - */ - void setOnEventListener(OnEventListener listener); - - /** - * プレビュー配信サーバのイベントを通知するリスナー. - */ - interface OnEventListener { - /** - * プレビュー配信サーバを開始したことを通知します. - * - * @param servers 開始したサーバのリスト - */ - void onStarted(List servers); - - /** - * プレビュー配信サーバを停止したことを通知します. - */ - void onStopped(); - - /** - * プレビュー配信サーバでエラーが発生したことを通知します. - * - * @param server エラーが発生したサーバ - * @param e エラー原因の例外 - */ - void onError(PreviewServer server, Exception e); - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java index 5b88dbccad..7655c04596 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioBroadcasterProvider.java @@ -3,34 +3,20 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractBroadcastProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; public class AudioBroadcasterProvider extends AbstractBroadcastProvider { - private final HostAudioRecorder mRecorder; - public AudioBroadcasterProvider(Context context, HostAudioRecorder recorder) { super(context, recorder); - mRecorder = recorder; } @Override - public Broadcaster createBroadcaster(String broadcastURI) { - String name = null; - for (String n : mRecorder.getSettings().getBroadcasterList()) { - HostMediaRecorder.StreamingSettings s = mRecorder.getSettings().getBroadcaster(n); - if (broadcastURI.equals(s.getBroadcastURI())) { - name = n; - } - } - - if (broadcastURI.startsWith("srt://")) { - return new AudioSRTBroadcaster(mRecorder, broadcastURI, name); - } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) { - return new AudioRTMPBroadcaster(mRecorder, broadcastURI, name); - } else { - return null; + public LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings) { + if (encoderSettings.getMimeType() == HostMediaRecorder.MimeType.RTMP) { + return new AudioRTMPBroadcaster((HostAudioRecorder) getRecorder(), encoderId); } + return null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioConst.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioConst.java deleted file mode 100755 index b703559e5e..0000000000 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioConst.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - AudioConst.java - Copyright (c) 2014 NTT DOCOMO,INC. - Released under the MIT license - http://opensource.org/licenses/mit-license.php - */ - -package org.deviceconnect.android.deviceplugin.host.recorder.audio; - -/** - * 音声録音Broadcastで使用する定数を定義. - * - * [音声録音開始リクエストBroadcast] - * ・ホストデバイスプラグインのHostMediaStreamingRecordingProfileから送信される。 - * ・action: SEND_HOSTDP_TO_AUDIO - * ・putExtra(EXTRA_NAME, EXTRA_NAME_AUDIO_RECORD_START); - * - * [音声録音停止リクエストBroadcast] - * ・ホストデバイスプラグインのHostMediaStreamingRecordingProfileへレスポンスを返す。 - * ・action: SEND_CAMERA_TO_AUDIO - * ・putExtra(EXTRA_NAME, EXTRA_NAME_AUDIO_RECORD_STOP); - * - * [音声録音一時停止リクエストBroadcast] - * ・ホストデバイスプラグインのHostMediaStreamingRecordingProfileへレスポンスを返す。 - * ・action: SEND_CAMERA_TO_AUDIO - * ・putExtra(EXTRA_NAME, EXTRA_NAME_AUDIO_RECORD_PAUSE); - * - * @author NTT DOCOMO, INC. - */ -public final class AudioConst { - - /** - * Constructor. - */ - private AudioConst() { - // No operation. - } - - /** Audio呼び出しアクション. */ - public static final String SEND_HOSTDP_TO_AUDIO = - "org.deviceconnect.android.intent.action.SEND_HOSTDP_TO_AUDIO"; - - /** コマンド名. */ - public static final String EXTRA_NAME = "command"; - - /** 再生. */ - public static final String EXTRA_NAME_AUDIO_RECORD_START = "start"; - - /** 停止. */ - public static final String EXTRA_NAME_AUDIO_RECORD_STOP = "stop"; - - /** 一時停止. */ - public static final String EXTRA_NAME_AUDIO_RECORD_PAUSE = "pause"; - - /** Resume. */ - public static final String EXTRA_NAME_AUDIO_RECORD_RESUME = "resume"; - /** ServiceのID. */ - public static final String EXTRA_SERVICE_ID = "serviceId"; - /** 使用するレコーダーのID. */ - public static final String EXTRA_RECORDER_ID = "recorderId"; - - /** ファイル名. */ - public static final String EXTRA_FILE_NAME = "filename"; - - /** コールバック */ - public static final String EXTRA_CALLBACK = "callback"; - - /** コールバックのエラーメッセージ。 */ - public static final String EXTRA_CALLBACK_ERROR_MESSAGE = "callback_error_message"; - - /** フォーマット名. */ - public static final String FORMAT_TYPE = ".aac"; -} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java deleted file mode 100644 index 3032c20217..0000000000 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServer.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.deviceconnect.android.deviceplugin.host.recorder.audio; - -import android.content.Context; - -import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServer; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; - -public class AudioPreviewServer extends AbstractPreviewServer { - /** - * コンストラクタ. - * - * @param context コンテキスト - * @param recorder プレビューで表示するレコーダ - */ - public AudioPreviewServer(Context context, HostMediaRecorder recorder) { - super(context, recorder, recorder.getId() + "-audio"); - } - - @Override - public String getMimeType() { - return null; - } - - @Override - public String getUri() { - return null; - } - - @Override - public void startWebServer(OnWebServerStartCallback callback) { - } - - @Override - public void stopWebServer() { - } - - @Override - public boolean requestSyncFrame() { - return false; - } - - @Override - public long getBPS() { - return 0; - } -} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java index 1a9c7a5207..5e373f1fa7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioPreviewServerProvider.java @@ -4,6 +4,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServerProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; public class AudioPreviewServerProvider extends AbstractPreviewServerProvider { /** @@ -14,8 +15,16 @@ public class AudioPreviewServerProvider extends AbstractPreviewServerProvider { */ public AudioPreviewServerProvider(Context context, HostMediaRecorder recorder) { super(context, recorder); + } - addServer(new AudioRTSPPreviewServer(context, recorder)); - addServer(new AudioSRTPreviewServer(context, recorder)); + @Override + public LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings) { + switch (encoderSettings.getMimeType()) { + case RTSP: + return new AudioRTSPPreviewServer(getRecorder(), encoderId); + case SRT: + return new AudioSRTPreviewServer(getRecorder(), encoderId); + } + return null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java index 382476d608..ac4525746b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTMPBroadcaster.java @@ -10,8 +10,9 @@ public class AudioRTMPBroadcaster extends AbstractRTMPBroadcaster { private final HostAudioRecorder mRecorder; - public AudioRTMPBroadcaster(HostAudioRecorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + + public AudioRTMPBroadcaster(HostAudioRecorder recorder, String encoderId) { + super(recorder, encoderId); mRecorder = recorder; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java index 0baa474837..2f1b147e8c 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioRTSPPreviewServer.java @@ -1,13 +1,10 @@ package org.deviceconnect.android.deviceplugin.host.recorder.audio; -import android.content.Context; - import org.deviceconnect.android.deviceplugin.host.recorder.AbstractRTSPPreviewServer; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class AudioRTSPPreviewServer extends AbstractRTSPPreviewServer { - AudioRTSPPreviewServer(Context context, HostMediaRecorder recorder) { - super(context, recorder); - setPort(getStreamingSettings().getPort()); + AudioRTSPPreviewServer(HostMediaRecorder recorder, String encoderId) { + super(recorder, encoderId); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java index 804a3a6bb3..0c994d59fd 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTBroadcaster.java @@ -3,7 +3,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTBroadcaster; public class AudioSRTBroadcaster extends AbstractSRTBroadcaster { - public AudioSRTBroadcaster(HostAudioRecorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public AudioSRTBroadcaster(HostAudioRecorder recorder, String name) { + super(recorder, name); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java index ab6816a609..94429bf564 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/AudioSRTPreviewServer.java @@ -1,13 +1,10 @@ package org.deviceconnect.android.deviceplugin.host.recorder.audio; -import android.content.Context; - import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; public class AudioSRTPreviewServer extends AbstractSRTPreviewServer { - AudioSRTPreviewServer(final Context context, final HostMediaRecorder recorder) { - super(context, recorder); - setPort(getStreamingSettings().getPort()); + AudioSRTPreviewServer(HostMediaRecorder recorder, String encoderId) { + super(recorder, encoderId); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java index 3d76317dd4..660d454cb1 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/audio/HostAudioRecorder.java @@ -9,7 +9,6 @@ import android.Manifest; import android.content.Context; import android.os.Handler; -import android.util.Log; import android.util.Size; import androidx.annotation.NonNull; @@ -75,16 +74,45 @@ public HostAudioRecorder(final Context context, FileManager fileManager, MediaPr private void initSettings() { if (!mSettings.isInitialized()) { mSettings.setPreviewSize(new Size(320, 240)); -// mSettings.setPreviewBitRate(512 * 1024); - mSettings.setPreviewMaxFrameRate(30); -// mSettings.setPreviewKeyFrameInterval(1); + // 音声設定 mSettings.setPreviewAudioSource(AudioSource.DEFAULT); mSettings.setPreviewAudioBitRate(128 * 1024); mSettings.setPreviewSampleRate(48000); mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); + // 各サーバ設定 + mSettings.addEncoder(getId() + "-RTSP"); + EncoderSettings rtsp = mSettings.getEncoderSetting(getId() + "-RTSP"); + rtsp.setName("RTSP"); + rtsp.setMimeType(MimeType.RTSP); + rtsp.setPort(32000); + rtsp.setPreviewSize(new Size(320, 240)); + rtsp.setPreviewBitRate(2 * 1024 * 1024); + rtsp.setPreviewMaxFrameRate(30); + rtsp.setPreviewKeyFrameInterval(5); + + mSettings.addEncoder(getId() + "-SRT"); + EncoderSettings srt = mSettings.getEncoderSetting(getId() + "-SRT"); + srt.setName("SRT"); + srt.setMimeType(MimeType.SRT); + srt.setPort(33000); + srt.setPreviewSize(new Size(320, 240)); + srt.setPreviewBitRate(2 * 1024 * 1024); + srt.setPreviewMaxFrameRate(30); + srt.setPreviewKeyFrameInterval(5); + + mSettings.addEncoder(getId() + "-RTMP"); + EncoderSettings rtmp = mSettings.getEncoderSetting(getId() + "-RTMP"); + rtmp.setName("RTMP"); + rtmp.setMimeType(MimeType.RTMP); + rtmp.setPreviewSize(new Size(320, 240)); + rtmp.setPreviewBitRate(2 * 1024 * 1024); + rtmp.setPreviewMaxFrameRate(30); + rtmp.setPreviewKeyFrameInterval(5); + rtmp.setBroadcastURI("rtmp://localhost:1935"); + mSettings.finishInitialization(); } } @@ -236,7 +264,7 @@ public boolean hasVideo() { // private method. private String generateAudioFileName() { - return "android_audio_" + mSimpleDateFormat.format(new Date()) + AudioConst.FORMAT_TYPE; + return "android_audio_" + mSimpleDateFormat.format(new Date()) + ".aac"; } protected MP4Recorder createMP4Recorder() { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java index 60a0f058cc..4e31144488 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2BroadcasterProvider.java @@ -3,36 +3,19 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractBroadcastProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; public class Camera2BroadcasterProvider extends AbstractBroadcastProvider { - /** - * カメラを操作するレコーダ. - */ - private final Camera2Recorder mRecorder; - public Camera2BroadcasterProvider(Context context, Camera2Recorder recorder) { super(context, recorder); - mRecorder = recorder; } @Override - public Broadcaster createBroadcaster(String broadcastURI) { - String name = null; - for (String n : mRecorder.getSettings().getBroadcasterList()) { - HostMediaRecorder.StreamingSettings s = mRecorder.getSettings().getBroadcaster(n); - if (broadcastURI.equals(s.getBroadcastURI())) { - name = n; - } - } - - if (broadcastURI.startsWith("srt://")) { - return new Camera2SRTBroadcaster(mRecorder, broadcastURI, name); - } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) { - return new Camera2RTMPBroadcaster(mRecorder, broadcastURI, name); - } else { - return null; + public LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings) { + if (encoderSettings.getMimeType() == HostMediaRecorder.MimeType.RTMP) { + return new Camera2RTMPBroadcaster((Camera2Recorder) getRecorder(), encoderId); } + return null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java index 13bc6410cb..7071a24e74 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2MJPEGPreviewServer.java @@ -6,9 +6,6 @@ */ package org.deviceconnect.android.deviceplugin.host.recorder.camera; -import android.content.Context; -import android.graphics.SurfaceTexture; - import org.deviceconnect.android.deviceplugin.host.recorder.AbstractMJPEGPreviewServer; import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGEncoder; @@ -16,9 +13,8 @@ * カメラのプレビューをMJPEG形式で配信するサーバー. */ class Camera2MJPEGPreviewServer extends AbstractMJPEGPreviewServer { - Camera2MJPEGPreviewServer(Context context, Camera2Recorder recorder, boolean useSSL) { - super(context, recorder, useSSL); - setPort(getStreamingSettings().getPort() + (useSSL ? 1 : 0)); + Camera2MJPEGPreviewServer(Camera2Recorder recorder, String encoderId) { + super(recorder, encoderId); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java index cd53d3a0ec..035b108f44 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2PreviewServerProvider.java @@ -7,10 +7,10 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; import android.content.Context; -import android.util.Log; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServerProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServer; import org.deviceconnect.android.deviceplugin.host.recorder.util.OverlayManager; @@ -35,28 +35,12 @@ class Camera2PreviewServerProvider extends AbstractPreviewServerProvider { */ Camera2PreviewServerProvider(final Context context, final Camera2Recorder recorder) { super(context, recorder); - mOverlayManager = new OverlayManager(context, recorder); - - List previewList = recorder.getSettings().getPreviewServerList(); - for (String name : previewList) { - HostMediaRecorder.StreamingSettings s = recorder.getSettings().getPreviewServer(name); - if (s != null) { - String mimeType = s.getMimeType(); - if ("video/x-mjpeg".equalsIgnoreCase(mimeType)) { - addServer(new Camera2MJPEGPreviewServer(context, recorder, false)); - } else if ("video/x-rtp".equalsIgnoreCase(mimeType)) { - addServer(new Camera2RTSPPreviewServer(context, recorder)); - } else if ("video/MP2T".equalsIgnoreCase(mimeType)) { - addServer(new Camera2SRTPreviewServer(context, recorder)); - } - } - } } @Override - public List startServers() { - List servers = super.startServers(); + public List start() { + List servers = super.start(); if (!servers.isEmpty()) { mOverlayManager.registerBroadcastReceiver(); } @@ -64,9 +48,9 @@ public List startServers() { } @Override - public void stopServers() { + public void stop() { mOverlayManager.destroy(); - super.stopServers(); + super.stop(); } @Override @@ -74,4 +58,17 @@ public void onConfigChange() { super.onConfigChange(); mOverlayManager.onConfigChange(); } + + @Override + public LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings) { + switch (encoderSettings.getMimeType()) { + case MJPEG: + return new Camera2MJPEGPreviewServer((Camera2Recorder) getRecorder(), encoderId); + case RTSP: + return new Camera2RTSPPreviewServer((Camera2Recorder) getRecorder(), encoderId); + case SRT: + return new Camera2SRTPreviewServer((Camera2Recorder) getRecorder(), encoderId); + } + return null; + } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java index b6e7d1869b..7fad036ed1 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTMPBroadcaster.java @@ -5,8 +5,8 @@ public class Camera2RTMPBroadcaster extends AbstractRTMPBroadcaster { - public Camera2RTMPBroadcaster(Camera2Recorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public Camera2RTMPBroadcaster(Camera2Recorder recorder, String encoderId) { + super(recorder, encoderId); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java index dc5807de34..061a512b6a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2RTSPPreviewServer.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; -import android.content.Context; import android.os.Build; import androidx.annotation.RequiresApi; @@ -11,15 +10,14 @@ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) class Camera2RTSPPreviewServer extends AbstractRTSPPreviewServer { - Camera2RTSPPreviewServer(Context context, Camera2Recorder recorder) { - super(context, recorder); - setPort(getStreamingSettings().getPort()); + Camera2RTSPPreviewServer(Camera2Recorder recorder, String encoderId) { + super(recorder, encoderId); } @Override protected VideoStream createVideoStream() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); - switch (getStreamingSettings().getPreviewEncoderName()) { + switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: return new CameraH264VideoStream(recorder, 5006); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index cd510ede73..7e007cfabf 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -28,7 +28,6 @@ import org.deviceconnect.android.deviceplugin.host.recorder.BroadcasterProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServerProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.ImageUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.MP4Recorder; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; @@ -156,33 +155,20 @@ public Camera2Recorder(Context context, CameraWrapper camera, FileManager fileMa private void initSupportedSettings() { CameraWrapper.Options options = mCameraWrapper.getOptions(); - // MediaCodec でエンコードできる最大解像度を取得 - // TODO h264, h265 で最大解像度が違う場合はどうするべきか? - // TODO ハードウェアエンコーダとソフトウェアエンコーダで最大解像度が違うのはどうするべきか? - Size maxSize = CapabilityUtil.getSupportedMaxSize("video/avc"); - List supportPreviewSizes = new ArrayList<>(); - for (Size size : options.getSupportedPreviewSizeList()) { - if (maxSize != null) { - if (size.getWidth() <= maxSize.getWidth() && size.getHeight() <= maxSize.getHeight()) { - supportPreviewSizes.add(size); - } - } - } - + List> supportedFpsList = options.getSupportedFpsList(); mSettings.mSupportedPictureSize = new ArrayList<>(options.getSupportedPictureSizeList()); - mSettings.mSupportedPreviewSize = supportPreviewSizes; + mSettings.mSupportedPreviewSize = new ArrayList<>(options.getSupportedPreviewSizeList()); if (!mSettings.isInitialized()) { // カメラ設定 mSettings.setPictureSize(options.getDefaultPictureSize()); mSettings.setPreviewSize(options.getDefaultPreviewSize()); -// mSettings.setPreviewBitRate(2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(30); -// mSettings.setPreviewKeyFrameInterval(1); -// mSettings.setPreviewQuality(80); mSettings.setPreviewAutoFocusMode(options.getAutoFocusMode()); mSettings.setPreviewWhiteBalance(options.getAutoWhiteBalanceMode()); mSettings.setPreviewWhiteBalanceTemperature(5600); + if (supportedFpsList.size() > 0) { + mSettings.setPreviewFps(supportedFpsList.get(supportedFpsList.size() - 1)); + } // 音声設定 mSettings.setPreviewAudioSource(null); @@ -191,66 +177,45 @@ private void initSupportedSettings() { mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); -// mSettings.setPort(MIME_TYPE_MJPEG, 11000 + mFacing.mValue); -// mSettings.setPreviewSize(MIME_TYPE_MJPEG, options.getDefaultPreviewSize()); -// mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); -// -// mSettings.setPort(MIME_TYPE_RTSP, 12000 + mFacing.mValue); -// mSettings.setPreviewSize(MIME_TYPE_RTSP, options.getDefaultPreviewSize()); -// mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); -// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); -// -// mSettings.setPort(MIME_TYPE_SRT, 13000 + mFacing.mValue); -// mSettings.setPreviewSize(MIME_TYPE_SRT, options.getDefaultPreviewSize()); -// mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); -// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); -// -// mSettings.setPreviewSize(MIME_TYPE_RTMP, options.getDefaultPreviewSize()); -// mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); -// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); - // 各サーバ設定 - mSettings.addPreviewServer(getId() + "-mjpeg"); - StreamingSettings mjpeg = mSettings.getPreviewServer(getId() + "-mjpeg"); + mSettings.addEncoder(getId() + "-MJPEG"); + EncoderSettings mjpeg = mSettings.getEncoderSetting(getId() + "-MJPEG"); mjpeg.setName("MJPEG"); - mjpeg.setMimeType(MIME_TYPE_MJPEG); + mjpeg.setMimeType(MimeType.MJPEG); mjpeg.setPort(11000 + mFacing.mValue); mjpeg.setPreviewSize(options.getDefaultPreviewSize()); mjpeg.setPreviewQuality(80); mjpeg.setPreviewMaxFrameRate(30); - mSettings.addPreviewServer(getId() + "-rtsp"); - StreamingSettings rtsp = mSettings.getPreviewServer(getId() + "-rtsp"); + mSettings.addEncoder(getId() + "-RTSP"); + EncoderSettings rtsp = mSettings.getEncoderSetting(getId() + "-RTSP"); rtsp.setName("RTSP"); - rtsp.setMimeType(MIME_TYPE_RTSP); + rtsp.setMimeType(MimeType.RTSP); rtsp.setPort(12000 + mFacing.mValue); rtsp.setPreviewSize(options.getDefaultPreviewSize()); rtsp.setPreviewBitRate(2 * 1024 * 1024); rtsp.setPreviewMaxFrameRate(30); rtsp.setPreviewKeyFrameInterval(5); - mSettings.addPreviewServer(getId() + "-srt"); - StreamingSettings srt = mSettings.getPreviewServer(getId() + "-srt"); + mSettings.addEncoder(getId() + "-SRT"); + EncoderSettings srt = mSettings.getEncoderSetting(getId() + "-SRT"); srt.setName("SRT"); - srt.setMimeType(MIME_TYPE_SRT); + srt.setMimeType(MimeType.SRT); srt.setPort(13000 + mFacing.mValue); srt.setPreviewSize(options.getDefaultPreviewSize()); srt.setPreviewBitRate(2 * 1024 * 1024); srt.setPreviewMaxFrameRate(30); srt.setPreviewKeyFrameInterval(5); - mSettings.addBroadcaster(getId() + "-rtmp"); - StreamingSettings rtmp = mSettings.getBroadcaster(getId() + "-rtmp"); + mSettings.addEncoder(getId() + "-RTMP"); + EncoderSettings rtmp = mSettings.getEncoderSetting(getId() + "-RTMP"); rtmp.setName("RTMP"); - rtmp.setMimeType(MIME_TYPE_RTMP); + rtmp.setMimeType(MimeType.RTMP); rtmp.setPreviewSize(options.getDefaultPreviewSize()); rtmp.setPreviewBitRate(2 * 1024 * 1024); rtmp.setPreviewMaxFrameRate(30); rtmp.setPreviewKeyFrameInterval(5); + rtmp.setBroadcastURI("rtmp://localhost:1935"); mSettings.finishInitialization(); } @@ -265,8 +230,8 @@ public CameraWrapper getCameraWrapper() { @Override public synchronized void clean() { super.clean(); - mCamera2BroadcasterProvider.stopBroadcaster(); - mCamera2PreviewServerProvider.stopServers(); + mCamera2BroadcasterProvider.stop(); + mCamera2PreviewServerProvider.stop(); mCameraSurfaceDrawingThread.stop(true); } @@ -551,7 +516,6 @@ public static CameraFacing detect(CameraWrapper cameraWrapper) { } private class CameraSettings extends Settings { - private List mSupportedPictureSize = new ArrayList<>(); private List mSupportedPreviewSize = new ArrayList<>(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java index c705a7c8b5..e71ddbd2c1 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTBroadcaster.java @@ -1,19 +1,18 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTBroadcaster; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class Camera2SRTBroadcaster extends AbstractSRTBroadcaster { - public Camera2SRTBroadcaster(Camera2Recorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public Camera2SRTBroadcaster(Camera2Recorder recorder, String id) { + super(recorder, id); } @Override protected VideoEncoder createVideoEncoder() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); - switch (getStreamingSettings().getPreviewEncoderName()) { + switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: return new CameraVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java index 1f5459be36..a7d97a5f40 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2SRTPreviewServer.java @@ -1,20 +1,17 @@ package org.deviceconnect.android.deviceplugin.host.recorder.camera; -import android.content.Context; - import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class Camera2SRTPreviewServer extends AbstractSRTPreviewServer { - Camera2SRTPreviewServer(final Context context, final Camera2Recorder recorder) { - super(context, recorder); - setPort(getStreamingSettings().getPort()); + Camera2SRTPreviewServer(Camera2Recorder recorder, String encoderId) { + super(recorder, encoderId); } @Override protected VideoEncoder createVideoEncoder() { Camera2Recorder recorder = (Camera2Recorder) getRecorder(); - switch (getStreamingSettings().getPreviewEncoderName()) { + switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: return new CameraVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java index e5e76aff12..2163a537f4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java @@ -101,7 +101,7 @@ private void startCamera(SurfaceTexture surfaceTexture) throws CameraWrapperExce CameraWrapper cameraWrapper = mRecorder.getCameraWrapper(); cameraWrapper.getOptions().setPictureSize(settings.getPictureSize()); cameraWrapper.getOptions().setPreviewSize(settings.getPreviewSize()); - cameraWrapper.getOptions().setFps(settings.getPreviewMaxFrameRate()); + cameraWrapper.getOptions().setFps(settings.getPreviewFps()); cameraWrapper.getOptions().setAutoFocusMode(settings.getPreviewAutoFocusMode()); cameraWrapper.getOptions().setAutoWhiteBalanceMode(settings.getPreviewWhiteBalance()); cameraWrapper.getOptions().setWhiteBalanceTemperature(settings.getPreviewWhiteBalanceTemperature()); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java index c45cdc9deb..fbb047d1dc 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastBroadcasterProvider.java @@ -3,34 +3,20 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractBroadcastProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.Broadcaster; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; public class ScreenCastBroadcasterProvider extends AbstractBroadcastProvider { - private final ScreenCastRecorder mRecorder; - public ScreenCastBroadcasterProvider(Context context, ScreenCastRecorder recorder) { super(context, recorder); - mRecorder = recorder; } @Override - public Broadcaster createBroadcaster(String broadcastURI) { - String name = null; - for (String n : mRecorder.getSettings().getBroadcasterList()) { - HostMediaRecorder.StreamingSettings s = mRecorder.getSettings().getBroadcaster(n); - if (broadcastURI.equals(s.getBroadcastURI())) { - name = n; - } - } - - if (broadcastURI.startsWith("srt://")) { - return new ScreenCastSRTBroadcaster(mRecorder, broadcastURI, name); - } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) { - return new ScreenCastRTMPBroadcaster(mRecorder, broadcastURI, name); - } else { - return null; + public LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings) { + if (encoderSettings.getMimeType() == HostMediaRecorder.MimeType.RTMP) { + return new ScreenCastRTMPBroadcaster((ScreenCastRecorder) getRecorder(), encoderId); } + return null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java index 15e7261c56..547bda2aeb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastMJPEGPreviewServer.java @@ -9,10 +9,8 @@ @TargetApi(Build.VERSION_CODES.LOLLIPOP) class ScreenCastMJPEGPreviewServer extends AbstractMJPEGPreviewServer { - - ScreenCastMJPEGPreviewServer(Context context, ScreenCastRecorder recorder, boolean useSSL) { - super(context, recorder, useSSL); - setPort(getStreamingSettings().getPort() + (useSSL ? 1 : 0)); + ScreenCastMJPEGPreviewServer(ScreenCastRecorder recorder, String encoderId) { + super(recorder, encoderId); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java index 374186ee8c..5bf23c4e11 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastPreviewServerProvider.java @@ -4,6 +4,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.AbstractPreviewServerProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.LiveStreaming; import java.util.List; @@ -13,20 +14,18 @@ class ScreenCastPreviewServerProvider extends AbstractPreviewServerProvider { ScreenCastPreviewServerProvider(Context context, ScreenCastRecorder recorder) { super(context, recorder); + } - List previewList = recorder.getSettings().getPreviewServerList(); - for (String name : previewList) { - HostMediaRecorder.StreamingSettings s = recorder.getSettings().getPreviewServer(name); - if (s != null) { - String mimeType = s.getMimeType(); - if ("video/x-mjpeg".equalsIgnoreCase(mimeType)) { - addServer(new ScreenCastMJPEGPreviewServer(context, recorder, false)); - } else if ("video/x-rtp".equalsIgnoreCase(mimeType)) { - addServer(new ScreenCastRTSPPreviewServer(context, recorder)); - } else if ("video/MP2T".equalsIgnoreCase(mimeType)) { - addServer(new ScreenCastSRTPreviewServer(context, recorder)); - } - } + @Override + public LiveStreaming createLiveStreaming(String encoderId, HostMediaRecorder.EncoderSettings encoderSettings) { + switch (encoderSettings.getMimeType()) { + case MJPEG: + return new ScreenCastMJPEGPreviewServer((ScreenCastRecorder) getRecorder(), encoderId); + case RTSP: + return new ScreenCastRTSPPreviewServer((ScreenCastRecorder) getRecorder(), encoderId); + case SRT: + return new ScreenCastSRTPreviewServer((ScreenCastRecorder) getRecorder(), encoderId); } + return null; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java index 4192362592..1386dd0194 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTMPBroadcaster.java @@ -4,8 +4,8 @@ import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class ScreenCastRTMPBroadcaster extends AbstractRTMPBroadcaster { - public ScreenCastRTMPBroadcaster(ScreenCastRecorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public ScreenCastRTMPBroadcaster(ScreenCastRecorder recorder, String encoderId) { + super(recorder, encoderId); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java index 369627494a..18ea0ca583 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java @@ -1,25 +1,22 @@ package org.deviceconnect.android.deviceplugin.host.recorder.screen; -import android.content.Context; import android.os.Build; import androidx.annotation.RequiresApi; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractRTSPPreviewServer; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.VideoStream; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) class ScreenCastRTSPPreviewServer extends AbstractRTSPPreviewServer { - ScreenCastRTSPPreviewServer(Context context, ScreenCastRecorder recorder) { - super(context, recorder); - setPort(getStreamingSettings().getPort()); + ScreenCastRTSPPreviewServer(ScreenCastRecorder recorder, String encoderId) { + super(recorder, encoderId); } @Override protected VideoStream createVideoStream() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); - switch (getStreamingSettings().getPreviewEncoderName()) { + switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: return new ScreenCastH264VideoStream(recorder, 5006); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index b95c95bc82..350af175b7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -28,7 +28,6 @@ import org.deviceconnect.android.deviceplugin.host.recorder.BroadcasterProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServerProvider; -import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.MP4Recorder; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; import org.deviceconnect.android.deviceplugin.host.recorder.util.SurfaceMP4Recorder; @@ -90,11 +89,6 @@ public ScreenCastRecorder(Context context, FileManager fileMgr, MediaProjectionP * レコーダの設定を初期化します. */ private void initSupportedSettings() { - // MediaCodec でエンコードできる最大解像度を取得 - // TODO h264, h265 で最大解像度が違う場合はどうするべきか? - // TODO ハードウェアエンコーダとソフトウェアエンコーダで最大解像度が違うのはどうするべきか? - Size maxSize = CapabilityUtil.getSupportedMaxSize("video/avc"); - Size originalSize = getDisplaySize(); List supportPictureSizes = new ArrayList<>(); @@ -113,11 +107,7 @@ private void initSupportedSettings() { Size size = new Size(width, height); supportPictureSizes.add(size); - if (maxSize != null) { - if (size.getWidth() <= maxSize.getWidth() && size.getHeight() <= maxSize.getHeight()) { - supportPreviewSizes.add(size); - } - } + supportPreviewSizes.add(size); } mSettings.mSupportedPreviewSize = supportPreviewSizes; mSettings.mSupportedPictureSize = supportPictureSizes; @@ -129,77 +119,54 @@ private void initSupportedSettings() { if (!mSettings.isInitialized()) { mSettings.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); mSettings.setPictureSize(mSettings.getSupportedPictureSizes().get(0)); -// mSettings.setPreviewBitRate(2 * 1024 * 1024); - mSettings.setPreviewMaxFrameRate(30); -// mSettings.setPreviewKeyFrameInterval(1); -// mSettings.setPreviewQuality(80); + // 音声設定 mSettings.setPreviewAudioSource(null); mSettings.setPreviewAudioBitRate(128 * 1024); mSettings.setPreviewSampleRate(48000); mSettings.setPreviewChannel(1); mSettings.setUseAEC(true); -// mSettings.setPort(MIME_TYPE_MJPEG, 21000); -// mSettings.setPreviewSize(MIME_TYPE_MJPEG, mSettings.getSupportedPreviewSizes().get(0)); -// mSettings.setPreviewQuality(MIME_TYPE_MJPEG, 80); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_MJPEG, 30); -// -// mSettings.setPort(MIME_TYPE_RTSP, 22000); -// mSettings.setPreviewSize(MIME_TYPE_RTSP, mSettings.getSupportedPreviewSizes().get(0)); -// mSettings.setPreviewBitRate(MIME_TYPE_RTSP, 2 * 1024 * 1024); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTSP, 30); -// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTSP, 5); -// -// mSettings.setPort(MIME_TYPE_SRT, 23000); -// mSettings.setPreviewSize(MIME_TYPE_SRT, mSettings.getSupportedPreviewSizes().get(0)); -// mSettings.setPreviewBitRate(MIME_TYPE_SRT, 2 * 1024 * 1024); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_SRT, 30); -// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_SRT, 5); -// -// mSettings.setPreviewSize(MIME_TYPE_RTMP, mSettings.getSupportedPreviewSizes().get(0)); -// mSettings.setPreviewBitRate(MIME_TYPE_RTMP, 2 * 1024 * 1024); -// mSettings.setPreviewMaxFrameRate(MIME_TYPE_RTMP, 30); -// mSettings.setPreviewKeyFrameInterval(MIME_TYPE_RTMP, 5); - - mSettings.addPreviewServer(getId() + "-mjpeg"); - StreamingSettings mjpeg = mSettings.getPreviewServer(getId() + "-mjpeg"); + // プレビュー配信サーバ設定 + mSettings.addEncoder(getId() + "-MJPEG"); + EncoderSettings mjpeg = mSettings.getEncoderSetting(getId() + "-MJPEG"); mjpeg.setName("MJPEG"); - mjpeg.setMimeType(MIME_TYPE_MJPEG); + mjpeg.setMimeType(MimeType.MJPEG); mjpeg.setPort(21000); mjpeg.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); mjpeg.setPreviewQuality(80); mjpeg.setPreviewMaxFrameRate(30); - mSettings.addPreviewServer(getId() + "-rtsp"); - StreamingSettings rtsp = mSettings.getPreviewServer(getId() + "-rtsp"); + mSettings.addEncoder(getId() + "-RTSP"); + EncoderSettings rtsp = mSettings.getEncoderSetting(getId() + "-RTSP"); rtsp.setName("RTSP"); - rtsp.setMimeType(MIME_TYPE_RTSP); + rtsp.setMimeType(MimeType.RTSP); rtsp.setPort(22000); rtsp.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); rtsp.setPreviewBitRate(2 * 1024 * 1024); rtsp.setPreviewMaxFrameRate(30); rtsp.setPreviewKeyFrameInterval(5); - mSettings.addPreviewServer(getId() + "-srt"); - StreamingSettings srt = mSettings.getPreviewServer(getId() + "-srt"); + mSettings.addEncoder(getId() + "-SRT"); + EncoderSettings srt = mSettings.getEncoderSetting(getId() + "-SRT"); srt.setName("SRT"); - srt.setMimeType(MIME_TYPE_SRT); + srt.setMimeType(MimeType.SRT); srt.setPort(23000); srt.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); srt.setPreviewBitRate(2 * 1024 * 1024); srt.setPreviewMaxFrameRate(30); srt.setPreviewKeyFrameInterval(5); - mSettings.addBroadcaster(getId() + "-rtmp"); - StreamingSettings rtmp = mSettings.getBroadcaster(getId() + "-rtmp"); + // 配信設定 + mSettings.addEncoder(getId() + "-RTMP"); + EncoderSettings rtmp = mSettings.getEncoderSetting(getId() + "-RTMP"); rtmp.setName("RTMP"); - rtmp.setMimeType(MIME_TYPE_RTMP); + rtmp.setMimeType(MimeType.RTMP); rtmp.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); rtmp.setPreviewBitRate(2 * 1024 * 1024); rtmp.setPreviewMaxFrameRate(30); rtmp.setPreviewKeyFrameInterval(5); - + rtmp.setBroadcastURI("rtmp://localhost:1935"); mSettings.finishInitialization(); } @@ -242,8 +209,8 @@ public EGLSurfaceDrawingThread getSurfaceDrawingThread() { @Override public void clean() { super.clean(); - mScreenCastBroadcasterProvider.stopBroadcaster(); - mScreenCastPreviewServerProvider.stopServers(); + mScreenCastBroadcasterProvider.stop(); + mScreenCastPreviewServerProvider.stop(); mScreenCastMgr.clean(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java index ccb5762dc3..fdf5e9de1c 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTBroadcaster.java @@ -1,19 +1,18 @@ package org.deviceconnect.android.deviceplugin.host.recorder.screen; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTBroadcaster; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; public class ScreenCastSRTBroadcaster extends AbstractSRTBroadcaster { - public ScreenCastSRTBroadcaster(ScreenCastRecorder recorder, String broadcastURI, String name) { - super(recorder, broadcastURI, name); + public ScreenCastSRTBroadcaster(ScreenCastRecorder recorder, String name) { + super(recorder, name); } @Override protected VideoEncoder createVideoEncoder() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); - switch (getStreamingSettings().getPreviewEncoderName()) { + switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: return new ScreenCastVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java index 49976367fa..abf34a884e 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java @@ -9,7 +9,6 @@ import android.content.Context; import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; /** @@ -18,15 +17,14 @@ * @author NTT DOCOMO, INC. */ class ScreenCastSRTPreviewServer extends AbstractSRTPreviewServer { - ScreenCastSRTPreviewServer(final Context context, final ScreenCastRecorder recorder) { - super(context, recorder); - setPort(getStreamingSettings().getPort()); + ScreenCastSRTPreviewServer(ScreenCastRecorder recorder, String encoderId) { + super(recorder, encoderId); } @Override protected VideoEncoder createVideoEncoder() { ScreenCastRecorder recorder = (ScreenCastRecorder) getRecorder(); - switch (getStreamingSettings().getPreviewEncoderName()) { + switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: return new ScreenCastVideoEncoder(recorder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java index d54eb5f9ff..9cbe2f174f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java @@ -13,10 +13,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.TextView; import androidx.constraintlayout.widget.ConstraintLayout; import org.deviceconnect.android.deviceplugin.host.R; +import org.deviceconnect.android.deviceplugin.host.recorder.CropInterface; import java.util.HashMap; import java.util.Map; @@ -62,8 +64,9 @@ public boolean onScale(ScaleGestureDetector detector) { Rect cropRect = holder.mCropRect; Object tag = holder.mTag; - float newWidth = cropRect.width() * detector.getScaleFactor(); - float newHeight = cropRect.height() * detector.getScaleFactor(); + float scaleFactor = Math.max(0.1f, Math.min(detector.getScaleFactor(), 5.0f)); + float newWidth = cropRect.width() * scaleFactor; + float newHeight = cropRect.height() * scaleFactor; if (mPreviewWidth < newWidth) { newWidth = mPreviewWidth; @@ -208,6 +211,11 @@ public float getScale() { } } + /** + * プレビューの表示を行う SurfaceView を取得します. + * + * @return SurfaceView + */ public SurfaceView getSurfaceView() { View root = findViewById(R.id.preview_root); if (root != null) { @@ -216,6 +224,14 @@ public SurfaceView getSurfaceView() { return null; } + /** + * 切り抜き範囲の枠を追加します. + * + * 既に同じキーが登録されている場合は、切り抜き範囲を変更します。 + * + * @param key 切り抜き範囲の枠を識別するキー + * @param cropRect 切り抜き範囲の枠 + */ public void addCropRect(Object key, Rect cropRect) { if (key == null || cropRect == null) { return; @@ -227,8 +243,12 @@ public void addCropRect(Object key, Rect cropRect) { holder = new CropRectHolder(); holder.mTag = key; holder.mCropRect = cropRect; - holder.mView = new View(getContext()); - holder.mView.setBackgroundResource(R.drawable.border_red); + holder.mView = inflate(getContext(), R.layout.item_crop_frame, null); + + TextView tv = holder.mView.findViewById(R.id.textview); + if (tv != null && key instanceof CropInterface) { + tv.setText(((CropInterface) key).getName()); + } ConstraintLayout constraintLayout = findViewById(R.id.preview_root); constraintLayout.addView(holder.mView); @@ -248,6 +268,13 @@ public void addCropRect(Object key, Rect cropRect) { }); } + /** + * 切り抜き範囲の枠を削除します. + * + * 対応する切り抜き範囲の枠が存在しない場合は何も処理を行いません。 + * + * @param key 切り抜き範囲の枠を識別するキー + */ public void removeCropRange(Object key) { post(() -> { CropRectHolder holder = mCropRectMap.remove(key); @@ -258,7 +285,13 @@ public void removeCropRange(Object key) { }); } - protected void onChangedCropRect(Object key, Rect drawingRange) { + /** + * 切り抜き範囲の枠の値が変更された時に呼び出されます. + * + * @param key キー + * @param cropRect 新しい値 + */ + protected void onChangedCropRect(Object key, Rect cropRect) { CropRectHolder holder = mCropRectMap.get(key); if (holder != null) { setCropRectView(holder); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java index 69c680fda5..5fea7750d8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java @@ -54,6 +54,16 @@ public static List getSupportedProfileLevel(Stri return list; } + public static boolean isSupportedProfileLevel(String mimeType, int profile, int level) { + List list = getSupportedProfileLevel(mimeType); + for (HostMediaRecorder.ProfileLevel pl : list) { + if (pl.getProfile() == profile && pl.getLevel() == level) { + return true; + } + } + return false; + } + private static List getSupportedEncoders(String mimeType) { List encoderList = new ArrayList<>(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MediaProjectionProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MediaProjectionProvider.java index 475bd5cdca..7c9b019c12 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MediaProjectionProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MediaProjectionProvider.java @@ -122,7 +122,11 @@ protected void onReceiveResult(final int resultCode, final Bundle resultData) { // 画面に HOST プラグイン関連の画面が表示されている場合は、Activity が起動できるので // そのまま Context#startActivity を実行します。 if (getApp().isDeviceConnectClassOfTopActivity() || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - mContext.startActivity(intent); + try { + mContext.startActivity(intent); + } catch (Exception e) { + callback.onDisallowed(); + } } else { // Android 10(Q) からは、バックグラウンドから Activity を起動できなくなったので、 // Notification から起動するようにします。 diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java index cb62c2bdbb..e3ae6ea440 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java @@ -179,8 +179,10 @@ public void run() { } } - mRect.set(mEndRect); - postOnMoved(mRect); + if (count == 0) { + mRect.set(mEndRect); + postOnMoved(mRect); + } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/item_crop_frame.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/item_crop_frame.xml new file mode 100644 index 0000000000..2c1939b7e1 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/layout/item_crop_frame.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml index 3d7de267b7..4461dc60bf 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_main.xml @@ -21,6 +21,7 @@ @@ -28,6 +29,7 @@ diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml index b0a8c4fa25..e1b314bdea 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml @@ -18,6 +18,12 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + } android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { minSdkVersion 21 diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java index 16e8d5f115..03c676034c 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/EGLSurfaceDrawingThread.java @@ -2,7 +2,6 @@ import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.util.Size; import android.view.Surface; import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList; @@ -100,7 +99,10 @@ public int getRenderingTimeout() { } /** - * 描画を行う Surface のサイズを設定します. + * カメラなどの映像ソースのサイズを設定します. + * + * 映像ソースのサイズと配信先のサイズから映像が崩れないように + * アスペクト比を計算を行い描画を行います。 * * @param width 横幅 * @param height 縦幅 @@ -547,6 +549,7 @@ public void run() { onStarted(); postOnStarted(); + ViewSize size = new ViewSize(); SurfaceTexture st = mStManager.getSurfaceTexture(); while (mState == STATE_RUNNING) { mStManager.awaitNewImage(); @@ -561,28 +564,17 @@ public void run() { Rect cropRect = eglSurfaceBase.getCropRect(); if (cropRect != null) { mStManager.setCropRect(cropRect, mWidth, mHeight); - - // 出力先のアスペクト比に合わせて計算を行う - Size size = calculateViewSize(cropRect.width(), cropRect.height(), viewportW, viewportH); - if (viewportW > size.getWidth()) { - viewportX = (viewportW - size.getWidth()) / 2; - viewportW = size.getWidth(); - } else if (viewportH > size.getHeight()) { - viewportY = (viewportH - size.getHeight()) / 2; - viewportH = size.getHeight(); - } + calculateViewSize(cropRect.width(), cropRect.height(), viewportW, viewportH, size); } else { mStManager.clearCropRect(); - - // 出力先のアスペクト比に合わせて計算を行う - Size size = calculateViewSize(mWidth, mHeight, viewportW, viewportH); - if (viewportW > size.getWidth()) { - viewportX = (viewportW - size.getWidth()) / 2; - viewportW = size.getWidth(); - } else if (viewportH > size.getHeight()) { - viewportY = (viewportH - size.getHeight()) / 2; - viewportH = size.getHeight(); - } + calculateViewSize(mWidth, mHeight, viewportW, viewportH, size); + } + if (viewportW > size.getWidth()) { + viewportX = (viewportW - size.getWidth()) / 2; + viewportW = size.getWidth(); + } else if (viewportH > size.getHeight()) { + viewportY = (viewportH - size.getHeight()) / 2; + viewportH = size.getHeight(); } mStManager.setViewport(viewportX, viewportY,viewportW, viewportH); mStManager.drawImage(getDisplayRotation()); @@ -627,6 +619,19 @@ public void run() { } } + private static class ViewSize { + int mWidth; + int mHeight; + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + } + /** * 指定された View のサイズにフィットするサイズを計算します. * @@ -634,18 +639,21 @@ public void run() { * @param height 縦幅 * @param viewWidth View のサイズ * @param viewHeight View のサイズ - * @return View にフィットするサイズ + * @param dest 出力先 */ - private Size calculateViewSize(int width, int height, int viewWidth, int viewHeight) { + private void calculateViewSize(int width, int height, int viewWidth, int viewHeight, ViewSize dest) { int h = (int) (height * (viewWidth / (float) width)); if (viewHeight < h) { int w = (int) (width * (viewHeight / (float) height)); if (w % 2 != 0) { w--; } - return new Size(w, viewHeight); + dest.mWidth = w; + dest.mHeight = viewHeight; + } else { + dest.mWidth = viewWidth; + dest.mHeight = h; } - return new Size(viewWidth, h); } /** diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraVideoQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraVideoQuality.java index a63a7e7d86..412b1268db 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraVideoQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/CameraVideoQuality.java @@ -1,6 +1,7 @@ package org.deviceconnect.android.libmedia.streaming.video; import android.hardware.camera2.CameraCharacteristics; +import android.util.Range; public class CameraVideoQuality extends VideoQuality { /** @@ -8,6 +9,11 @@ public class CameraVideoQuality extends VideoQuality { */ private int mFacing = CameraCharacteristics.LENS_FACING_BACK; + /** + * カメラのFPS. + */ + private Range mFps; + /** * コンストラクタ. * @param mimeType マイムタイプ @@ -24,6 +30,9 @@ public CameraVideoQuality(String mimeType) { public void set(CameraVideoQuality quality) { super.set(quality); mFacing = quality.getFacing(); + if (quality.mFps != null) { + mFps = new Range<>(quality.mFps.getLower(), quality.mFps.getUpper()); + } } /** @@ -52,4 +61,24 @@ public int getFacing() { public void setFacing(int facing) { mFacing = facing; } + + /** + * カメラの fps を取得します. + * + * 設定されていない場合は null を返却します. + * + * @return fps + */ + public Range getFps() { + return mFps; + } + + /** + * カメラの fps を設定します. + * + * @param fps カメラの FPS + */ + public void setFps(Range fps) { + mFps = fps; + } } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java index cd7ff97349..9c06202a60 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java @@ -61,6 +61,25 @@ protected void release() { */ public abstract VideoQuality getVideoQuality(); + /** + * Low Latency の設定を要求します. + * + * @param lowLatency 1 の場合は、Low Latency を有効、0 の場合は、Low Latency を無効 + */ + public void requestLowLatency(int lowLatency) { + if (mMediaCodec != null) { + if (lowLatency == 1 || lowLatency == 0) { + Bundle b = new Bundle(); + b.putInt(MediaCodec.PARAMETER_KEY_LOW_LATENCY, lowLatency); + try { + mMediaCodec.setParameters(b); + } catch (Exception e) { + // ignore. + } + } + } + } + /** * キーフレームを要求します. */ @@ -228,6 +247,15 @@ private MediaCodec createMediaCodec(int colorFormat, int w, int h) throws IOExce } } + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { + Integer lowLatency = videoQuality.getLowLatency(); + if (lowLatency != null) { + if (lowLatency == 1 || lowLatency == 0) { + format.setInteger(MediaFormat.KEY_LOW_LATENCY, lowLatency); + } + } + } + MediaCodec mediaCodec = MediaCodec.createByCodecName(codecInfo.getName()); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); return mediaCodec; diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java index ced8b61454..efa9e59b71 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java @@ -16,6 +16,7 @@ public class VideoQuality { private int mIntraRefresh = 0; private int mProfile = 0; private int mLevel = 0; + private Integer mLowLatency; /** * ビットレートのモード. @@ -33,6 +34,11 @@ public class VideoQuality { */ private Rect mCropRect; + /** + * 画面の向き. + */ + private int mOrientation; + /** * コンストラクタ. * @param mimeType エンコードのマイムタイプ @@ -274,26 +280,6 @@ public void setBitRateMode(BitRateMode bitRateMode) { mBitRateMode = bitRateMode; } - /** - * ビットレートモード. - */ - public enum BitRateMode { - /** - * 可変ビットレート. - */ - VBR, - - /** - * 固定ビットレート. - */ - CBR, - - /** - * 固定品質ビットレート. - */ - CQ - } - /** * 描画範囲を取得します. * @@ -316,6 +302,48 @@ public void setCropRect(Rect cropRect) { mCropRect = cropRect; } + /** + * Low Latency フラグを設定します. + * + * 1: 有効 + * 0: 無効 + * null: デフォルト設定 + * + * @param lowLatency Low Latency フラグ + */ + public void setLowLatency(Integer lowLatency) { + mLowLatency = lowLatency; + } + + /** + * Low Latency フラグを取得します. + * + * @return Low Latency フラグ + */ + public Integer getLowLatency() { + return mLowLatency; + } + + /** + * ビットレートモード. + */ + public enum BitRateMode { + /** + * 可変ビットレート. + */ + VBR, + + /** + * 固定ビットレート. + */ + CBR, + + /** + * 固定品質ビットレート. + */ + CQ + } + @Override public String toString() { return "VideoQuality{" + From 71c6e45705ac2776966d95f671383d8724673a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 13 Sep 2021 11:16:35 +0900 Subject: [PATCH 19/40] =?UTF-8?q?MJPEG=E3=80=81RTSP=E3=80=81SRT=20?= =?UTF-8?q?=E3=81=AE=E8=A8=AD=E5=AE=9A=E7=94=BB=E9=9D=A2=E3=82=92=E5=85=B1?= =?UTF-8?q?=E9=80=9A=E3=81=AE=E3=82=AF=E3=83=A9=E3=82=B9=E3=82=92=E7=B6=99?= =?UTF-8?q?=E6=89=BF=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/SettingsEncoderFragment.java | 413 +++++++++++++++++ .../settings/SettingsMJPEGFragment.java | 259 +---------- .../settings/SettingsRTSPFragment.java | 409 +---------------- .../settings/SettingsSRTFragment.java | 418 +----------------- .../HostMediaStreamingRecordingProfile.java | 18 +- .../recorder/AbstractRTSPPreviewServer.java | 1 - .../recorder/AbstractSRTPreviewServer.java | 1 - .../host/recorder/HostMediaRecorder.java | 42 +- .../host/recorder/camera/Camera2Recorder.java | 58 +++ .../recorder/screen/ScreenCastRecorder.java | 27 +- .../screen/ScreenCastSRTPreviewServer.java | 2 - 11 files changed, 560 insertions(+), 1088 deletions(-) create mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java new file mode 100644 index 0000000000..404155e344 --- /dev/null +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java @@ -0,0 +1,413 @@ +package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; + +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; +import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; +import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; +import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public abstract class SettingsEncoderFragment extends SettingsParameterFragment { + private HostMediaRecorder mMediaRecorder; + + @Override + public void onBindService() { + mMediaRecorder = getRecorder(); + + HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); + + setPreviewServerPort(); + setPreviewServerUrl(settings.getPort()); + setPreviewSizePreference(settings); + setPreviewVideoEncoderPreference(settings); + setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); + + setPreviewCutOutReset(); + + setInputTypeNumber("preview_framerate"); + setInputTypeNumber("preview_bitrate"); + setInputTypeNumber("preview_i_frame_interval"); + setInputTypeNumber("preview_intra_refresh"); + + setPreviewClipPreference("preview_clip_left"); + setPreviewClipPreference("preview_clip_top"); + setPreviewClipPreference("preview_clip_right"); + setPreviewClipPreference("preview_clip_bottom"); + } + + protected HostMediaRecorder.EncoderSettings getEncoderSetting() { + HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); + return s.getEncoderSetting(getEncoderId()); + } + + private void setPreviewServerPort() { + setInputTypeNumber("port"); + EditTextPreference pref = findPreference("port"); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + } + } + + protected abstract void setPreviewServerUrl(int port); + + /** + * 切り抜き範囲の設定にリスナーを設定します. + * + * @param key キー + */ + public void setPreviewClipPreference(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + setInputTypeNumber(key); + } + } + + private void setEmptyText(String key) { + EditTextPreference left = findPreference(key); + if (left != null) { + left.setText(null); + } + } + + /** + * 切り抜き範囲のリセットボタンのリスナーを設定します. + */ + private void setPreviewCutOutReset() { + PreferenceScreen pref = findPreference("preview_clip_reset"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + setEmptyText("preview_clip_left"); + setEmptyText("preview_clip_top"); + setEmptyText("preview_clip_right"); + setEmptyText("preview_clip_bottom"); + getEncoderSetting().setCropRect(null); + return false; + }); + } + } + + /** + * プレビューの解像度 Preference を作成します. + * + * @param settings レコーダの設定 + */ + private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { + ListPreference pref = findPreference("camera_preview_size"); + if (pref != null) { + List previewSizes = getSupportedPreviewSizes(); + if (!previewSizes.isEmpty()) { + List entryValues = new ArrayList<>(); + for (Size preview : previewSizes) { + entryValues.add(getValueFromSize(preview)); + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + Size previewSize = settings.getPreviewSize(); + if (previewSize != null) { + pref.setValue(getValueFromSize(previewSize)); + } + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダの設定を行います. + * + * @param settings レコーダ設定 + */ + private void setPreviewVideoEncoderPreference(HostMediaRecorder.EncoderSettings settings) { + ListPreference pref = findPreference("preview_encoder"); + if (pref != null) { + List list = settings.getSupportedVideoEncoders(); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(list); + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * エンコーダのプロファイルとレベルを設定します. + * + * @param settings レコーダ設定 + * @param videoCodec エンコーダ + * @param reset リセットフラグ + */ + private void setPreviewProfileLevelPreference(HostMediaRecorder.EncoderSettings settings, HostMediaRecorder.VideoCodec videoCodec, boolean reset) { + ListPreference pref = findPreference("preview_profile_level"); + if (pref != null) { + List list = CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType()); + if (!list.isEmpty()) { + List entryValues = new ArrayList<>(); + entryValues.add("none"); + + for (HostMediaRecorder.ProfileLevel pl : list) { + String value = getProfileLevel(videoCodec, pl); + if (value != null) { + entryValues.add(value); + } + } + + pref.setEntries(entryValues.toArray(new String[0])); + pref.setEntryValues(entryValues.toArray(new String[0])); + pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); + + if (reset) { + pref.setValue("none"); + } else { + HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); + if (pl != null) { + pref.setValue(getProfileLevel(videoCodec, pl)); + } + } + + pref.setVisible(true); + } else { + pref.setEnabled(false); + } + } + } + + /** + * サイズの小さい方からソートを行うための比較演算子. + */ + private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + }; + + /** + * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. + * + * @return サポートしているプレビューサイズのリスト + */ + @NonNull + private List getSupportedPreviewSizes() { + HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(getEncoderId()); + List previewSizes = new ArrayList<>(encoderSettings.getSupportedEncoderSizes()); + Collections.sort(previewSizes, SIZE_COMPARATOR); + return previewSizes; + } + + /** + * プレビューのサイズを文字列に変換します. + * + * @param previewSize プレビューサイズ + * @return 文字列 + */ + private String getValueFromSize(Size previewSize) { + return previewSize.getWidth() + " x " + previewSize.getHeight(); + } + + /** + * 文字列を Size に変換します. + * + * Size に変換できなかった場合には null を返却します。 + * + * @param value 文字列のサイズ + * @return サイズ + */ + private Size getSizeFromValue(String value) { + String[] t = value.split("x"); + if (t.length == 2) { + try { + int w = Integer.parseInt(t[0].trim()); + int h = Integer.parseInt(t[1].trim()); + return new Size(w, h); + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * プロファイルとレベルを文字列に変換します. + * + * @param encoderName エンコーダ + * @param pl プロファイルとレベル + * @return 文字列 + */ + private String getProfileLevel(HostMediaRecorder.VideoCodec encoderName, HostMediaRecorder.ProfileLevel pl) { + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.valueOf(pl.getProfile()); + H264Level l = H264Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + case H265: { + H265Profile p = H265Profile.valueOf(pl.getProfile()); + H265Level l = H265Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + return p.getName() + " - " + l.getName(); + } + } + } + return null; + } + + /** + * 文字列をプロファイルとレベルに変換します. + * + * プロファイルとレベルに変換できなかった場合には、null を返却します。 + * + * @param encoderName エンコーダ + * @param value 変換する文字列 + * @return プロファイルとレベル + */ + private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoCodec encoderName, String value) { + String[] t = value.split("-"); + if (t.length == 2) { + try { + String profile = t[0].trim(); + String level = t[1].trim(); + switch (encoderName) { + case H264: { + H264Profile p = H264Profile.nameOf(profile); + H264Level l = H264Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + case H265: { + H265Profile p = H265Profile.nameOf(profile); + H265Level l = H265Level.nameOf(level); + if (p != null && l != null) { + return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); + } + } + } + } catch (Exception e) { + return null; + } + } + return null; + } + + /** + * 切り抜き範囲の値を取得します. + * + * 未設定の場合には null を返却します。 + * + * @param key キー + * @return 切り抜き範囲 + */ + private Integer getDrawingRange(String key) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + try { + return Integer.parseInt(pref.getText()); + } catch (NumberFormatException e) { + // ignore. + } + } + return null; + } + + /** + * 設定が変更された時に呼び出されるリスナー. + */ + private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { + if (mMediaRecorder == null) { + return false; + } + + HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); + + String key = preference.getKey(); + if ("camera_preview_size".equals(key)) { + Size size = getSizeFromValue((String) newValue); + if (size != null) { + settings.setPreviewSize(size); + } + } else if ("port".equalsIgnoreCase(key)) { + setPreviewServerUrl(Integer.parseInt((String) newValue)); + } else if ("preview_encoder".equals(key)) { + // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく + try { + settings.setProfileLevel(null); + } catch (Exception e) { + return false; + } + HostMediaRecorder.VideoCodec encoderName = + HostMediaRecorder.VideoCodec.nameOf((String) newValue); + setPreviewProfileLevelPreference(settings, encoderName, true); + } else if ("preview_profile_level".equalsIgnoreCase(key)) { + try { + settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); + } catch (Exception e) { + return false; + } + } else if ("preview_clip_left".equalsIgnoreCase(key)) { + try { + int clipLeft = Integer.parseInt((String) newValue); + Integer clipRight = getDrawingRange("preview_clip_right"); + if (clipRight != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_top".equalsIgnoreCase(key)) { + try { + int clipTop = Integer.parseInt((String) newValue); + Integer clipBottom = getDrawingRange("preview_clip_bottom"); + if (clipBottom != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_right".equalsIgnoreCase(key)) { + try { + int clipRight = Integer.parseInt((String) newValue); + Integer clipLeft = getDrawingRange("preview_clip_left"); + if (clipLeft != null && clipRight <= clipLeft) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { + try { + int clipBottom = Integer.parseInt((String) newValue); + Integer clipTop = getDrawingRange("preview_clip_top"); + if (clipTop != null && clipBottom <= clipTop) { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } + return true; + }; +} diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java index ec8ad7eb12..cf9d408a32 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -1,27 +1,14 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; import android.os.Bundle; -import android.util.Size; -import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; import org.deviceconnect.android.deviceplugin.host.R; import org.deviceconnect.android.deviceplugin.host.activity.fragment.SeekBarDialogPreference; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class SettingsMJPEGFragment extends SettingsParameterFragment { - private HostMediaRecorder mMediaRecorder; - +public class SettingsMJPEGFragment extends SettingsEncoderFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName(getEncoderId()); @@ -30,39 +17,12 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @Override public void onBindService() { - mMediaRecorder = getRecorder(); - - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); - - setPreviewServerPort(); - setPreviewServerUrl(settings.getPort()); - setPreviewSizePreference(settings); - + super.onBindService(); setPreviewJpegQuality(); - setPreviewCutOutReset(); - - setInputTypeNumber("preview_framerate"); - - setPreviewClipPreference("preview_clip_left"); - setPreviewClipPreference("preview_clip_top"); - setPreviewClipPreference("preview_clip_right"); - setPreviewClipPreference("preview_clip_bottom"); - } - - private HostMediaRecorder.EncoderSettings getStreamingSetting() { - HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getEncoderSetting(getEncoderId()); - } - - private void setPreviewServerPort() { - setInputTypeNumber("port"); - EditTextPreference pref = findPreference("port"); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - } } - private void setPreviewServerUrl(int port) { + @Override + protected void setPreviewServerUrl(int port) { EditTextPreference pref = findPreference("url"); if (pref != null) { String ipAddress = NetworkUtil.getIPAddress(requireContext()); @@ -81,215 +41,4 @@ private void setPreviewJpegQuality() { pref.setEnabled(true); } } - - /** - * 切り抜き範囲の設定にリスナーを設定します. - * - * @param key キー - */ - public void setPreviewClipPreference(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - setInputTypeNumber(key); - } - } - - /** - * 切り抜き範囲のリセットボタンのリスナーを設定します. - */ - private void setPreviewCutOutReset() { - PreferenceScreen pref = findPreference("preview_clip_reset"); - if (pref != null) { - pref.setOnPreferenceClickListener(preference -> { - EditTextPreference left = findPreference("preview_clip_left"); - if (left != null) { - left.setText(null); - } - EditTextPreference top = findPreference("preview_clip_top"); - if (top != null) { - top.setText(null); - } - EditTextPreference right = findPreference("preview_clip_right"); - if (right != null) { - right.setText(null); - } - EditTextPreference bottom = findPreference("preview_clip_bottom"); - if (bottom != null) { - bottom.setText(null); - } - getStreamingSetting().setCropRect(null); - return false; - }); - } - } - - /** - * プレビューの解像度 Preference を作成します. - * - * @param settings レコーダの設定 - */ - private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { - ListPreference pref = findPreference("camera_preview_size"); - if (pref != null) { - List previewSizes = getSupportedPreviewSizes(); - if (!previewSizes.isEmpty()) { - List entryValues = new ArrayList<>(); - for (Size preview : previewSizes) { - entryValues.add(getValueFromSize(preview)); - } - - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - - Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - pref.setValue(getValueFromSize(previewSize)); - } - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * サイズの小さい方からソートを行うための比較演算子. - */ - private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { - // We cast here to ensure the multiplications won't overflow - return Long.signum((long) lhs.getWidth() * lhs.getHeight() - - (long) rhs.getWidth() * rhs.getHeight()); - }; - - /** - * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. - * - * @return サポートしているプレビューサイズのリスト - */ - @NonNull - private List getSupportedPreviewSizes() { - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); - List previewSizes = new ArrayList<>(); - if (settings != null) { - previewSizes.addAll(settings.getSupportedPreviewSizes()); - Collections.sort(previewSizes, SIZE_COMPARATOR); - } - return previewSizes; - } - - /** - * プレビューのサイズを文字列に変換します. - * - * @param previewSize プレビューサイズ - * @return 文字列 - */ - private String getValueFromSize(Size previewSize) { - return previewSize.getWidth() + " x " + previewSize.getHeight(); - } - - /** - * 文字列を Size に変換します. - * - * Size に変換できなかった場合には null を返却します。 - * - * @param value 文字列のサイズ - * @return サイズ - */ - private Size getSizeFromValue(String value) { - String[] t = value.split("x"); - if (t.length == 2) { - try { - int w = Integer.parseInt(t[0].trim()); - int h = Integer.parseInt(t[1].trim()); - return new Size(w, h); - } catch (Exception e) { - return null; - } - } - return null; - } - - /** - * 切り抜き範囲の値を取得します. - * - * 未設定の場合には null を返却します。 - * - * @param key キー - * @return 切り抜き範囲 - */ - private Integer getDrawingRange(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - try { - return Integer.parseInt(pref.getText()); - } catch (NumberFormatException e) { - // ignore. - } - } - return null; - } - - /** - * 設定が変更された時に呼び出されるリスナー. - */ - private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { - if (mMediaRecorder == null) { - return false; - } - - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); - - String key = preference.getKey(); - if ("camera_preview_size".equals(key)) { - Size size = getSizeFromValue((String) newValue); - if (size != null) { - settings.setPreviewSize(size); - } - } else if ("port".equalsIgnoreCase(key)) { - setPreviewServerUrl(Integer.parseInt((String) newValue)); - } else if ("preview_clip_left".equalsIgnoreCase(key)) { - try { - int clipLeft = Integer.parseInt((String) newValue); - Integer clipRight = getDrawingRange("preview_clip_right"); - if (clipRight != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_top".equalsIgnoreCase(key)) { - try { - int clipTop = Integer.parseInt((String) newValue); - Integer clipBottom = getDrawingRange("preview_clip_bottom"); - if (clipBottom != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_right".equalsIgnoreCase(key)) { - try { - int clipRight = Integer.parseInt((String) newValue); - Integer clipLeft = getDrawingRange("preview_clip_left"); - if (clipLeft != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { - try { - int clipBottom = Integer.parseInt((String) newValue); - Integer clipTop = getDrawingRange("preview_clip_top"); - if (clipTop != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } - return true; - }; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java index 23864e790f..cf7515b04a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -1,31 +1,13 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; import android.os.Bundle; -import android.util.Size; -import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; import org.deviceconnect.android.deviceplugin.host.R; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; -import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class SettingsRTSPFragment extends SettingsParameterFragment { - private HostMediaRecorder mMediaRecorder; - +public class SettingsRTSPFragment extends SettingsEncoderFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName(getEncoderId()); @@ -33,398 +15,11 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } @Override - public void onBindService() { - mMediaRecorder = getRecorder(); - - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); - - setPreviewServerPort(); - setPreviewServerUrl(settings.getPort()); - setPreviewSizePreference(settings); - setPreviewVideoEncoderPreference(settings); - setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); - - setPreviewCutOutReset(); - - setInputTypeNumber("preview_framerate"); - setInputTypeNumber("preview_bitrate"); - setInputTypeNumber("preview_i_frame_interval"); - setInputTypeNumber("preview_intra_refresh"); - - setPreviewClipPreference("preview_clip_left"); - setPreviewClipPreference("preview_clip_top"); - setPreviewClipPreference("preview_clip_right"); - setPreviewClipPreference("preview_clip_bottom"); - } - - private HostMediaRecorder.EncoderSettings getStreamingSetting() { - HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getEncoderSetting(getEncoderId()); - } - - private void setPreviewServerPort() { - setInputTypeNumber("port"); - EditTextPreference pref = findPreference("port"); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - } - } - - private void setPreviewServerUrl(int port) { + protected void setPreviewServerUrl(int port) { EditTextPreference pref = findPreference("url"); if (pref != null) { String ipAddress = NetworkUtil.getIPAddress(requireContext()); pref.setText("rtsp://" + ipAddress + ":" + port); } } - - /** - * 切り抜き範囲の設定にリスナーを設定します. - * - * @param key キー - */ - public void setPreviewClipPreference(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - setInputTypeNumber(key); - } - } - - private void setEmptyText(String key) { - EditTextPreference left = findPreference(key); - if (left != null) { - left.setText(null); - } - } - - /** - * 切り抜き範囲のリセットボタンのリスナーを設定します. - */ - private void setPreviewCutOutReset() { - PreferenceScreen pref = findPreference("preview_clip_reset"); - if (pref != null) { - pref.setOnPreferenceClickListener(preference -> { - setEmptyText("preview_clip_left"); - setEmptyText("preview_clip_top"); - setEmptyText("preview_clip_right"); - setEmptyText("preview_clip_bottom"); - getStreamingSetting().setCropRect(null); - return false; - }); - } - } - - /** - * プレビューの解像度 Preference を作成します. - * - * @param settings レコーダの設定 - */ - private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { - ListPreference pref = findPreference("camera_preview_size"); - if (pref != null) { - List previewSizes = getSupportedPreviewSizes(); - if (!previewSizes.isEmpty()) { - List entryValues = new ArrayList<>(); - for (Size preview : previewSizes) { - entryValues.add(getValueFromSize(preview)); - } - - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - - Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - pref.setValue(getValueFromSize(previewSize)); - } - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * エンコーダの設定を行います. - * - * @param settings レコーダ設定 - */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.EncoderSettings settings) { - ListPreference pref = findPreference("preview_encoder"); - if (pref != null) { - List list = settings.getSupportedVideoEncoders(); - if (!list.isEmpty()) { - List entryValues = new ArrayList<>(list); - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * エンコーダのプロファイルとレベルを設定します. - * - * @param settings レコーダ設定 - * @param videoCodec エンコーダ - * @param reset リセットフラグ - */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.EncoderSettings settings, HostMediaRecorder.VideoCodec videoCodec, boolean reset) { - ListPreference pref = findPreference("preview_profile_level"); - if (pref != null) { - List list = CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType()); - if (!list.isEmpty()) { - List entryValues = new ArrayList<>(); - entryValues.add("none"); - - for (HostMediaRecorder.ProfileLevel pl : list) { - String value = getProfileLevel(videoCodec, pl); - if (value != null) { - entryValues.add(value); - } - } - - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - - if (reset) { - pref.setValue("none"); - } else { - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); - if (pl != null) { - pref.setValue(getProfileLevel(videoCodec, pl)); - } - } - - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * サイズの小さい方からソートを行うための比較演算子. - */ - private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { - // We cast here to ensure the multiplications won't overflow - return Long.signum((long) lhs.getWidth() * lhs.getHeight() - - (long) rhs.getWidth() * rhs.getHeight()); - }; - - /** - * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. - * - * @return サポートしているプレビューサイズのリスト - */ - @NonNull - private List getSupportedPreviewSizes() { - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); - List previewSizes = new ArrayList<>(); - if (settings != null) { - previewSizes.addAll(settings.getSupportedPreviewSizes()); - Collections.sort(previewSizes, SIZE_COMPARATOR); - } - return previewSizes; - } - - /** - * プレビューのサイズを文字列に変換します. - * - * @param previewSize プレビューサイズ - * @return 文字列 - */ - private String getValueFromSize(Size previewSize) { - return previewSize.getWidth() + " x " + previewSize.getHeight(); - } - - /** - * 文字列を Size に変換します. - * - * Size に変換できなかった場合には null を返却します。 - * - * @param value 文字列のサイズ - * @return サイズ - */ - private Size getSizeFromValue(String value) { - String[] t = value.split("x"); - if (t.length == 2) { - try { - int w = Integer.parseInt(t[0].trim()); - int h = Integer.parseInt(t[1].trim()); - return new Size(w, h); - } catch (Exception e) { - return null; - } - } - return null; - } - - /** - * プロファイルとレベルを文字列に変換します. - * - * @param encoderName エンコーダ - * @param pl プロファイルとレベル - * @return 文字列 - */ - private String getProfileLevel(HostMediaRecorder.VideoCodec encoderName, HostMediaRecorder.ProfileLevel pl) { - switch (encoderName) { - case H264: { - H264Profile p = H264Profile.valueOf(pl.getProfile()); - H264Level l = H264Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - return p.getName() + " - " + l.getName(); - } - } - case H265: { - H265Profile p = H265Profile.valueOf(pl.getProfile()); - H265Level l = H265Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - return p.getName() + " - " + l.getName(); - } - } - } - return null; - } - - /** - * 文字列をプロファイルとレベルに変換します. - * - * プロファイルとレベルに変換できなかった場合には、null を返却します。 - * - * @param encoderName エンコーダ - * @param value 変換する文字列 - * @return プロファイルとレベル - */ - private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoCodec encoderName, String value) { - String[] t = value.split("-"); - if (t.length == 2) { - try { - String profile = t[0].trim(); - String level = t[1].trim(); - switch (encoderName) { - case H264: { - H264Profile p = H264Profile.nameOf(profile); - H264Level l = H264Level.nameOf(level); - if (p != null && l != null) { - return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } - } - case H265: { - H265Profile p = H265Profile.nameOf(profile); - H265Level l = H265Level.nameOf(level); - if (p != null && l != null) { - return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } - } - } - } catch (Exception e) { - return null; - } - } - return null; - } - - /** - * 切り抜き範囲の値を取得します. - * - * 未設定の場合には null を返却します。 - * - * @param key キー - * @return 切り抜き範囲 - */ - private Integer getDrawingRange(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - try { - return Integer.parseInt(pref.getText()); - } catch (NumberFormatException e) { - // ignore. - } - } - return null; - } - - /** - * 設定が変更された時に呼び出されるリスナー. - */ - private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { - if (mMediaRecorder == null) { - return false; - } - - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); - - String key = preference.getKey(); - if ("camera_preview_size".equals(key)) { - Size size = getSizeFromValue((String) newValue); - if (size != null) { - settings.setPreviewSize(size); - } - } else if ("port".equalsIgnoreCase(key)) { - setPreviewServerUrl(Integer.parseInt((String) newValue)); - } else if ("preview_encoder".equals(key)) { - // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく - try { - settings.setProfileLevel(null); - } catch (Exception e) { - return false; - } - HostMediaRecorder.VideoCodec encoderName = - HostMediaRecorder.VideoCodec.nameOf((String) newValue); - setPreviewProfileLevelPreference(settings, encoderName, true); - } else if ("preview_profile_level".equalsIgnoreCase(key)) { - try { - settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); - } catch (Exception e) { - return false; - } - } else if ("preview_clip_left".equalsIgnoreCase(key)) { - try { - int clipLeft = Integer.parseInt((String) newValue); - Integer clipRight = getDrawingRange("preview_clip_right"); - if (clipRight != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_top".equalsIgnoreCase(key)) { - try { - int clipTop = Integer.parseInt((String) newValue); - Integer clipBottom = getDrawingRange("preview_clip_bottom"); - if (clipBottom != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_right".equalsIgnoreCase(key)) { - try { - int clipRight = Integer.parseInt((String) newValue); - Integer clipLeft = getDrawingRange("preview_clip_left"); - if (clipLeft != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { - try { - int clipBottom = Integer.parseInt((String) newValue); - Integer clipTop = getDrawingRange("preview_clip_top"); - if (clipTop != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } - return true; - }; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index ed23fa49eb..722a2af113 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -2,31 +2,14 @@ import android.os.Bundle; import android.text.TextUtils; -import android.util.Size; -import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; import org.deviceconnect.android.deviceplugin.host.R; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; -import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Profile; -import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; -import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.util.NetworkUtil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class SettingsSRTFragment extends SettingsParameterFragment { - private HostMediaRecorder mMediaRecorder; - +public class SettingsSRTFragment extends SettingsEncoderFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName(getEncoderId()); @@ -45,33 +28,12 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } @Override - public void onBindService() { - mMediaRecorder = getRecorder(); - - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); - - setPreviewServerPort(); - setPreviewServerUrl(settings.getPort()); - setPreviewSizePreference(settings); - setPreviewVideoEncoderPreference(settings); - setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); - - setPreviewCutOutReset(); - - setInputTypeNumber("preview_framerate"); - setInputTypeNumber("preview_bitrate"); - setInputTypeNumber("preview_i_frame_interval"); - setInputTypeNumber("preview_intra_refresh"); - - setPreviewClipPreference("preview_clip_left"); - setPreviewClipPreference("preview_clip_top"); - setPreviewClipPreference("preview_clip_right"); - setPreviewClipPreference("preview_clip_bottom"); - } - - private HostMediaRecorder.EncoderSettings getStreamingSetting() { - HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); - return s.getEncoderSetting(getEncoderId()); + protected void setPreviewServerUrl(int port) { + EditTextPreference pref = findPreference("url"); + if (pref != null) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + pref.setText("srt://" + ipAddress + ":" + port); + } } private void setSummaryOptionAuto(String name) { @@ -88,370 +50,4 @@ private void setSummaryOptionAuto(String name) { } return value; }; - - private void setPreviewServerPort() { - setInputTypeNumber("port"); - EditTextPreference pref = findPreference("port"); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - } - } - - private void setPreviewServerUrl(int port) { - EditTextPreference pref = findPreference("url"); - if (pref != null) { - String ipAddress = NetworkUtil.getIPAddress(requireContext()); - pref.setText("srt://" + ipAddress + ":" + port); - } - } - - /** - * 切り抜き範囲の設定にリスナーを設定します. - * - * @param key キー - */ - public void setPreviewClipPreference(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - setInputTypeNumber(key); - } - } - - private void setEmptyText(String key) { - EditTextPreference left = findPreference(key); - if (left != null) { - left.setText(null); - } - } - - /** - * 切り抜き範囲のリセットボタンのリスナーを設定します. - */ - private void setPreviewCutOutReset() { - PreferenceScreen pref = findPreference("preview_clip_reset"); - if (pref != null) { - pref.setOnPreferenceClickListener(preference -> { - setEmptyText("preview_clip_left"); - setEmptyText("preview_clip_top"); - setEmptyText("preview_clip_right"); - setEmptyText("preview_clip_bottom"); - getStreamingSetting().setCropRect(null); - return false; - }); - } - } - - /** - * プレビューの解像度 Preference を作成します. - * - * @param settings レコーダの設定 - */ - private void setPreviewSizePreference(HostMediaRecorder.EncoderSettings settings) { - ListPreference pref = findPreference("camera_preview_size"); - if (pref != null) { - List previewSizes = getSupportedPreviewSizes(); - if (!previewSizes.isEmpty()) { - List entryValues = new ArrayList<>(); - for (Size preview : previewSizes) { - entryValues.add(getValueFromSize(preview)); - } - - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - - Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - pref.setValue(getValueFromSize(previewSize)); - } - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * エンコーダの設定を行います. - * - * @param settings レコーダ設定 - */ - private void setPreviewVideoEncoderPreference(HostMediaRecorder.EncoderSettings settings) { - ListPreference pref = findPreference("preview_encoder"); - if (pref != null) { - List list = settings.getSupportedVideoEncoders(); - if (!list.isEmpty()) { - List entryValues = new ArrayList<>(list); - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * エンコーダのプロファイルとレベルを設定します. - * - * @param settings レコーダ設定 - * @param videoCodec エンコーダ - * @param reset リセットフラグ - */ - private void setPreviewProfileLevelPreference(HostMediaRecorder.EncoderSettings settings, HostMediaRecorder.VideoCodec videoCodec, boolean reset) { - ListPreference pref = findPreference("preview_profile_level"); - if (pref != null) { - List list = CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType()); - if (!list.isEmpty()) { - List entryValues = new ArrayList<>(); - entryValues.add("none"); - - for (HostMediaRecorder.ProfileLevel pl : list) { - String value = getProfileLevel(videoCodec, pl); - if (value != null) { - entryValues.add(value); - } - } - - pref.setEntries(entryValues.toArray(new String[0])); - pref.setEntryValues(entryValues.toArray(new String[0])); - pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); - - if (reset) { - pref.setValue("none"); - } else { - HostMediaRecorder.ProfileLevel pl = settings.getProfileLevel(); - if (pl != null) { - pref.setValue(getProfileLevel(videoCodec, pl)); - } - } - - pref.setVisible(true); - } else { - pref.setEnabled(false); - } - } - } - - /** - * サイズの小さい方からソートを行うための比較演算子. - */ - private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { - // We cast here to ensure the multiplications won't overflow - return Long.signum((long) lhs.getWidth() * lhs.getHeight() - - (long) rhs.getWidth() * rhs.getHeight()); - }; - - /** - * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します. - * - * @return サポートしているプレビューサイズのリスト - */ - @NonNull - private List getSupportedPreviewSizes() { - HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); - List previewSizes = new ArrayList<>(); - if (settings != null) { - previewSizes.addAll(settings.getSupportedPreviewSizes()); - Collections.sort(previewSizes, SIZE_COMPARATOR); - } - return previewSizes; - } - - /** - * プレビューのサイズを文字列に変換します. - * - * @param previewSize プレビューサイズ - * @return 文字列 - */ - private String getValueFromSize(Size previewSize) { - return previewSize.getWidth() + " x " + previewSize.getHeight(); - } - - /** - * 文字列を Size に変換します. - * - * Size に変換できなかった場合には null を返却します。 - * - * @param value 文字列のサイズ - * @return サイズ - */ - private Size getSizeFromValue(String value) { - String[] t = value.split("x"); - if (t.length == 2) { - try { - int w = Integer.parseInt(t[0].trim()); - int h = Integer.parseInt(t[1].trim()); - return new Size(w, h); - } catch (Exception e) { - return null; - } - } - return null; - } - - /** - * プロファイルとレベルを文字列に変換します. - * - * @param encoderName エンコーダ - * @param pl プロファイルとレベル - * @return 文字列 - */ - private String getProfileLevel(HostMediaRecorder.VideoCodec encoderName, HostMediaRecorder.ProfileLevel pl) { - switch (encoderName) { - case H264: { - H264Profile p = H264Profile.valueOf(pl.getProfile()); - H264Level l = H264Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - return p.getName() + " - " + l.getName(); - } - } - case H265: { - H265Profile p = H265Profile.valueOf(pl.getProfile()); - H265Level l = H265Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - return p.getName() + " - " + l.getName(); - } - } - } - return null; - } - - /** - * 文字列をプロファイルとレベルに変換します. - * - * プロファイルとレベルに変換できなかった場合には、null を返却します。 - * - * @param encoderName エンコーダ - * @param value 変換する文字列 - * @return プロファイルとレベル - */ - private HostMediaRecorder.ProfileLevel getProfileLevel(HostMediaRecorder.VideoCodec encoderName, String value) { - String[] t = value.split("-"); - if (t.length == 2) { - try { - String profile = t[0].trim(); - String level = t[1].trim(); - switch (encoderName) { - case H264: { - H264Profile p = H264Profile.nameOf(profile); - H264Level l = H264Level.nameOf(level); - if (p != null && l != null) { - return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } - } - case H265: { - H265Profile p = H265Profile.nameOf(profile); - H265Level l = H265Level.nameOf(level); - if (p != null && l != null) { - return new HostMediaRecorder.ProfileLevel(p.getValue(), l.getValue()); - } - } - } - } catch (Exception e) { - return null; - } - } - return null; - } - - /** - * 切り抜き範囲の値を取得します. - * - * 未設定の場合には null を返却します。 - * - * @param key キー - * @return 切り抜き範囲 - */ - private Integer getDrawingRange(String key) { - EditTextPreference pref = findPreference(key); - if (pref != null) { - try { - return Integer.parseInt(pref.getText()); - } catch (NumberFormatException e) { - // ignore. - } - } - return null; - } - - /** - * 設定が変更された時に呼び出されるリスナー. - */ - private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> { - if (mMediaRecorder == null) { - return false; - } - - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); - - String key = preference.getKey(); - if ("camera_preview_size".equals(key)) { - Size size = getSizeFromValue((String) newValue); - if (size != null) { - settings.setPreviewSize(size); - } - } else if ("port".equalsIgnoreCase(key)) { - setPreviewServerUrl(Integer.parseInt((String) newValue)); - } else if ("preview_encoder".equals(key)) { - // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく - try { - settings.setProfileLevel(null); - } catch (Exception e) { - return false; - } - HostMediaRecorder.VideoCodec encoderName = - HostMediaRecorder.VideoCodec.nameOf((String) newValue); - setPreviewProfileLevelPreference(settings, encoderName, true); - } else if ("preview_profile_level".equalsIgnoreCase(key)) { - try { - settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue)); - } catch (Exception e) { - return false; - } - } else if ("preview_clip_left".equalsIgnoreCase(key)) { - try { - int clipLeft = Integer.parseInt((String) newValue); - Integer clipRight = getDrawingRange("preview_clip_right"); - if (clipRight != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_top".equalsIgnoreCase(key)) { - try { - int clipTop = Integer.parseInt((String) newValue); - Integer clipBottom = getDrawingRange("preview_clip_bottom"); - if (clipBottom != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_right".equalsIgnoreCase(key)) { - try { - int clipRight = Integer.parseInt((String) newValue); - Integer clipLeft = getDrawingRange("preview_clip_left"); - if (clipLeft != null && clipRight <= clipLeft) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } else if ("preview_clip_bottom".equalsIgnoreCase(key)) { - try { - int clipBottom = Integer.parseInt((String) newValue); - Integer clipTop = getDrawingRange("preview_clip_top"); - if (clipTop != null && clipBottom <= clipTop) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - } - return true; - }; } \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index b8a83b58fa..dc29382ad0 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -659,7 +659,6 @@ public boolean onRequest(final Intent request, final Intent response) { String target = getTarget(request); HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); return true; @@ -729,11 +728,11 @@ public boolean onRequest(final Intent request, final Intent response) { String target = getTarget(request); final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); return true; } + recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { @@ -769,7 +768,6 @@ public boolean onRequest(final Intent request, final Intent response) { final String target = getTarget(request); final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); return true; @@ -863,7 +861,7 @@ public boolean onRequest(Intent request, Intent response) { final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); return true; } @@ -1066,7 +1064,7 @@ public boolean onRequest(Intent request, Intent response) { HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); return true; } @@ -1229,7 +1227,7 @@ public boolean onRequest(Intent request, Intent response) { HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); + MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); return true; } @@ -1291,6 +1289,10 @@ public boolean onRequest(Intent request, Intent response) { } String encoderId = recorder.getId() + "-" + name; + if (!recorder.getSettings().existEncoderId(encoderId)) { + MessageUtils.setInvalidRequestParameterError(response, "name is not found."); + return true; + } if (left >= right || top >= bottom) { MessageUtils.setInvalidRequestParameterError(response, "parameter is invalid."); @@ -1356,6 +1358,10 @@ public boolean onRequest(Intent request, Intent response) { } String encoderId = recorder.getId() + "-" + name; + if (!recorder.getSettings().existEncoderId(encoderId)) { + MessageUtils.setInvalidRequestParameterError(response, "name is not found."); + return true; + } recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java index aa96d6baea..27ef74dc6a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -import android.content.Context; import android.util.Log; import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index 2deaf4cea1..a48e71d9d9 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -import android.content.Context; import android.util.Log; import org.deviceconnect.android.deviceplugin.host.BuildConfig; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index 49d36b9e37..fd7bcce684 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -884,6 +884,19 @@ public void setPreviewQuality(int quality) { mProperty.put("preview_jpeg_quality", quality); } + /** + * サポートしているエンコーダの解像度の最大値を取得します. + * + * @return サポートしている解像度の最大値 + */ + public Size getSupportedPreviewSize() { + VideoCodec codec = getPreviewEncoderName(); + if (codec != null) { + return CapabilityUtil.getSupportedMaxSize(codec.getMimeType()); + } + return CapabilityUtil.getSupportedMaxSize(VideoCodec.H264.getMimeType()); + } + /** * サポートしているエンコーダの解像度の最大値を取得します. * @@ -906,6 +919,15 @@ public boolean isSupportedPreviewSize(String mimeType, Size size) { return maxSize != null && (size.getWidth() <= maxSize.getWidth() && size.getHeight() <= maxSize.getHeight()); } + /** + * 画面に表示するプレビューのリストを取得します. + * + * @return プレビューリスト + */ + public List getSupportedEncoderSizes() { + return new ArrayList<>(); + } + /** * サポートしているエンコーダのリストを取得します. * @@ -1117,6 +1139,10 @@ public Settings(Context context, HostMediaRecorder recorder) { mProperty = new PropertyUtil(context, recorder.getId()); } + protected EncoderSettings createEncoderSettings(String encoderId) { + return new EncoderSettings(mContext, encoderId); + } + /** * 初期化されているか確認します. * @@ -1152,7 +1178,7 @@ public void clear() { public EncoderSettings getEncoderSetting(String encoderId) { List encoderSettingList = getEncoderIdList(); if (encoderSettingList.contains(encoderId)) { - return new EncoderSettings(mContext, encoderId); + return createEncoderSettings(encoderId); } return null; } @@ -1166,6 +1192,17 @@ public List getEncoderIdList() { return mProperty.getArrayString("encoder_id_list"); } + /** + * エンコーダ ID が存在するか確認します. + * + * @param encoderId エンコーダID + * @return 存在する場合はtrue、それ以外はfalse + */ + public boolean existEncoderId(String encoderId) { + List encorderIdList = getEncoderIdList(); + return encorderIdList.contains(encoderId); + } + /** * エンコーダを追加します. * @@ -1194,10 +1231,7 @@ public void removeEncoder(String encoderId) { } List encoderList = getEncoderIdList(); - Log.e("ABC", "#$$$$ " + encoderList); encoderList.remove(encoderId); - Log.e("ABC", "#$$$$ end: " + encoderList); - mProperty.put("encoder_id_list", encoderList); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index 7e007cfabf..61e8d2c833 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -28,6 +28,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.BroadcasterProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServerProvider; +import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.ImageUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.MP4Recorder; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; @@ -38,11 +39,35 @@ import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Locale; public class Camera2Recorder extends AbstractMediaRecorder { + protected static final List ENCODE_SIZE_LIST = Arrays.asList( + new Size(128, 96), + new Size(320, 240), + new Size(640, 480), + new Size(800, 600), + new Size(1024, 768), + new Size(1280, 960), + new Size(1440, 1080), + new Size(1600, 1200), + new Size(320, 180), + new Size(640, 360), + new Size(854, 480), + new Size(1280, 720), + new Size(1366, 768), + new Size(1920, 1080), + new Size(2560, 1440), + new Size(3840, 2160), + new Size(5760, 3240), + new Size(7680, 4320) + ); + /** * カメラターゲットIDの定義. */ @@ -149,6 +174,27 @@ public Camera2Recorder(Context context, CameraWrapper camera, FileManager fileMa mCamera2BroadcasterProvider = new Camera2BroadcasterProvider(context, this); } + /** + * サイズの小さい方からソートを行うための比較演算子. + */ + private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + }; + + private List getEncoderSizeList() { + Size maxSize = CapabilityUtil.getSupportedMaxSize(VideoCodec.H264.getMimeType()); + List sizes = new ArrayList<>(); + for (Size size : ENCODE_SIZE_LIST) { + if (size.getWidth() <= maxSize.getWidth() && size.getHeight() <= maxSize.getHeight()) { + sizes.add(size); + } + } + Collections.sort(sizes, SIZE_COMPARATOR); + return sizes; + } + /** * レコーダの設定を初期化します. */ @@ -158,6 +204,7 @@ private void initSupportedSettings() { List> supportedFpsList = options.getSupportedFpsList(); mSettings.mSupportedPictureSize = new ArrayList<>(options.getSupportedPictureSizeList()); mSettings.mSupportedPreviewSize = new ArrayList<>(options.getSupportedPreviewSizeList()); + mSettings.mSupportedEncoderSize = getEncoderSizeList(); if (!mSettings.isInitialized()) { // カメラ設定 @@ -518,11 +565,22 @@ public static CameraFacing detect(CameraWrapper cameraWrapper) { private class CameraSettings extends Settings { private List mSupportedPictureSize = new ArrayList<>(); private List mSupportedPreviewSize = new ArrayList<>(); + private List mSupportedEncoderSize; CameraSettings(Context context, HostMediaRecorder recorder) { super(context, recorder); } + @Override + protected EncoderSettings createEncoderSettings(String encoderId) { + return new EncoderSettings(getContext(), encoderId) { + @Override + public List getSupportedEncoderSizes() { + return mSupportedEncoderSize; + } + }; + } + @Override public List getSupportedPictureSizes() { return mSupportedPictureSize; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index 350af175b7..69d967c5c8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -28,6 +28,7 @@ import org.deviceconnect.android.deviceplugin.host.recorder.BroadcasterProvider; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import org.deviceconnect.android.deviceplugin.host.recorder.PreviewServerProvider; +import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil; import org.deviceconnect.android.deviceplugin.host.recorder.util.MP4Recorder; import org.deviceconnect.android.deviceplugin.host.recorder.util.MediaProjectionProvider; import org.deviceconnect.android.deviceplugin.host.recorder.util.SurfaceMP4Recorder; @@ -85,6 +86,18 @@ public ScreenCastRecorder(Context context, FileManager fileMgr, MediaProjectionP mScreenCastBroadcasterProvider = new ScreenCastBroadcasterProvider(context, this); } + private List getEncoderSizeList(List supportPreviewSizes) { + List sizes = new ArrayList<>(); + Size maxSize = CapabilityUtil.getSupportedMaxSize(VideoCodec.H264.getMimeType()); + for (Size size : supportPreviewSizes) { + if (size.getWidth() <= maxSize.getWidth() && size.getHeight() <= maxSize.getHeight() + || size.getWidth() <= maxSize.getHeight() && size.getHeight() <= maxSize.getWidth()) { + sizes.add(size); + } + } + return sizes; + } + /** * レコーダの設定を初期化します. */ @@ -111,6 +124,7 @@ private void initSupportedSettings() { } mSettings.mSupportedPreviewSize = supportPreviewSizes; mSettings.mSupportedPictureSize = supportPictureSizes; + mSettings.mSupportEncoderSizeList = getEncoderSizeList(supportPreviewSizes); List> supportFps = new ArrayList<>(); supportFps.add(new Range<>(30, 30)); @@ -370,15 +384,26 @@ private void takePhotoInternal(final @NonNull OnPhotoEventListener listener) { } } - private static class ScreenCastSettings extends Settings { + private class ScreenCastSettings extends Settings { private List mSupportedPictureSize = new ArrayList<>(); private List mSupportedPreviewSize = new ArrayList<>(); private List> mSupportedFps = new ArrayList<>(); + private List mSupportEncoderSizeList = new ArrayList<>(); ScreenCastSettings(Context context, HostMediaRecorder recorder) { super(context, recorder); } + @Override + protected EncoderSettings createEncoderSettings(String encoderId) { + return new EncoderSettings(getContext(), encoderId) { + @Override + public List getSupportedEncoderSizes() { + return mSupportEncoderSizeList; + } + }; + } + @Override public List getSupportedPictureSizes() { return mSupportedPictureSize; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java index abf34a884e..29400103e7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSRTPreviewServer.java @@ -6,8 +6,6 @@ */ package org.deviceconnect.android.deviceplugin.host.recorder.screen; -import android.content.Context; - import org.deviceconnect.android.deviceplugin.host.recorder.AbstractSRTPreviewServer; import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder; From 24b25be1a4abd2f90c9013414052af03ed59d23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 13 Sep 2021 11:17:08 +0900 Subject: [PATCH 20/40] =?UTF-8?q?=E3=83=86=E3=82=AF=E3=82=B9=E3=83=81?= =?UTF-8?q?=E3=83=A3=E3=82=92=E6=8F=8F=E7=94=BB=E3=81=99=E3=82=8B=E6=99=82?= =?UTF-8?q?=E3=81=AE=E3=83=9E=E3=82=A4=E3=83=8A=E3=82=B9=E3=81=AE=E5=BA=A7?= =?UTF-8?q?=E6=A8=99=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E6=99=82=E3=81=AE?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libmedia/streaming/gles/SurfaceTextureRenderer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java index 3f5d19b35d..6e1b6d5735 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java @@ -277,6 +277,14 @@ private void setCropRect(float l, float t, float r, float b) { ey = -2.0f / b + 1.0f; b = 1.0f; } + if (l < 0.0f) { + sx = sx - l; + l = 0.0f; + } + if (t < 0.0f) { + sy = sy - t; + t = 0.0f; + } float[] triangleVerticesData = { // X, Y, Z, U, V sx, sy, 0.f, l, (1 - t), From dee266d11bf5968b60a9b02d4469b1e346abc06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 13 Sep 2021 15:09:23 +0900 Subject: [PATCH 21/40] =?UTF-8?q?=E7=94=BB=E9=9D=A2=E3=81=8C=E5=9B=9E?= =?UTF-8?q?=E8=BB=A2=E3=81=95=E3=82=8C=E3=81=9F=E6=99=82=E3=81=AE=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=82=B3=E3=83=BC=E3=83=80=E5=86=8D=E8=B5=B7=E5=8B=95?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/recorder/AbstractLiveStreaming.java | 8 +++---- .../recorder/AbstractMJPEGPreviewServer.java | 1 - .../host/recorder/AbstractMediaRecorder.java | 24 +++++-------------- .../recorder/AbstractRTMPBroadcaster.java | 9 ------- .../recorder/AbstractRTSPPreviewServer.java | 6 ----- .../host/recorder/AbstractSRTBroadcaster.java | 9 ------- .../recorder/AbstractSRTPreviewServer.java | 6 ----- 7 files changed, 10 insertions(+), 53 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java index ead23adcc1..6f54725bf2 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java @@ -79,14 +79,14 @@ public String getId() { @Override public void setOnEventListener(LiveStreaming.OnEventListener listener) { - } @Override public void onConfigChange() { - VideoQuality videoQuality = getVideoQuality(); - if (videoQuality != null) { - setVideoQuality(videoQuality); + VideoEncoder videoEncoder = getVideoEncoder(); + if (videoEncoder != null) { + setVideoQuality(videoEncoder.getVideoQuality()); + videoEncoder.restart(); } AudioQuality audioQuality = getAudioQuality(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index 19242e76b5..863448df7d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -1,6 +1,5 @@ package org.deviceconnect.android.deviceplugin.host.recorder; -import android.content.Context; import android.util.Log; import android.util.Size; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java index 3c52ee03b7..9602d1a140 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java @@ -176,37 +176,25 @@ public boolean isPreviewRunning() { @Override public void onDisplayRotation(int rotation) { - // 画面が回転した場合には、一度描画用のスレッドを停止しておく - EGLSurfaceDrawingThread drawingThread = getSurfaceDrawingThread(); - if (drawingThread != null && drawingThread.isRunning()) { - drawingThread.stop(true); - } - - PreviewServerProvider previewServerProvider = getServerProvider(); - if (previewServerProvider != null) { - previewServerProvider.onConfigChange(); - } - - BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); - if (broadcasterProvider != null) { - broadcasterProvider.onConfigChange(); - } + onConfigChange(); } @Override public void onConfigChange() { + PreviewServerProvider previewServerProvider = getServerProvider(); + BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); + // 設定が変更された場合には、一度描画用のスレッドを停止しておく EGLSurfaceDrawingThread drawingThread = getSurfaceDrawingThread(); - if (drawingThread != null && drawingThread.isRunning()) { + if (drawingThread != null && drawingThread.isRunning() + && (previewServerProvider.isRunning() || broadcasterProvider.isRunning())) { drawingThread.stop(true); } - PreviewServerProvider previewServerProvider = getServerProvider(); if (previewServerProvider != null) { previewServerProvider.onConfigChange(); } - BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); if (broadcasterProvider != null) { broadcasterProvider.onConfigChange(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java index 0ef30a1317..01f813aeeb 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTMPBroadcaster.java @@ -129,15 +129,6 @@ public boolean isMuted() { return mRtmpClient != null && mRtmpClient.isMute(); } - @Override - public void onConfigChange() { - super.onConfigChange(); - - if (mRtmpClient != null) { - mRtmpClient.restartVideoEncoder(); - } - } - @Override protected VideoEncoder getVideoEncoder() { return mRtmpClient != null ? mRtmpClient.getVideoEncoder() : null; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java index 27ef74dc6a..bc9c1a9d70 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractRTSPPreviewServer.java @@ -76,12 +76,6 @@ public long getBPS() { return mRtspServer != null ? mRtspServer.getBPS() : 0; } - @Override - public void onConfigChange() { - super.onConfigChange(); - restartVideoStream(); - } - @Override public void setMute(boolean mute) { super.setMute(mute); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java index f7ee58772d..2e9b992e41 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTBroadcaster.java @@ -129,15 +129,6 @@ public boolean isMuted() { return mSrtClient != null && mSrtClient.isMute(); } - @Override - public void onConfigChange() { - super.onConfigChange(); - - if (mSrtClient != null) { - mSrtClient.restartVideoEncoder(); - } - } - @Override protected VideoEncoder getVideoEncoder() { return mSrtClient != null ? mSrtClient.getVideoEncoder() : null; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java index a48e71d9d9..ac726f1615 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractSRTPreviewServer.java @@ -75,12 +75,6 @@ public long getBPS() { return 0; } - @Override - public void onConfigChange() { - super.onConfigChange(); - restartVideoEncoder(); - } - @Override public void setMute(boolean mute) { super.setMute(mute); From 5771729c03f75436fd50f16aa6b0b9bd93c2990a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 14 Sep 2021 11:04:09 +0900 Subject: [PATCH 22/40] =?UTF-8?q?=E5=85=A8=E3=81=A6=E3=81=AE=20RTMP=20?= =?UTF-8?q?=E3=81=8C=E5=88=87=E6=96=AD=E3=81=95=E3=82=8C=E3=81=9F=E6=99=82?= =?UTF-8?q?=E3=81=AB=E5=81=9C=E6=AD=A2=E3=82=A4=E3=83=99=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E9=80=9A=E7=9F=A5=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractLiveStreamingProvider.java | 37 +++++++++++++------ .../host/recorder/ui/PreviewSurfaceView.java | 6 +-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java index e730c7592c..f364b5b502 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java @@ -85,7 +85,11 @@ public void onStopped() { @Override public void onError(Exception e) { - postPreviewError(streaming, e); + postOnError(streaming, e); + + if (isAllStreamingStopped()) { + stop(); + } } }); streaming.start(new LiveStreaming.OnStartCallback() { @@ -107,7 +111,7 @@ public void onFailed(Exception e) { if (results.size() > 0) { mIsRunning = true; sendNotification(mRecorder.getId(), mRecorder.getName()); - postPreviewStarted(results); + postOnStarted(results); } } catch (InterruptedException e) { // ignore. @@ -125,7 +129,7 @@ public void stop() { if (mIsRunning) { mIsRunning = false; - postPreviewStopped(); + postOnStopped(); } } @@ -181,6 +185,14 @@ public void release() { */ protected abstract void hideNotification(String id); + public Context getContext() { + return mContext; + } + + public HostMediaRecorder getRecorder() { + return mRecorder; + } + private void init() { HostMediaRecorder.Settings settings = getRecorder().getSettings(); for (String encoderId : settings.getEncoderIdList()) { @@ -194,27 +206,28 @@ private void init() { } } - public Context getContext() { - return mContext; - } - - public HostMediaRecorder getRecorder() { - return mRecorder; + private boolean isAllStreamingStopped() { + for (LiveStreaming streaming : mLiveStreamingList) { + if (streaming.isRunning()) { + return true; + } + } + return true; } - protected void postPreviewStarted(List servers) { + private void postOnStarted(List servers) { if (mOnEventListener != null) { mOnEventListener.onStarted(servers); } } - protected void postPreviewStopped() { + private void postOnStopped() { if (mOnEventListener != null) { mOnEventListener.onStopped(); } } - protected void postPreviewError(LiveStreaming server, Exception e) { + private void postOnError(LiveStreaming server, Exception e) { if (mOnEventListener != null) { mOnEventListener.onError(server, e); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java index 9cbe2f174f..f323199ea8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java @@ -4,7 +4,6 @@ import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Log; import android.util.Size; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -126,7 +125,7 @@ public void onScaleEnd(ScaleGestureDetector detector) { surfaceView.setOnTouchListener((view, event) -> { mScaleGestureDetector.onTouchEvent(event); - if (mScaleFlag) { + if (mScaleFlag || event.getPointerCount() > 1) { return true; } @@ -142,11 +141,10 @@ public void onScaleEnd(ScaleGestureDetector detector) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: { - clearFocusCropRect(); - for (CropRectHolder holder : mCropRectMap.values()) { Rect cropRect = holder.mCropRect; if (cropRect.contains(orgX, orgY)) { + clearFocusCropRect(); mDragStartX = x; mDragStartY = y; mDragFlag = true; From 05c6ca16a32d98c467fcda4e1730dcab039e80e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 21 Sep 2021 12:32:30 +0900 Subject: [PATCH 23/40] =?UTF-8?q?=E5=90=8C=E3=81=98=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=A8=E3=83=AC=E3=83=99?= =?UTF-8?q?=E3=83=AB=E3=81=AF=E3=83=AA=E3=82=B9=E3=83=88=E3=81=AB=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deviceplugin/host/recorder/util/CapabilityUtil.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java index 5fea7750d8..e63e1bdf44 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/CapabilityUtil.java @@ -44,7 +44,10 @@ public static List getSupportedProfileLevel(Stri MediaCodecInfo.CodecCapabilities codecCapabilities = codecInfo.getCapabilitiesForType(mimeType); if (codecCapabilities.profileLevels != null) { for (MediaCodecInfo.CodecProfileLevel c : codecCapabilities.profileLevels) { - list.add(new HostMediaRecorder.ProfileLevel(c.profile, c.level)); + HostMediaRecorder.ProfileLevel pl = new HostMediaRecorder.ProfileLevel(c.profile, c.level); + if (!list.contains(pl)) { + list.add(pl); + } } } } From 765f5e143a936576c6c208762aa3978b7268c2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 21 Sep 2021 12:33:31 +0900 Subject: [PATCH 24/40] =?UTF-8?q?=E3=83=97=E3=83=AD=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=A8=E3=83=AC=E3=83=99=E3=83=AB=E3=81=AE?= =?UTF-8?q?=E6=AF=94=E8=BC=83=E7=94=A8=E3=81=AB=20equals=20=E3=81=A8=20has?= =?UTF-8?q?hCode=20=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/recorder/HostMediaRecorder.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index fd7bcce684..d4efd93a31 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import javax.net.ssl.SSLContext; @@ -368,6 +369,20 @@ public int getProfile() { public int getLevel() { return mLevel; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProfileLevel that = (ProfileLevel) o; + return mProfile == that.mProfile && + mLevel == that.mLevel; + } + + @Override + public int hashCode() { + return Objects.hash(mProfile, mLevel); + } } enum AudioFilter { From 7e2e20d82a806350580ba1783abcce45eb3de260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 21 Sep 2021 12:34:08 +0900 Subject: [PATCH 25/40] =?UTF-8?q?VideoQuality=20=E3=81=A7=20low=5Flatency?= =?UTF-8?q?=20=E3=81=AE=E3=83=91=E3=83=A9=E3=83=A1=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=92=E6=B8=A1=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/recorder/ui/PreviewSurfaceView.java | 17 ++--- .../streaming/video/VideoEncoder.java | 62 +++++++++---------- .../streaming/video/VideoQuality.java | 2 +- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java index f323199ea8..a0a51c34a2 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/ui/PreviewSurfaceView.java @@ -4,6 +4,7 @@ import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Log; import android.util.Size; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -365,7 +366,8 @@ public void fullSurfaceView(int previewWidth, int previewHeight) { layoutParams.width = mSurfaceWidth; layoutParams.height = mSurfaceHeight; surfaceView.setLayoutParams(layoutParams); - surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); + surfaceView.getHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight); +// surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); }); } @@ -383,16 +385,17 @@ public void adjustSurfaceView(int previewWidth, int previewHeight) { Size viewSize = new Size(root.getWidth(), root.getHeight()); Size changeSize = calculateViewSize(previewWidth, previewHeight, viewSize); - ViewGroup.LayoutParams layoutParams = surfaceView.getLayoutParams(); - layoutParams.width = changeSize.getWidth(); - layoutParams.height = changeSize.getHeight(); - surfaceView.setLayoutParams(layoutParams); - surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); - mPreviewWidth = previewWidth; mPreviewHeight = previewHeight; mSurfaceWidth = changeSize.getWidth(); mSurfaceHeight = changeSize.getHeight(); + + ViewGroup.LayoutParams layoutParams = surfaceView.getLayoutParams(); + layoutParams.width = mSurfaceWidth; + layoutParams.height = mSurfaceHeight; + surfaceView.setLayoutParams(layoutParams); + surfaceView.getHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight); +// surfaceView.getHolder().setFixedSize(previewWidth, previewHeight); }); } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java index 9c06202a60..ee779cf15b 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java @@ -61,25 +61,6 @@ protected void release() { */ public abstract VideoQuality getVideoQuality(); - /** - * Low Latency の設定を要求します. - * - * @param lowLatency 1 の場合は、Low Latency を有効、0 の場合は、Low Latency を無効 - */ - public void requestLowLatency(int lowLatency) { - if (mMediaCodec != null) { - if (lowLatency == 1 || lowLatency == 0) { - Bundle b = new Bundle(); - b.putInt(MediaCodec.PARAMETER_KEY_LOW_LATENCY, lowLatency); - try { - mMediaCodec.setParameters(b); - } catch (Exception e) { - // ignore. - } - } - } - } - /** * キーフレームを要求します. */ @@ -235,11 +216,20 @@ private MediaCodec createMediaCodec(int colorFormat, int w, int h) throws IOExce } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // エンコーダのレイテンシーを設定します。 - // 機種依存でサポートされていない場合には、この値は無視されます。 - format.setInteger(MediaFormat.KEY_LATENCY, 0); + Integer lowLatency = videoQuality.getLowLatency(); + if (lowLatency != null) { + if (lowLatency == 1 || lowLatency == 0) { + // エンコーダのレイテンシーを設定します。 + // 機種依存でサポートされていない場合には、この値は無視されます。 + format.setInteger(MediaFormat.KEY_LATENCY, lowLatency); + } + } } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { +// format.setInteger(MediaFormat.KEY_MAX_FPS_TO_ENCODER, videoQuality.getFrameRate()); +// } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { int intraRefresh = videoQuality.getIntraRefresh(); if (intraRefresh != 0) { @@ -247,15 +237,6 @@ private MediaCodec createMediaCodec(int colorFormat, int w, int h) throws IOExce } } - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { - Integer lowLatency = videoQuality.getLowLatency(); - if (lowLatency != null) { - if (lowLatency == 1 || lowLatency == 0) { - format.setInteger(MediaFormat.KEY_LOW_LATENCY, lowLatency); - } - } - } - MediaCodec mediaCodec = MediaCodec.createByCodecName(codecInfo.getName()); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); return mediaCodec; @@ -347,7 +328,7 @@ private List getMediaCodecInfo(String mimeType, int colorFormat) } - private void printCodecInfo(MediaCodecInfo codecInfo) { + private static void printCodecInfo(MediaCodecInfo codecInfo) { Log.i(TAG, "CODEC: " + codecInfo.getName()); String[] types = codecInfo.getSupportedTypes(); @@ -364,6 +345,21 @@ private void printCodecInfo(MediaCodecInfo codecInfo) { Log.i(TAG, " FORMAT: " + format); } + MediaCodecInfo.EncoderCapabilities encoderCapabilities = capabilities.getEncoderCapabilities(); + if (encoderCapabilities != null) { + Log.i(TAG, " ----"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + Log.i(TAG, " Quality range: " + + encoderCapabilities.getQualityRange().getLower() + + " - " + + encoderCapabilities.getQualityRange().getUpper()); + } + Log.i(TAG, " Complexity range: " + + encoderCapabilities.getComplexityRange().getLower() + + " - " + + encoderCapabilities.getComplexityRange().getUpper()); + } + MediaCodecInfo.VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities(); if (videoCapabilities != null) { Log.i(TAG, " ----"); @@ -391,7 +387,7 @@ private void printCodecInfo(MediaCodecInfo codecInfo) { } } - private void printCodecInfo() { + public static void printCodecInfo() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS); for (MediaCodecInfo codecInfo : list.getCodecInfos()) { diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java index efa9e59b71..79433740ba 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoQuality.java @@ -16,7 +16,7 @@ public class VideoQuality { private int mIntraRefresh = 0; private int mProfile = 0; private int mLevel = 0; - private Integer mLowLatency; + private Integer mLowLatency = 1; /** * ビットレートのモード. From a22b18a14fa959ee296345ab8b3400d2bf334cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Mon, 4 Oct 2021 17:37:17 +0900 Subject: [PATCH 26/40] =?UTF-8?q?=E3=82=AB=E3=83=A1=E3=83=A9=E3=81=AE?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E7=94=BB=E9=9D=A2=E3=81=AB=E3=83=9C=E3=82=BF?= =?UTF-8?q?=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0=20=E3=82=AF=E3=83=AA?= =?UTF-8?q?=E3=83=83=E3=83=97=E7=AF=84=E5=9B=B2=E6=8C=87=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0=20/gotap?= =?UTF-8?q?i/mediaStreamingRecording/options:PUT=E3=81=A7=E3=80=81?= =?UTF-8?q?=E3=83=9E=E3=82=A4=E3=83=A0=E3=82=BF=E3=82=A4=E3=83=97=E3=81=94?= =?UTF-8?q?=E3=81=A8=E3=81=AB=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recorder/settings/SettingsActivity.java | 2 + .../settings/SettingsBroadcastFragment.java | 31 +++- .../settings/SettingsEncoderFragment.java | 71 +++++++- .../settings/SettingsMJPEGFragment.java | 9 +- .../settings/SettingsPortFragment.java | 20 --- .../settings/SettingsRTSPFragment.java | 9 +- .../settings/SettingsSRTFragment.java | 9 +- .../HostMediaStreamingRecordingProfile.java | 151 +++++++++++------- .../recorder/AbstractBroadcastProvider.java | 9 ++ .../host/recorder/AbstractLiveStreaming.java | 16 +- .../recorder/AbstractMJPEGPreviewServer.java | 14 +- .../host/recorder/HostMediaRecorder.java | 4 +- .../host/recorder/LiveStreamingProvider.java | 7 + .../host/recorder/PreviewServerProvider.java | 7 +- .../host/recorder/camera/Camera2Recorder.java | 1 + .../screen/ScreenCastRTSPPreviewServer.java | 4 +- .../recorder/screen/ScreenCastRecorder.java | 1 + .../app/src/main/res/values-ja/strings.xml | 2 + .../app/src/main/res/values/strings.xml | 2 + .../xml/settings_host_recorder_broadcast.xml | 6 + .../res/xml/settings_host_recorder_mjpeg.xml | 10 +- .../res/xml/settings_host_recorder_port.xml | 46 ------ .../res/xml/settings_host_recorder_rtsp.xml | 10 +- .../res/xml/settings_host_recorder_srt.xml | 10 +- .../streaming/mjpeg/MJPEGQuality.java | 12 +- .../streaming/mjpeg/SurfaceMJPEGEncoder.java | 9 +- .../streaming/rtsp/session/RtspSession.java | 26 +-- 27 files changed, 300 insertions(+), 198 deletions(-) delete mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java delete mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_port.xml diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java index 4f4b9b7228..d32dde4925 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java @@ -25,6 +25,8 @@ public void onCreate(Bundle savedInstanceState) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setTitle(""); } + + setRequestedOrientation(getDisplayOrientation()); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java index bc940fbcd6..41b78986ff 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsBroadcastFragment.java @@ -35,13 +35,14 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onBindService() { mMediaRecorder = getRecorder(); - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); setPreviewSizePreference(settings); setPreviewVideoEncoderPreference(settings); setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); setPreviewCutOutReset(); + setPreviewCutOutSet(); setInputTypeNumber("preview_framerate"); setInputTypeNumber("preview_bitrate"); @@ -54,7 +55,7 @@ public void onBindService() { setPreviewClipPreference("preview_clip_bottom"); } - private HostMediaRecorder.EncoderSettings getStreamingSetting() { + private HostMediaRecorder.EncoderSettings getEncoderSetting() { HostMediaRecorder.Settings s = mMediaRecorder.getSettings(); return s.getEncoderSetting(getEncoderId()); } @@ -79,6 +80,13 @@ private void setEmptyText(String key) { } } + private void setPreviewClip(String key, Integer value) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setText(String.valueOf(value)); + } + } + /** * 切り抜き範囲のリセットボタンのリスナーを設定します. */ @@ -90,7 +98,22 @@ private void setPreviewCutOutReset() { setEmptyText("preview_clip_top"); setEmptyText("preview_clip_right"); setEmptyText("preview_clip_bottom"); - getStreamingSetting().setCropRect(null); + getEncoderSetting().setCropRect(null); + return false; + }); + } + } + + private void setPreviewCutOutSet() { + PreferenceScreen pref = findPreference("preview_clip_set"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); + Size previewSize = settings.getPreviewSize(); + setPreviewClip("preview_clip_left", 0); + setPreviewClip("preview_clip_top", 0); + setPreviewClip("preview_clip_right", previewSize.getWidth()); + setPreviewClip("preview_clip_bottom", previewSize.getHeight()); return false; }); } @@ -339,7 +362,7 @@ private Integer getCropRect(String key) { return false; } - HostMediaRecorder.EncoderSettings settings = getStreamingSetting(); + HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); String key = preference.getKey(); if ("camera_preview_size".equals(key)) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java index 404155e344..8482301a10 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java @@ -1,6 +1,10 @@ package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; import android.util.Size; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; @@ -8,6 +12,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import org.deviceconnect.android.deviceplugin.host.R; import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Level; import org.deviceconnect.android.deviceplugin.host.profile.utils.H264Profile; import org.deviceconnect.android.deviceplugin.host.profile.utils.H265Level; @@ -36,6 +41,7 @@ public void onBindService() { setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false); setPreviewCutOutReset(); + setPreviewCutOutSet(); setInputTypeNumber("preview_framerate"); setInputTypeNumber("preview_bitrate"); @@ -55,13 +61,38 @@ protected HostMediaRecorder.EncoderSettings getEncoderSetting() { private void setPreviewServerPort() { setInputTypeNumber("port"); + EditTextPreference pref = findPreference("port"); if (pref != null) { pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener); } } - protected abstract void setPreviewServerUrl(int port); + /** + * サーバへのURLを取得します. + * + * @param port ポート番号 + * @return サーバへのURL + */ + protected abstract String getServerUrl(int port); + + /** + * サーバへのURLを設定します. + * + * @param port ポート番号 + */ + private void setPreviewServerUrl(int port) { + PreferenceScreen pref = findPreference("url"); + if (pref != null) { + String url = getServerUrl(port); + pref.setOnPreferenceClickListener(preference -> { + copyToClipboard(requireContext(), "Host Plugin - url", url); + Toast.makeText(requireContext(), R.string.host_recorder_settings_clipboard_copy, Toast.LENGTH_SHORT).show(); + return false; + }); + pref.setSummary(url); + } + } /** * 切り抜き範囲の設定にリスナーを設定します. @@ -83,6 +114,13 @@ private void setEmptyText(String key) { } } + private void setPreviewClip(String key, Integer value) { + EditTextPreference pref = findPreference(key); + if (pref != null) { + pref.setText(String.valueOf(value)); + } + } + /** * 切り抜き範囲のリセットボタンのリスナーを設定します. */ @@ -100,6 +138,21 @@ private void setPreviewCutOutReset() { } } + private void setPreviewCutOutSet() { + PreferenceScreen pref = findPreference("preview_clip_set"); + if (pref != null) { + pref.setOnPreferenceClickListener(preference -> { + HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); + Size previewSize = settings.getPreviewSize(); + setPreviewClip("preview_clip_left", 0); + setPreviewClip("preview_clip_top", 0); + setPreviewClip("preview_clip_right", previewSize.getWidth()); + setPreviewClip("preview_clip_bottom", previewSize.getHeight()); + return false; + }); + } + } + /** * プレビューの解像度 Preference を作成します. * @@ -333,6 +386,22 @@ private Integer getDrawingRange(String key) { return null; } + /** + * クリップボードにテキストをコピーします. + * + * @param context コンテキスト + * @param label ラベル + * @param text コピーするテキスト + */ + private static void copyToClipboard(Context context, String label, String text) { + ClipboardManager clipboardManager = + (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipboardManager == null) { + return; + } + clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text)); + } + /** * 設定が変更された時に呼び出されるリスナー. */ diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java index cf9d408a32..8cca327369 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMJPEGFragment.java @@ -22,12 +22,9 @@ public void onBindService() { } @Override - protected void setPreviewServerUrl(int port) { - EditTextPreference pref = findPreference("url"); - if (pref != null) { - String ipAddress = NetworkUtil.getIPAddress(requireContext()); - pref.setText("http://" + ipAddress + ":" + port + "/mjpeg"); - } + protected String getServerUrl(int port) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + return "http://" + ipAddress + ":" + port + "/mjpeg"; } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java deleted file mode 100644 index faf2aa1327..0000000000 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; - -import android.os.Bundle; - -import org.deviceconnect.android.deviceplugin.host.R; - -public class SettingsPortFragment extends SettingsBaseFragment { - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId()); - setPreferencesFromResource(R.xml.settings_host_recorder_port, rootKey); - } - - @Override - public void onBindService() { - setInputTypeNumber("mjpeg_port"); - setInputTypeNumber("rtsp_port"); - setInputTypeNumber("srt_port"); - } -} \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java index cf7515b04a..97edc2d2e6 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsRTSPFragment.java @@ -15,11 +15,8 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } @Override - protected void setPreviewServerUrl(int port) { - EditTextPreference pref = findPreference("url"); - if (pref != null) { - String ipAddress = NetworkUtil.getIPAddress(requireContext()); - pref.setText("rtsp://" + ipAddress + ":" + port); - } + protected String getServerUrl(int port) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + return "rtsp://" + ipAddress + ":" + port; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java index 722a2af113..6e1c971235 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsSRTFragment.java @@ -28,12 +28,9 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } @Override - protected void setPreviewServerUrl(int port) { - EditTextPreference pref = findPreference("url"); - if (pref != null) { - String ipAddress = NetworkUtil.getIPAddress(requireContext()); - pref.setText("srt://" + ipAddress + ":" + port); - } + protected String getServerUrl(int port) { + String ipAddress = NetworkUtil.getIPAddress(requireContext()); + return "srt://" + ipAddress + ":" + port; } private void setSummaryOptionAuto(String name) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index dc29382ad0..63017dff80 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -151,59 +151,12 @@ public boolean onRequest(final Intent request, final Intent response) { @Override public void onAllowed() { List recorders = new LinkedList<>(); - for (HostMediaRecorder recorder : mRecorderMgr.getRecorders()) { - HostMediaRecorder.Settings settings = recorder.getSettings(); - - Bundle info = new Bundle(); - setRecorderId(info, recorder.getId()); - setRecorderName(info, recorder.getName()); - setRecorderMIMEType(info, recorder.getMimeType()); - - if (recorder.getState() == HostMediaRecorder.State.RECORDING) { - setRecorderState(info, RecorderState.RECORDING); - } else { - setRecorderState(info, RecorderState.INACTIVE); - } - - // 静止画の解像度 - Size pictureSize = settings.getPictureSize(); - if (pictureSize != null) { - setRecorderImageWidth(info, pictureSize.getWidth()); - setRecorderImageHeight(info, pictureSize.getHeight()); - } - - // プレビュー解像度 - Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - setRecorderPreviewWidth(info, previewSize.getWidth()); - setRecorderPreviewHeight(info, previewSize.getHeight()); - } - - // カメラのフレームレート - Range previewFps = settings.getPreviewFps(); - if (previewFps != null) { - info.putString("previewFps", previewFps.getLower() + "-" + previewFps.getUpper()); - info.putInt("previewMaxFrameRate", previewFps.getUpper()); - } - - // エンコーダ設定 - List encoders = new ArrayList<>(); - for (String encoderId : settings.getEncoderIdList()) { - HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(encoderId); - if (s != null) { - encoders.add(createVideoEncoder(s)); - } + if (target == null) { + for (HostMediaRecorder recorder : mRecorderMgr.getRecorders()) { + recorders.add(createMediaRecorderInfo(recorder)); } - info.putParcelableArray("encoders", encoders.toArray(new Bundle[0])); - - info.putString("audioSource", settings.getPreviewAudioSource().getValue()); - info.putInt("audioBitrate", settings.getPreviewAudioBitRate() / 1024); - info.putInt("audioSampleRate", settings.getPreviewSampleRate()); - info.putInt("audioChannel", settings.getPreviewChannel()); - info.putBoolean("audioEchoCanceler", settings.isUseAEC()); - - setRecorderConfig(info, ""); - recorders.add(info); + } else { + recorders.add(createMediaRecorderInfo(recorder)); } setRecorders(response, recorders.toArray(new Bundle[0])); setResult(response, DConnectMessage.RESULT_OK); @@ -247,7 +200,7 @@ public void onAllowed() { setSupportedPreviewSizes(response, settings.getSupportedPreviewSizes()); setSupportedVideoEncoders(response, settings.getSupportedVideoEncoders()); setSupportedFps(response, settings.getSupportedFps()); - } else if (recorder.getMimeType().startsWith("audio/")) { +// } else if (recorder.getMimeType().startsWith("audio/")) { // 音声系の設定 } @@ -1507,6 +1460,68 @@ private HostDevicePlugin getHostDevicePlugin() { return (HostDevicePlugin) getContext(); } + private Bundle createMediaRecorderInfo(HostMediaRecorder recorder) { + HostMediaRecorder.Settings settings = recorder.getSettings(); + + Bundle info = new Bundle(); + setRecorderId(info, recorder.getId()); + setRecorderName(info, recorder.getName()); + setRecorderMIMEType(info, recorder.getMimeType()); + + if (recorder.getState() == HostMediaRecorder.State.RECORDING) { + setRecorderState(info, RecorderState.RECORDING); + } else { + setRecorderState(info, RecorderState.INACTIVE); + } + + // 静止画の解像度 + Size pictureSize = settings.getPictureSize(); + if (pictureSize != null) { + setRecorderImageWidth(info, pictureSize.getWidth()); + setRecorderImageHeight(info, pictureSize.getHeight()); + } + + // プレビュー解像度 + Size previewSize = settings.getPreviewSize(); + if (previewSize != null) { + setRecorderPreviewWidth(info, previewSize.getWidth()); + setRecorderPreviewHeight(info, previewSize.getHeight()); + } + + // カメラのフレームレート + Range previewFps = settings.getPreviewFps(); + if (previewFps != null) { + info.putString("previewFps", previewFps.getLower() + "-" + previewFps.getUpper()); + info.putInt("previewMaxFrameRate", previewFps.getUpper()); + } + + // エンコーダ設定 + List encoders = new ArrayList<>(); + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(encoderId); + if (s != null) { + encoders.add(createVideoEncoder(s)); + } + } + info.putParcelableArray("encoders", encoders.toArray(new Bundle[0])); + + // 各機能の状態 + Bundle status = new Bundle(); + status.putBoolean("preview", recorder.isPreviewRunning()); + status.putBoolean("broadcast", recorder.isBroadcasterRunning()); + status.putBoolean("recording", recorder.getState() == HostMediaRecorder.State.RECORDING); + info.putParcelable("status", status); + + info.putString("audioSource", settings.getPreviewAudioSource().getValue()); + info.putInt("audioBitrate", settings.getPreviewAudioBitRate() / 1024); + info.putInt("audioSampleRate", settings.getPreviewSampleRate()); + info.putInt("audioChannel", settings.getPreviewChannel()); + info.putBoolean("audioEchoCanceler", settings.isUseAEC()); + + setRecorderConfig(info, ""); + return info; + } + private Bundle createVideoEncoder(HostMediaRecorder.EncoderSettings s) { Bundle bundle = new Bundle(); bundle.putString("name", s.getName()); @@ -1540,6 +1555,10 @@ private Bundle createVideoEncoder(HostMediaRecorder.EncoderSettings s) { } } + if (s.getPort() > 0) { + bundle.putInt("port", s.getPort()); + } + // 切り抜き設定 Rect rect = s.getCropRect(); if (rect != null) { @@ -1883,6 +1902,14 @@ private void setOptions(final Intent request, final Intent response) { if (previewWidth != null && previewHeight != null) { settings.setPreviewSize(new Size(previewWidth, previewHeight)); + + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { + encoderSettings.setPreviewSize(new Size(previewWidth, previewHeight)); + + } + } } if (fps != null) { @@ -1890,7 +1917,7 @@ private void setOptions(final Intent request, final Intent response) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setPreviewMaxFrameRate(previewMaxFrameRate.intValue()); } } @@ -1899,7 +1926,7 @@ private void setOptions(final Intent request, final Intent response) { if (previewBitRate != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setPreviewBitRate(previewBitRate * 1024); } } @@ -1908,7 +1935,7 @@ private void setOptions(final Intent request, final Intent response) { if (previewKeyFrameInterval != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setPreviewKeyFrameInterval(previewKeyFrameInterval); } } @@ -1917,7 +1944,7 @@ private void setOptions(final Intent request, final Intent response) { if (previewEncoder != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setPreviewEncoder(previewEncoder); encoderSettings.setProfileLevel(null); } @@ -1927,7 +1954,7 @@ private void setOptions(final Intent request, final Intent response) { if (profileLevel != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setProfileLevel(profileLevel); } } @@ -1936,7 +1963,7 @@ private void setOptions(final Intent request, final Intent response) { if (previewIntraRefresh != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setIntraRefresh(previewIntraRefresh); } } @@ -1945,7 +1972,7 @@ private void setOptions(final Intent request, final Intent response) { if (previewJpegQuality != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setPreviewQuality((int) (previewJpegQuality * 100)); } } @@ -1954,14 +1981,14 @@ private void setOptions(final Intent request, final Intent response) { if (previewClipReset != null && previewClipReset) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setCropRect(null); } } } else if (previewClipLeft != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java index 5a0c197418..7432a307bf 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java @@ -24,6 +24,15 @@ public AbstractBroadcastProvider(Context context, HostMediaRecorder recorder) { super(context, recorder); } + @Override + public List getSupportedMimeType() { + List mimeType = new ArrayList<>(); + for (LiveStreaming server : getLiveStreamingList()) { + mimeType.add(server.getMimeType()); + } + return mimeType; + } + @Override protected void hideNotification(String id) { NotificationManager manager = (NotificationManager) getContext() diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java index 6f54725bf2..7f30e90f13 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java @@ -51,19 +51,21 @@ public abstract class AbstractLiveStreaming implements LiveStreaming, CropInterf /** * 切り抜き範囲移動用スレッドからのイベントを受け取るリスナー. */ - private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = (rect) -> { + private final MovingRectThread.OnEventListener mMovingRectThreadOnEventListener = this::onUpdateCropRect; + + /** + * 切り抜き範囲のイベントを通知するリスナー. + */ + private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); + + protected void onUpdateCropRect(Rect rect) { VideoQuality videoQuality = getVideoQuality(); if (videoQuality != null) { videoQuality.setCropRect(new Rect(rect)); } getEncoderSettings().setCropRect(rect); postOnMoved(rect); - }; - - /** - * 切り抜き範囲のイベントを通知するリスナー. - */ - private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>(); + } public AbstractLiveStreaming(HostMediaRecorder recorder, String id) { mRecorder = recorder; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index 863448df7d..899d5972bc 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -1,5 +1,6 @@ package org.deviceconnect.android.deviceplugin.host.recorder; +import android.graphics.Rect; import android.util.Log; import android.util.Size; @@ -32,6 +33,17 @@ public AbstractMJPEGPreviewServer(HostMediaRecorder recorder, String encoderId) super(recorder, encoderId); } + @Override + protected void onUpdateCropRect(Rect rect) { + if (mMJPEGServer != null) { + MJPEGEncoder encoder = mMJPEGServer.getMJPEGEncoder(); + if (encoder != null) { + encoder.getMJPEGQuality().setCropRect(rect); + } + } + super.onUpdateCropRect(rect); + } + // PreviewServer @Override @@ -139,7 +151,7 @@ private void setMJPEGQuality(MJPEGQuality quality) { quality.setHeight(h); quality.setQuality(settings.getPreviewQuality()); quality.setFrameRate(settings.getPreviewMaxFrameRate()); - quality.setDrawingRange(settings.getCropRect()); + quality.setCropRect(settings.getCropRect()); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index d4efd93a31..e807396236 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -1214,8 +1214,8 @@ public List getEncoderIdList() { * @return 存在する場合はtrue、それ以外はfalse */ public boolean existEncoderId(String encoderId) { - List encorderIdList = getEncoderIdList(); - return encorderIdList.contains(encoderId); + List encoderIdList = getEncoderIdList(); + return encoderIdList.contains(encoderId); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java index 22dc177b06..1c3f196973 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java @@ -32,6 +32,13 @@ public interface LiveStreamingProvider { */ List getLiveStreamingList(); + /** + * プレビューで配信するマイムタイプを取得します. + * + * @return プレビューで配信するマイムタイプ + */ + List getSupportedMimeType(); + /** * プレビューサーバが動作している確認します. * diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java index d9eaa36fcc..555026f5fa 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java @@ -9,10 +9,5 @@ import java.util.List; public interface PreviewServerProvider extends LiveStreamingProvider { - /** - * プレビューで配信するマイムタイプを取得します. - * - * @return プレビューで配信するマイムタイプ - */ - List getSupportedMimeType(); + } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index 61e8d2c833..57e6f7b1fe 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -311,6 +311,7 @@ public String getMimeType() { @Override public List getSupportedMimeTypes() { List mimeTypes = mCamera2PreviewServerProvider.getSupportedMimeType(); + mimeTypes.addAll(mCamera2BroadcasterProvider.getSupportedMimeType()); mimeTypes.add(0, MIME_TYPE_JPEG); return mimeTypes; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java index 18ea0ca583..3c9e452853 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRTSPPreviewServer.java @@ -19,9 +19,9 @@ protected VideoStream createVideoStream() { switch (getEncoderSettings().getPreviewEncoderName()) { case H264: default: - return new ScreenCastH264VideoStream(recorder, 5006); + return new ScreenCastH264VideoStream(recorder, 5016); case H265: - return new ScreenCastH265VideoStream(recorder, 5006); + return new ScreenCastH265VideoStream(recorder, 5016); } } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index 69d967c5c8..620f3b0365 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -247,6 +247,7 @@ public String getMimeType() { @Override public List getSupportedMimeTypes() { List mimeTypes = mScreenCastPreviewServerProvider.getSupportedMimeType(); + mimeTypes.addAll(mScreenCastBroadcasterProvider.getSupportedMimeType()); mimeTypes.add(0, MIME_TYPE_JPEG); return mimeTypes; } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml index 7959b237da..35a2a9dcdd 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml @@ -117,6 +117,8 @@ Right Bottom リセット + 配信解像度を入力する + クリップボードにコピーしました 音声有効 diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml index b493d0ddec..e0ec654918 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml @@ -115,6 +115,8 @@ Right Bottom Reset + Set by delivery resolution + Copied to the clipboard Enable Set whether to enable preview audio. diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml index 81bc1c0651..d34fc24f04 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_broadcast.xml @@ -129,5 +129,11 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml index 153d924049..f332ec1946 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_mjpeg.xml @@ -6,11 +6,9 @@ android:title="@string/host_recorder_settings_server" app:iconSpaceReserved="false"> - @@ -85,5 +83,11 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_port.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_port.xml deleted file mode 100644 index ae41d60e96..0000000000 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_port.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml index 3466103dea..b0c13eaec4 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_rtsp.xml @@ -6,11 +6,9 @@ android:title="@string/host_recorder_settings_server" app:iconSpaceReserved="false"> - @@ -121,5 +119,11 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml index 7d20c726cb..9fc5124a47 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_srt.xml @@ -6,11 +6,9 @@ android:title="@string/host_recorder_settings_server" app:iconSpaceReserved="false"> - @@ -121,6 +119,12 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + Date: Tue, 5 Oct 2021 12:20:58 +0900 Subject: [PATCH 27/40] =?UTF-8?q?mediaStreamingRecording=20=E3=81=AE?= =?UTF-8?q?=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=BC=E3=83=B3?= =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=82=B9=E3=83=88=E3=81=AE=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=AE=E5=8F=96=E5=BE=97=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/SettingsAudioFragment.java | 2 +- .../settings/SettingsPortFragment.java | 20 - .../HostMediaStreamingRecordingProfile.java | 517 ++++++++++-------- .../host/recorder/camera/Camera2Recorder.java | 2 +- .../recorder/screen/ScreenCastRecorder.java | 45 +- 5 files changed, 308 insertions(+), 278 deletions(-) delete mode 100644 dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java index 9525510825..4b04b9b9ed 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java @@ -17,7 +17,7 @@ public class SettingsAudioFragment extends SettingsParameterFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId().replaceAll("/", "_")); + getPreferenceManager().setSharedPreferencesName(getRecorderId()); setPreferencesFromResource(R.xml.settings_host_recorder_audio, rootKey); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java deleted file mode 100644 index 1117c92087..0000000000 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsPortFragment.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.deviceconnect.android.deviceplugin.host.activity.recorder.settings; - -import android.os.Bundle; - -import org.deviceconnect.android.deviceplugin.host.R; - -public class SettingsPortFragment extends SettingsBaseFragment { - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId().replaceAll("/", "_")); - setPreferencesFromResource(R.xml.settings_host_recorder_port, rootKey); - } - - @Override - public void onBindService() { - setInputTypeNumber("mjpeg_port"); - setInputTypeNumber("rtsp_port"); - setInputTypeNumber("srt_port"); - } -} \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 63017dff80..a2191e80d6 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -155,10 +155,10 @@ public void onAllowed() { for (HostMediaRecorder recorder : mRecorderMgr.getRecorders()) { recorders.add(createMediaRecorderInfo(recorder)); } + setRecorders(response, recorders.toArray(new Bundle[0])); } else { - recorders.add(createMediaRecorderInfo(recorder)); + response.putExtras(createMediaRecorderInfo(recorder)); } - setRecorders(response, recorders.toArray(new Bundle[0])); setResult(response, DConnectMessage.RESULT_OK); sendResponse(response); } @@ -193,20 +193,16 @@ public boolean onRequest(final Intent request, final Intent response) { recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { - HostMediaRecorder.Settings settings = recorder.getSettings(); - if (recorder.getMimeType().startsWith("image/") || recorder.getMimeType().startsWith("video/")) { - // 映像系の設定 - setSupportedImageSizes(response, settings.getSupportedPictureSizes()); - setSupportedPreviewSizes(response, settings.getSupportedPreviewSizes()); - setSupportedVideoEncoders(response, settings.getSupportedVideoEncoders()); - setSupportedFps(response, settings.getSupportedFps()); -// } else if (recorder.getMimeType().startsWith("audio/")) { - // 音声系の設定 + if (target == null) { + List recorders = new LinkedList<>(); + for (HostMediaRecorder recorder : mRecorderMgr.getRecorders()) { + recorders.add(createRecorderOption(recorder)); + } + setRecorders(response, recorders.toArray(new Bundle[0])); + } else { + response.putExtras(createRecorderOption(recorder)); } - setResult(response, DConnectMessage.RESULT_OK); - setMIMEType(response, recorder.getSupportedMimeTypes()); - sendResponse(response); } @@ -1460,213 +1456,6 @@ private HostDevicePlugin getHostDevicePlugin() { return (HostDevicePlugin) getContext(); } - private Bundle createMediaRecorderInfo(HostMediaRecorder recorder) { - HostMediaRecorder.Settings settings = recorder.getSettings(); - - Bundle info = new Bundle(); - setRecorderId(info, recorder.getId()); - setRecorderName(info, recorder.getName()); - setRecorderMIMEType(info, recorder.getMimeType()); - - if (recorder.getState() == HostMediaRecorder.State.RECORDING) { - setRecorderState(info, RecorderState.RECORDING); - } else { - setRecorderState(info, RecorderState.INACTIVE); - } - - // 静止画の解像度 - Size pictureSize = settings.getPictureSize(); - if (pictureSize != null) { - setRecorderImageWidth(info, pictureSize.getWidth()); - setRecorderImageHeight(info, pictureSize.getHeight()); - } - - // プレビュー解像度 - Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - setRecorderPreviewWidth(info, previewSize.getWidth()); - setRecorderPreviewHeight(info, previewSize.getHeight()); - } - - // カメラのフレームレート - Range previewFps = settings.getPreviewFps(); - if (previewFps != null) { - info.putString("previewFps", previewFps.getLower() + "-" + previewFps.getUpper()); - info.putInt("previewMaxFrameRate", previewFps.getUpper()); - } - - // エンコーダ設定 - List encoders = new ArrayList<>(); - for (String encoderId : settings.getEncoderIdList()) { - HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(encoderId); - if (s != null) { - encoders.add(createVideoEncoder(s)); - } - } - info.putParcelableArray("encoders", encoders.toArray(new Bundle[0])); - - // 各機能の状態 - Bundle status = new Bundle(); - status.putBoolean("preview", recorder.isPreviewRunning()); - status.putBoolean("broadcast", recorder.isBroadcasterRunning()); - status.putBoolean("recording", recorder.getState() == HostMediaRecorder.State.RECORDING); - info.putParcelable("status", status); - - info.putString("audioSource", settings.getPreviewAudioSource().getValue()); - info.putInt("audioBitrate", settings.getPreviewAudioBitRate() / 1024); - info.putInt("audioSampleRate", settings.getPreviewSampleRate()); - info.putInt("audioChannel", settings.getPreviewChannel()); - info.putBoolean("audioEchoCanceler", settings.isUseAEC()); - - setRecorderConfig(info, ""); - return info; - } - - private Bundle createVideoEncoder(HostMediaRecorder.EncoderSettings s) { - Bundle bundle = new Bundle(); - bundle.putString("name", s.getName()); - bundle.putString("mimeType", s.getMimeType().getValue()); - bundle.putInt("width", s.getPreviewSize().getWidth()); - bundle.putInt("height", s.getPreviewSize().getHeight()); - bundle.putInt("bitrate", s.getPreviewBitRate() / 1024); - - if ("video/x-mjpeg".equals(s.getMimeType().getValue())) { - bundle.putFloat("jpegQuality", s.getPreviewQuality() / 100.0f); - } else if (s.getMimeType().getValue().startsWith("video/")) { - bundle.putInt("keyFrameInterval", s.getPreviewKeyFrameInterval()); - bundle.putString("encoder", s.getPreviewEncoder()); - HostMediaRecorder.ProfileLevel pl = s.getProfileLevel(); - if (pl != null) { - switch (HostMediaRecorder.VideoCodec.nameOf(s.getPreviewEncoder())) { - case H264: - bundle.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName()); - bundle.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName()); - break; - case H265: - bundle.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName()); - bundle.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName()); - break; - } - } - bundle.putBoolean("useSoftwareEncoder", s.isUseSoftwareEncoder()); - Integer intraRefresh = s.getIntraRefresh(); - if (intraRefresh != null) { - bundle.putInt("intraRefresh", intraRefresh); - } - } - - if (s.getPort() > 0) { - bundle.putInt("port", s.getPort()); - } - - // 切り抜き設定 - Rect rect = s.getCropRect(); - if (rect != null) { - Bundle drawingRect = new Bundle(); - drawingRect.putInt("left", rect.left); - drawingRect.putInt("top", rect.top); - drawingRect.putInt("right", rect.right); - drawingRect.putInt("bottom", rect.bottom); - bundle.putBundle("crop", drawingRect); - } - - return bundle; - } - - /** - * サポートしている静止画の解像度をレスポンスに格納します. - * - * @param response 静止画の解像度を格納するレスポンス - * @param sizes サポートしている静止画の解像度のリスト - */ - private static void setSupportedImageSizes(final Intent response, final List sizes) { - Bundle[] array = new Bundle[sizes.size()]; - int i = 0; - for (Size size : sizes) { - Bundle info = new Bundle(); - setWidth(info, size.getWidth()); - setHeight(info, size.getHeight()); - array[i++] = info; - } - setImageSizes(response, array); - } - - /** - * サポートしているプレビューの解像度をレスポンスに格納します. - * - * @param response プレビューの解像度を格納するレスポンス - * @param sizes サポートしているプレビューの解像度のリスト - */ - private static void setSupportedPreviewSizes(final Intent response, final List sizes) { - Bundle[] array = new Bundle[sizes.size()]; - int i = 0; - for (Size size : sizes) { - Bundle info = new Bundle(); - setWidth(info, size.getWidth()); - setHeight(info, size.getHeight()); - array[i++] = info; - } - setPreviewSizes(response, array); - } - - /** - * サポートしているエンコーダをレスポンスに格納します. - * - * @param response レスポンス - * @param encoderNames エンコーダのリスト - */ - private static void setSupportedVideoEncoders(Intent response, List encoderNames) { - List encoders = new ArrayList<>(); - for (String name : encoderNames) { - HostMediaRecorder.VideoCodec videoCodec = HostMediaRecorder.VideoCodec.nameOf(name); - Size maxSize = CapabilityUtil.getSupportedMaxSize(videoCodec.getMimeType()); - Bundle encoder = new Bundle(); - encoder.putString("name", name); - encoder.putParcelableArray("profileLevel", getProfileLevels(videoCodec)); - if (maxSize != null) { - encoder.putInt("maxWidth", maxSize.getWidth()); - encoder.putInt("maxHeight", maxSize.getHeight()); - } - encoders.add(encoder); - } - response.putExtra("encoder", encoders.toArray(new Bundle[0])); - } - - /** - * エンコーダがサポートしているプロファイルとレベルを格納した Bundle の配列を取得します. - * - * @param videoCodec エンコーダ - * @return プロファイルとレベルを格納した Bundle の配列 - */ - private static Bundle[] getProfileLevels(HostMediaRecorder.VideoCodec videoCodec) { - List list = new ArrayList<>(); - for (HostMediaRecorder.ProfileLevel pl : CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType())) { - switch (videoCodec) { - case H264: { - H264Profile p = H264Profile.valueOf(pl.getProfile()); - H264Level l = H264Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - Bundle encoder = new Bundle(); - encoder.putString("profile", p.getName()); - encoder.putString("level", l.getName()); - list.add(encoder); - } - } break; - case H265: { - H265Profile p = H265Profile.valueOf(pl.getProfile()); - H265Level l = H265Level.valueOf(pl.getLevel()); - if (p != null && l != null) { - Bundle encoder = new Bundle(); - encoder.putString("profile", p.getName()); - encoder.putString("level", l.getName()); - list.add(encoder); - } - } break; - } - } - return list.toArray(new Bundle[0]); - } - /** * RecorderのMute状態を切り返す. * RTSPをサポートしているRecorderのみ対応する. @@ -2032,18 +1821,296 @@ private HostMediaRecorder.ProfileLevel convertProfileLevel(HostMediaRecorder rec return null; } + private Bundle createMediaRecorderInfo(HostMediaRecorder recorder) { + HostMediaRecorder.Settings settings = recorder.getSettings(); + + Bundle info = new Bundle(); + setRecorderId(info, recorder.getId()); + setRecorderName(info, recorder.getName()); + setRecorderMIMEType(info, recorder.getMimeType()); + + if (recorder.getState() == HostMediaRecorder.State.RECORDING) { + setRecorderState(info, RecorderState.RECORDING); + } else { + setRecorderState(info, RecorderState.INACTIVE); + } + + // 静止画の解像度 + Size pictureSize = settings.getPictureSize(); + if (pictureSize != null) { + setRecorderImageWidth(info, pictureSize.getWidth()); + setRecorderImageHeight(info, pictureSize.getHeight()); + } + + // プレビュー解像度 + Size previewSize = settings.getPreviewSize(); + if (previewSize != null) { + setRecorderPreviewWidth(info, previewSize.getWidth()); + setRecorderPreviewHeight(info, previewSize.getHeight()); + } + + // カメラのフレームレート + Range previewFps = settings.getPreviewFps(); + if (previewFps != null) { + info.putString("previewFps", previewFps.getLower() + "-" + previewFps.getUpper()); + info.putInt("previewMaxFrameRate", previewFps.getUpper()); + } + + // エンコーダ設定 + List encoders = new ArrayList<>(); + for (String encoderId : settings.getEncoderIdList()) { + HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(encoderId); + if (s != null) { + encoders.add(createVideoEncoder(s)); + } + } + info.putParcelableArray("encoders", encoders.toArray(new Bundle[0])); + + // 各機能の状態 + Bundle status = new Bundle(); + status.putBoolean("preview", recorder.isPreviewRunning()); + status.putBoolean("broadcast", recorder.isBroadcasterRunning()); + status.putBoolean("recording", recorder.getState() == HostMediaRecorder.State.RECORDING); + info.putParcelable("status", status); + + info.putString("audioSource", settings.getPreviewAudioSource().getValue()); + info.putInt("audioBitrate", settings.getPreviewAudioBitRate() / 1024); + info.putInt("audioSampleRate", settings.getPreviewSampleRate()); + info.putInt("audioChannel", settings.getPreviewChannel()); + info.putBoolean("audioEchoCanceler", settings.isUseAEC()); + + setRecorderConfig(info, ""); + return info; + } + + private Bundle createVideoEncoder(HostMediaRecorder.EncoderSettings s) { + Bundle bundle = new Bundle(); + bundle.putString("name", s.getName()); + bundle.putString("mimeType", s.getMimeType().getValue()); + bundle.putInt("width", s.getPreviewSize().getWidth()); + bundle.putInt("height", s.getPreviewSize().getHeight()); + bundle.putInt("bitrate", s.getPreviewBitRate() / 1024); + + if ("video/x-mjpeg".equals(s.getMimeType().getValue())) { + bundle.putFloat("jpegQuality", s.getPreviewQuality() / 100.0f); + } else if (s.getMimeType().getValue().startsWith("video/")) { + bundle.putInt("keyFrameInterval", s.getPreviewKeyFrameInterval()); + bundle.putString("encoder", s.getPreviewEncoder()); + HostMediaRecorder.ProfileLevel pl = s.getProfileLevel(); + if (pl != null) { + switch (HostMediaRecorder.VideoCodec.nameOf(s.getPreviewEncoder())) { + case H264: + bundle.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName()); + bundle.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName()); + break; + case H265: + bundle.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName()); + bundle.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName()); + break; + } + } + bundle.putBoolean("useSoftwareEncoder", s.isUseSoftwareEncoder()); + Integer intraRefresh = s.getIntraRefresh(); + if (intraRefresh != null) { + bundle.putInt("intraRefresh", intraRefresh); + } + } + + if (s.getPort() > 0) { + bundle.putInt("port", s.getPort()); + } + + // 切り抜き設定 + Rect rect = s.getCropRect(); + if (rect != null) { + Bundle drawingRect = new Bundle(); + drawingRect.putInt("left", rect.left); + drawingRect.putInt("top", rect.top); + drawingRect.putInt("right", rect.right); + drawingRect.putInt("bottom", rect.bottom); + bundle.putBundle("crop", drawingRect); + } + + return bundle; + } + + private Bundle createRecorderOption(HostMediaRecorder recorder) { + Bundle bundle = new Bundle(); + HostMediaRecorder.Settings settings = recorder.getSettings(); + setRecorderId(bundle, recorder.getId()); + setRecorderName(bundle, recorder.getName()); + if (recorder.getMimeType().startsWith("image/") || recorder.getMimeType().startsWith("video/")) { + setSupportedImageSizes(bundle, settings.getSupportedPictureSizes()); + setSupportedPreviewSizes(bundle, settings.getSupportedPreviewSizes()); + setSupportedVideoEncoders(bundle, settings.getSupportedVideoEncoders()); + setSupportedFps(bundle, settings.getSupportedFps()); + } + setMIMEType(bundle, recorder.getSupportedMimeTypes()); + return bundle; + } + + private static Bundle[] createSupportedImageSizes(List sizes) { + Bundle[] array = new Bundle[sizes.size()]; + int i = 0; + for (Size size : sizes) { + Bundle info = new Bundle(); + setWidth(info, size.getWidth()); + setHeight(info, size.getHeight()); + array[i++] = info; + } + return array; + } + /** - * カメラがサポートしている Fps のリストをレスポンスに格納します. + * サポートしている静止画の解像度をレスポンスに格納します. + * + * @param response 静止画の解像度を格納するレスポンス + * @param sizes サポートしている静止画の解像度のリスト + */ + private static void setSupportedImageSizes(final Intent response, final List sizes) { + setImageSizes(response, createSupportedImageSizes(sizes)); + } + + /** + * サポートしている静止画の解像度をレスポンスに格納します. + * + * @param bundle 静止画の解像度を格納するレスポンス + * @param sizes サポートしている静止画の解像度のリスト + */ + private static void setSupportedImageSizes(final Bundle bundle, final List sizes) { + bundle.putParcelableArray("imageSizes", createSupportedImageSizes(sizes)); + } + + private static Bundle[] createSupportedPreviewSizes(List sizes) { + Bundle[] array = new Bundle[sizes.size()]; + int i = 0; + for (Size size : sizes) { + Bundle info = new Bundle(); + setWidth(info, size.getWidth()); + setHeight(info, size.getHeight()); + array[i++] = info; + } + return array; + } + + /** + * サポートしているプレビューの解像度をレスポンスに格納します. + * + * @param response プレビューの解像度を格納するレスポンス + * @param sizes サポートしているプレビューの解像度のリスト + */ + private static void setSupportedPreviewSizes(final Intent response, final List sizes) { + setPreviewSizes(response, createSupportedPreviewSizes(sizes)); + } + + /** + * サポートしているプレビューの解像度をレスポンスに格納します. + * + * @param bundle プレビューの解像度を格納するレスポンス + * @param sizes サポートしているプレビューの解像度のリスト + */ + private static void setSupportedPreviewSizes(final Bundle bundle, final List sizes) { + bundle.putParcelableArray("previewSizes", createSupportedPreviewSizes(sizes)); + } + + private static Bundle[] createSupportedVideoEncoders(List encoderNames) { + List encoders = new ArrayList<>(); + for (String name : encoderNames) { + HostMediaRecorder.VideoCodec videoCodec = HostMediaRecorder.VideoCodec.nameOf(name); + Size maxSize = CapabilityUtil.getSupportedMaxSize(videoCodec.getMimeType()); + Bundle encoder = new Bundle(); + encoder.putString("name", name); + encoder.putParcelableArray("profileLevel", getProfileLevels(videoCodec)); + if (maxSize != null) { + encoder.putInt("maxWidth", maxSize.getWidth()); + encoder.putInt("maxHeight", maxSize.getHeight()); + } + encoders.add(encoder); + } + return encoders.toArray(new Bundle[0]); + } + + /** + * サポートしているエンコーダをレスポンスに格納します. * * @param response レスポンス - * @param supportedFps サポートしている fps のリスト + * @param encoderNames エンコーダのリスト */ - private static void setSupportedFps(Intent response, List> supportedFps) { + private static void setSupportedVideoEncoders(Intent response, List encoderNames) { + response.putExtra("encoder", createSupportedVideoEncoders(encoderNames)); + } + + /** + * サポートしているエンコーダをレスポンスに格納します. + * + * @param bundle レスポンス + * @param encoderNames エンコーダのリスト + */ + private static void setSupportedVideoEncoders(Bundle bundle, List encoderNames) { + bundle.putParcelableArray("encoder", createSupportedVideoEncoders(encoderNames)); + } + + /** + * エンコーダがサポートしているプロファイルとレベルを格納した Bundle の配列を取得します. + * + * @param videoCodec エンコーダ + * @return プロファイルとレベルを格納した Bundle の配列 + */ + private static Bundle[] getProfileLevels(HostMediaRecorder.VideoCodec videoCodec) { + List list = new ArrayList<>(); + for (HostMediaRecorder.ProfileLevel pl : CapabilityUtil.getSupportedProfileLevel(videoCodec.getMimeType())) { + switch (videoCodec) { + case H264: { + H264Profile p = H264Profile.valueOf(pl.getProfile()); + H264Level l = H264Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + Bundle encoder = new Bundle(); + encoder.putString("profile", p.getName()); + encoder.putString("level", l.getName()); + list.add(encoder); + } + } break; + case H265: { + H265Profile p = H265Profile.valueOf(pl.getProfile()); + H265Level l = H265Level.valueOf(pl.getLevel()); + if (p != null && l != null) { + Bundle encoder = new Bundle(); + encoder.putString("profile", p.getName()); + encoder.putString("level", l.getName()); + list.add(encoder); + } + } break; + } + } + return list.toArray(new Bundle[0]); + } + + private static String[] createSupportedFps(List> supportedFps) { List fpsList = new ArrayList<>(); for (Range fps : supportedFps) { fpsList.add(fps.getLower() + "-" + fps.getUpper()); } - response.putExtra("frameRate", fpsList.toArray(new String[0])); + return fpsList.toArray(new String[0]); + } + + /** + * カメラがサポートしている Fps のリストをレスポンスに格納します. + * + * @param response レスポンス + * @param supportedFps サポートしている fps のリスト + */ + private static void setSupportedFps(Intent response, List> supportedFps) { + response.putExtra("frameRate", createSupportedFps(supportedFps)); + } + + /** + * カメラがサポートしている Fps のリストをレスポンスに格納します. + * + * @param bundle レスポンス + * @param supportedFps サポートしている fps のリスト + */ + private static void setSupportedFps(Bundle bundle, List> supportedFps) { + bundle.putStringArray("frameRate", createSupportedFps(supportedFps)); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index 57e6f7b1fe..cfa3ca2f42 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -290,7 +290,7 @@ public void destroy() { @Override public String getId() { - return ID_BASE + "_" + mCameraWrapper.getId(); + return ID_BASE + "_" + mCameraWrapper.getId().replaceAll("/", "_"); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index 74a6394c26..88bf1c6a4e 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -21,7 +21,6 @@ import android.util.Range; import android.util.Size; import android.view.Display; -import android.view.Surface; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; @@ -199,52 +198,36 @@ private Size getDisplaySize() { if (wm == null) { throw new RuntimeException("WindowManager is not supported."); } - DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); - boolean isSwap; - switch (wm.getDefaultDisplay().getRotation()) { - case Surface.ROTATION_0: - case Surface.ROTATION_180: - isSwap = false; - break; - default: - case Surface.ROTATION_90: - case Surface.ROTATION_270: - isSwap = true; - break; - } + int insetsWidth = 0; int insetsHeight = 0; Size displaySize; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); - final WindowInsets windowInsets = windowMetrics.getWindowInsets(); + WindowInsets windowInsets = windowMetrics.getWindowInsets(); Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout()); - - int width = isSwap ? windowMetrics.getBounds().height() :windowMetrics.getBounds().width(); - int height = isSwap ? windowMetrics.getBounds().width() :windowMetrics.getBounds().height(); + int width = windowMetrics.getBounds().width(); + int height = windowMetrics.getBounds().height(); insetsWidth = insets.right + insets.left; insetsHeight = insets.top + insets.bottom; - - // Legacy size that Display#getSize reports - displaySize = new Size(width - insetsWidth, - height - insetsHeight); + displaySize = new Size(width - insetsWidth, height - insetsHeight); } else { - // 画面が回転している場合には、縦横をスワップしておく。 - int width = isSwap ? metrics.heightPixels : metrics.widthPixels; - int height = isSwap ? metrics.widthPixels : metrics.heightPixels; - displaySize = new Size(width, height); + DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); + displaySize = new Size(metrics.widthPixels, metrics.heightPixels); } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Display.Mode[] modes = wm.getDefaultDisplay().getSupportedModes(); - if(modes.length > 0){ + if (modes.length > 0){ Display.Mode mode = modes[modes.length - 1]; - int width = isSwap ? mode.getPhysicalHeight() : mode.getPhysicalWidth(); - int height = isSwap ? mode.getPhysicalWidth() : mode.getPhysicalHeight(); - // 4Kサイズの解像度がある場合はそちらを優先する + int width = mode.getPhysicalWidth(); + int height = mode.getPhysicalHeight(); + // 4K サイズの解像度がある場合はそちらを優先する displaySize = new Size(width - insetsWidth, height - insetsHeight); } } + return displaySize; } From 263a94f9aea9b5d0154b79c6ea00032d186cab21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 5 Oct 2021 17:20:46 +0900 Subject: [PATCH 28/40] =?UTF-8?q?MJPEG=20=E3=81=AE=E9=85=8D=E4=BF=A1?= =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java index e2f2cfd323..35b47d380f 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/mjpeg/SurfaceMJPEGEncoder.java @@ -1,7 +1,6 @@ package org.deviceconnect.android.libmedia.streaming.mjpeg; import android.graphics.Bitmap; -import android.graphics.Rect; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceBase; import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread; @@ -189,15 +188,8 @@ private synchronized void startDrawingThreadInternal() { mSurfaceDrawingThread = createEGLSurfaceDrawingThread(); } - int w = quality.getWidth(); - int h = quality.getHeight(); - Rect rect = quality.getCropRect(); - if (rect != null) { - w = rect.width(); - h = rect.height(); - } mSurfaceDrawingThread.setSize(quality.getWidth(), quality.getHeight()); - mSurfaceDrawingThread.addEGLSurfaceBase(w, h, TAG_SURFACE, quality.getCropRect()); + mSurfaceDrawingThread.addEGLSurfaceBase(quality.getWidth(), quality.getHeight(), TAG_SURFACE, quality.getCropRect()); mSurfaceDrawingThread.addOnDrawingEventListener(mOnDrawingEventListener); mSurfaceDrawingThread.start(); } From fdc0ef8ab2a204cba87fbb7d531d4d8360368d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 5 Oct 2021 19:39:47 +0900 Subject: [PATCH 29/40] =?UTF-8?q?Host=E3=82=AB=E3=83=A1=E3=83=A9=E3=82=A2?= =?UTF-8?q?=E3=83=97=E3=83=AA=E8=B5=B7=E5=8B=95=E6=99=82=E3=81=AB=E6=A8=AA?= =?UTF-8?q?=E5=90=91=E3=81=8D=E5=9B=BA=E5=AE=9A=E3=81=A7=E8=B5=B7=E5=8B=95?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20/gotapi/mediaStreamRecording/encoder=20=E3=81=A7=E3=80=81?= =?UTF-8?q?=E8=A4=87=E6=95=B0=E3=81=AE=E3=82=A8=E3=83=B3=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=80=E3=81=AB=E5=AF=BE=E3=81=97=E3=81=A6=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E3=81=8C=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/AndroidManifest.xml | 2 + .../HostDevicePluginBindActivity.java | 22 ++- .../recorder/settings/SettingsActivity.java | 9 +- .../HostMediaStreamingRecordingProfile.java | 162 +++++++++++------- .../host/recorder/AbstractLiveStreaming.java | 1 + .../host/recorder/util/MovingRectThread.java | 11 ++ 6 files changed, 130 insertions(+), 77 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/AndroidManifest.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/AndroidManifest.xml index 0a82c67962..82ac7e2549 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/AndroidManifest.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/AndroidManifest.xml @@ -112,6 +112,7 @@ android:name="org.deviceconnect.android.deviceplugin.host.setting.HostSettingActivity" android:exported="false" android:launchMode="singleTask" + android:taskAffinity=".settings" android:theme="@style/AppCompatTheme" /> @@ -119,6 +120,7 @@ android:name=".activity.recorder.settings.SettingsActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:exported="false" + android:taskAffinity=".recorder_settings" android:theme="@style/AppCompatTheme" /> diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/HostDevicePluginBindActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/HostDevicePluginBindActivity.java index 2f749976b0..1eaf9da48f 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/HostDevicePluginBindActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/HostDevicePluginBindActivity.java @@ -31,11 +31,6 @@ public class HostDevicePluginBindActivity extends AppCompatActivity { */ private boolean mIsBound = false; - /** - * 画面の回転固定フラグ. - */ - private boolean mDisplayRotationFixed = false; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -179,9 +174,18 @@ public void onServiceDisconnected(ComponentName name) { * @param fixed 固定する場合はtrue、それ以外はfalse */ public void setDisplayRotation(boolean fixed) { - mDisplayRotationFixed = fixed; + setDisplayRotation(fixed, getDisplayOrientation()); + } + + /** + * 画面に表示されている状態で回転を固定します. + * + * @param fixed 固定する場合はtrue、それ以外はfalse + * @param orientation 固定する向き + */ + public void setDisplayRotation(boolean fixed, int orientation) { if (fixed) { - setRequestedOrientation(getDisplayOrientation()); + setRequestedOrientation(orientation); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } @@ -193,14 +197,14 @@ public void setDisplayRotation(boolean fixed) { * @return 画面が固定されている場合はtrue、それ以外はfalse */ public boolean isDisplayRotationFixed() { - return mDisplayRotationFixed; + return getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } /** * 画面固定を切り替えます. */ public void toggleDisplayRotation() { - setDisplayRotation(!mDisplayRotationFixed); + setDisplayRotation(!isDisplayRotationFixed()); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java index d32dde4925..b5f40c8613 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java @@ -18,6 +18,7 @@ public class SettingsActivity extends HostDevicePluginBindActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setRequestedOrientation(getDisplayOrientation()); setContentView(R.layout.activity_recorder_settings); ActionBar actionBar = getSupportActionBar(); @@ -25,14 +26,6 @@ public void onCreate(Bundle savedInstanceState) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setTitle(""); } - - setRequestedOrientation(getDisplayOrientation()); - } - - @Override - public void onStart() { - super.onStart(); - setRequestedOrientation(getDisplayOrientation()); } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index a2191e80d6..abaccde862 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -1006,18 +1006,20 @@ public boolean onRequest(Intent request, Intent response) { Integer retryCount = parseInteger(request, "retryCount"); Integer retryInterval = parseInteger(request, "retryInterval"); - if (name == null) { - MessageUtils.setInvalidRequestParameterError(response, "name is not set."); - return true; - } HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); + MessageUtils.setInvalidRequestParameterError(response, + "target is invalid."); return true; } - String encoderId = recorder.getId() + "-" + name; + List encoderIdList = getEncoderSettings(recorder, name); + if (encoderIdList.isEmpty()) { + MessageUtils.setInvalidRequestParameterError(response, + "name is invalid."); + return true; + } if (width != null && width < 0) { MessageUtils.setInvalidRequestParameterError(response, @@ -1084,56 +1086,79 @@ public boolean onRequest(Intent request, Intent response) { recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { - HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + HostMediaRecorder.Settings settings = recorder.getSettings(); if (width != null && height != null) { - encoderSettings.setPreviewSize(new Size(width, height)); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setPreviewSize(new Size(width, height)); + } } if (frameRate != null) { - encoderSettings.setPreviewMaxFrameRate(frameRate); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setPreviewMaxFrameRate(frameRate); + } } if (bitRate != null) { - encoderSettings.setPreviewBitRate(bitRate * 1024); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setPreviewBitRate(bitRate * 1024); + } } if (keyFrameInterval != null) { - encoderSettings.setPreviewKeyFrameInterval(keyFrameInterval); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setPreviewKeyFrameInterval(keyFrameInterval); + } } if (codec != null) { - encoderSettings.setPreviewEncoder(codec); - // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする. - encoderSettings.setProfileLevel(null); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setPreviewEncoder(codec); + settings.getEncoderSetting(encoderId).setProfileLevel(null); + } } if (profileLevel != null) { - encoderSettings.setProfileLevel(profileLevel); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setProfileLevel(profileLevel); + } } if (intraRefresh != null) { - encoderSettings.setIntraRefresh(intraRefresh); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setIntraRefresh(intraRefresh); + } } if (useSoftwareEncoder != null) { - encoderSettings.setUseSoftwareEncoder(useSoftwareEncoder); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setUseSoftwareEncoder(useSoftwareEncoder); + } } if (jpegQuality != null) { - encoderSettings.setPreviewQuality((int) (jpegQuality * 100)); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setPreviewQuality((int) (jpegQuality * 100)); + } } if (broadcastUri != null) { - encoderSettings.setBroadcastURI(broadcastUri); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setBroadcastURI(broadcastUri); + } } if (retryCount != null) { - encoderSettings.setRetryCount(retryCount); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setRetryCount(retryCount); + } } if (retryInterval != null) { - encoderSettings.setRetryInterval(retryInterval); + for (String encoderId : encoderIdList) { + settings.getEncoderSetting(encoderId).setRetryInterval(retryInterval); + } } try { @@ -1212,11 +1237,6 @@ public boolean onRequest(Intent request, Intent response) { return true; } - if (name == null) { - MessageUtils.setInvalidRequestParameterError(response, "name is not set."); - return true; - } - if (left == null) { MessageUtils.setInvalidRequestParameterError(response, "left is not set."); return true; @@ -1237,9 +1257,9 @@ public boolean onRequest(Intent request, Intent response) { return true; } - String encoderId = recorder.getId() + "-" + name; - if (!recorder.getSettings().existEncoderId(encoderId)) { - MessageUtils.setInvalidRequestParameterError(response, "name is not found."); + List encoderIdList = getEncoderSettings(recorder, name); + if (encoderIdList.isEmpty()) { + MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); return true; } @@ -1251,28 +1271,33 @@ public boolean onRequest(Intent request, Intent response) { recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { - HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); - Rect start = encoderSettings.getCropRect(); - if (start == null) { - int width = recorder.getSettings().getPreviewSize().getWidth(); - int height = recorder.getSettings().getPreviewSize().getHeight(); - start = new Rect(0, 0, width, height); - } + for (String encoderId : encoderIdList) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings == null) { + continue; + } - Rect end = new Rect(left, top, right, bottom); + Rect start = encoderSettings.getCropRect(); + if (start == null) { + int width = recorder.getSettings().getPreviewSize().getWidth(); + int height = recorder.getSettings().getPreviewSize().getHeight(); + start = new Rect(0, 0, width, height); + } + + Rect end = new Rect(left, top, right, bottom); - for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { - if (encoderId.equals(previewServer.getId())) { - ((CropInterface) previewServer).moveCropRect(start, end, duration); + for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { + if (encoderId.equals(previewServer.getId())) { + ((CropInterface) previewServer).moveCropRect(start, end, duration); + } } - } - for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { - if (encoderId.equals(broadcaster.getId())) { - ((CropInterface) broadcaster).moveCropRect(start, end, duration); + for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { + if (encoderId.equals(broadcaster.getId())) { + ((CropInterface) broadcaster).moveCropRect(start, end, duration); + } } } - setResult(response, DConnectMessage.RESULT_OK); sendResponse(response); } @@ -1306,24 +1331,26 @@ public boolean onRequest(Intent request, Intent response) { return true; } - String encoderId = recorder.getId() + "-" + name; - if (!recorder.getSettings().existEncoderId(encoderId)) { - MessageUtils.setInvalidRequestParameterError(response, "name is not found."); + List encoderIdList = getEncoderSettings(recorder, name); + if (encoderIdList.isEmpty()) { + MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); return true; } recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { @Override public void onAllowed() { - for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { - if (encoderId.equals(previewServer.getId())) { - ((CropInterface) previewServer).setCropRect(null); + for (String encoderId : encoderIdList) { + for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { + if (encoderId.equals(previewServer.getId())) { + ((CropInterface) previewServer).setCropRect(null); + } } - } - for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { - if (encoderId.equals(broadcaster.getId())) { - ((CropInterface) broadcaster).setCropRect(null); + for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { + if (encoderId.equals(broadcaster.getId())) { + ((CropInterface) broadcaster).setCropRect(null); + } } } @@ -1342,7 +1369,6 @@ public void onDisallowed() { } }); - // PUT /gotapi/mediaStreamRecording/broadcast addApi(new PutApi() { @Override @@ -1456,6 +1482,22 @@ private HostDevicePlugin getHostDevicePlugin() { return (HostDevicePlugin) getContext(); } + private List getEncoderSettings(HostMediaRecorder recorder, String name) { + List encoderIdList = new ArrayList<>(); + if (name == null) { + encoderIdList.addAll(recorder.getSettings().getEncoderIdList()); + } else { + String[] names = name.split(","); + for (String n : names) { + String encoderId = recorder.getId() + "-" + n; + if (recorder.getSettings().existEncoderId(encoderId)) { + encoderIdList.add(encoderId); + } + } + } + return encoderIdList; + } + /** * RecorderのMute状態を切り返す. * RTSPをサポートしているRecorderのみ対応する. @@ -1526,10 +1568,10 @@ private void setOptions(final Intent request, final Intent response) { HostMediaRecorder.Settings settings = recorder.getSettings(); - if (!isSupportedMimeType(recorder, mimeType)) { - MessageUtils.setInvalidRequestParameterError(response, "MIME-Type " + mimeType + " is unsupported."); - return; - } +// if (!isSupportedMimeType(recorder, mimeType)) { +// MessageUtils.setInvalidRequestParameterError(response, "MIME-Type " + mimeType + " is unsupported."); +// return; +// } if (recorder.getState() != HostMediaRecorder.State.INACTIVE && recorder.getState() != HostMediaRecorder.State.PREVIEW) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java index 7f30e90f13..7d540e5d0d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java @@ -173,6 +173,7 @@ public void setCropRect(Rect rect) { checkCropRect(rect); if (rect == null || mMovingRectThread == null) { + mMovingRectThread.cancelMove(); setCropRectInternal(rect); } else { mMovingRectThread.set(rect); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java index e3ae6ea440..3abdab1ac5 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/util/MovingRectThread.java @@ -98,6 +98,17 @@ public void move(Rect start, Rect end, int duration) { move(start, end, duration, true); } + /** + * 現在動いている範囲を停止します. + * + * 動いている範囲が無い場合は何もしません。 + */ + public void cancelMove() { + if (mWorkThread != null) { + mWorkThread.cancel(); + } + } + /** * 範囲の移動イベントを通知するリスナーを設定します. * From 987f2e01c587853f6f89096019f16c76f584d267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Thu, 7 Oct 2021 19:15:42 +0900 Subject: [PATCH 30/40] =?UTF-8?q?/gotapi/mediaStreamRecording/options:PUT?= =?UTF-8?q?=20=E3=81=AB=20previewClipDuration=20=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HostMediaStreamingRecordingProfile.java | 125 ++++++++++++------ 1 file changed, 81 insertions(+), 44 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index abaccde862..1df1137ee1 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -1272,31 +1272,7 @@ public boolean onRequest(Intent request, Intent response) { @Override public void onAllowed() { for (String encoderId : encoderIdList) { - HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); - if (encoderSettings == null) { - continue; - } - - Rect start = encoderSettings.getCropRect(); - if (start == null) { - int width = recorder.getSettings().getPreviewSize().getWidth(); - int height = recorder.getSettings().getPreviewSize().getHeight(); - start = new Rect(0, 0, width, height); - } - - Rect end = new Rect(left, top, right, bottom); - - for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { - if (encoderId.equals(previewServer.getId())) { - ((CropInterface) previewServer).moveCropRect(start, end, duration); - } - } - - for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { - if (encoderId.equals(broadcaster.getId())) { - ((CropInterface) broadcaster).moveCropRect(start, end, duration); - } - } + setCrop(recorder, encoderId, left, top, right, bottom, duration); } setResult(response, DConnectMessage.RESULT_OK); sendResponse(response); @@ -1341,19 +1317,8 @@ public boolean onRequest(Intent request, Intent response) { @Override public void onAllowed() { for (String encoderId : encoderIdList) { - for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { - if (encoderId.equals(previewServer.getId())) { - ((CropInterface) previewServer).setCropRect(null); - } - } - - for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { - if (encoderId.equals(broadcaster.getId())) { - ((CropInterface) broadcaster).setCropRect(null); - } - } + clearCrop(recorder, encoderId); } - setResult(response, DConnectMessage.RESULT_OK); sendResponse(response); } @@ -1556,9 +1521,12 @@ private void setOptions(final Intent request, final Intent response) { Integer previewClipTop = parseInteger(request, "previewClipTop"); Integer previewClipRight = parseInteger(request, "previewClipRight"); Integer previewClipBottom = parseInteger(request, "previewClipBottom"); + Integer previewClipDuration = parseInteger(request, "previewClipDuration"); Boolean previewClipReset = parseBoolean(request, "previewClipReset"); + HostMediaRecorder.ProfileLevel profileLevel = null; Range fps = null; + boolean isChangeConfig = false; HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); if (recorder == null) { @@ -1723,6 +1691,16 @@ private void setOptions(final Intent request, final Intent response) { "previewClipTop is larger than previewClipBottom."); return; } + + if (previewClipDuration != null) { + if (previewClipDuration < 0) { + MessageUtils.setInvalidRequestParameterError(response, + "previewClipDuration is negative value."); + return; + } + } else { + previewClipDuration = 0; + } } // 値の設定 @@ -1741,6 +1719,8 @@ private void setOptions(final Intent request, final Intent response) { } } + + isChangeConfig = true; } if (fps != null) { @@ -1752,6 +1732,8 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setPreviewMaxFrameRate(previewMaxFrameRate.intValue()); } } + + isChangeConfig = true; } if (previewBitRate != null) { @@ -1761,6 +1743,8 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setPreviewBitRate(previewBitRate * 1024); } } + + isChangeConfig = true; } if (previewKeyFrameInterval != null) { @@ -1770,6 +1754,8 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setPreviewKeyFrameInterval(previewKeyFrameInterval); } } + + isChangeConfig = true; } if (previewEncoder != null) { @@ -1780,6 +1766,8 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setProfileLevel(null); } } + + isChangeConfig = true; } if (profileLevel != null) { @@ -1789,6 +1777,8 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setProfileLevel(profileLevel); } } + + isChangeConfig = true; } if (previewIntraRefresh != null) { @@ -1798,6 +1788,8 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setIntraRefresh(previewIntraRefresh); } } + + isChangeConfig = true; } if (previewJpegQuality != null) { @@ -1813,28 +1805,73 @@ private void setOptions(final Intent request, final Intent response) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { - encoderSettings.setCropRect(null); + clearCrop(recorder, encoderId); } } } else if (previewClipLeft != null) { for (String encoderId : settings.getEncoderIdList()) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { - encoderSettings.setCropRect(new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom)); + setCrop(recorder, encoderId, previewClipLeft, previewClipTop, + previewClipRight, previewClipBottom, previewClipDuration); } } } - try { - recorder.onConfigChange(); - } catch (Exception e) { - MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config."); - return; + if (isChangeConfig) { + try { + recorder.onConfigChange(); + } catch (Exception e) { + MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config."); + return; + } } setResult(response, DConnectMessage.RESULT_OK); } + private void setCrop(HostMediaRecorder recorder, String encoderId, int left, int top, int right, int bottom, int duration) { + HostMediaRecorder.EncoderSettings encoderSettings = recorder.getSettings().getEncoderSetting(encoderId); + if (encoderSettings == null) { + return; + } + + Rect start = encoderSettings.getCropRect(); + if (start == null) { + int width = recorder.getSettings().getPreviewSize().getWidth(); + int height = recorder.getSettings().getPreviewSize().getHeight(); + start = new Rect(0, 0, width, height); + } + + Rect end = new Rect(left, top, right, bottom); + + for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { + if (encoderId.equals(previewServer.getId())) { + ((CropInterface) previewServer).moveCropRect(start, end, duration); + } + } + + for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { + if (encoderId.equals(broadcaster.getId())) { + ((CropInterface) broadcaster).moveCropRect(start, end, duration); + } + } + } + + private void clearCrop(HostMediaRecorder recorder, String encoderId) { + for (LiveStreaming previewServer : recorder.getServerProvider().getLiveStreamingList()) { + if (encoderId.equals(previewServer.getId())) { + ((CropInterface) previewServer).setCropRect(null); + } + } + + for (LiveStreaming broadcaster : recorder.getBroadcasterProvider().getLiveStreamingList()) { + if (encoderId.equals(broadcaster.getId())) { + ((CropInterface) broadcaster).setCropRect(null); + } + } + } + private HostMediaRecorder.ProfileLevel convertProfileLevel(HostMediaRecorder recorder, String codec, String profile, String level) { if (profile != null && level != null) { HostMediaRecorder.VideoCodec codec1 = HostMediaRecorder.VideoCodec.H264; From 511ee203fb66338fd654322f6bd67f86ada3471b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Thu, 7 Oct 2021 19:16:18 +0900 Subject: [PATCH 31/40] =?UTF-8?q?RTP=20=E3=81=AE=E3=83=91=E3=82=B1?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=81=AE=E4=BD=9C=E6=88=90=E3=81=99=E3=82=8B?= =?UTF-8?q?=E6=99=82=E3=81=AB=20NAL=20Unit=20=E3=81=94=E3=81=A8=E3=81=AB?= =?UTF-8?q?=E9=80=81=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../streaming/rtp/packet/H264Packetize.java | 46 ++++++++++++++----- .../streaming/rtp/packet/H265Packetize.java | 44 ++++++++++++++---- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H264Packetize.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H264Packetize.java index b4ecbe88e9..5bbb15b276 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H264Packetize.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H264Packetize.java @@ -56,10 +56,18 @@ public void write(byte[] data, int dataLength, long pts) { pts = updateTimestamp(pts * 1000L); - if (dataLength <= MAX_PACKET_SIZE - HEADER_LEN) { - writeSingleNALUnit(data, dataLength, pts); - } else { - writeFragmentationUnits(data, dataLength, pts); + int start = 0; + while (start < dataLength) { + int end = searchNalUnit(data, start + 4, dataLength); + int length = end - start; + + if (length <= MAX_PACKET_SIZE - HEADER_LEN) { + writeSingleNALUnit(data, start, length, pts); + } else { + writeFragmentationUnits(data, start, length, pts); + } + + start += length; } } @@ -76,14 +84,14 @@ public void write(byte[] data, int dataLength, long pts) { // | :...OPTIONAL RTP padding | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - private void writeSingleNALUnit(byte[] data, int dataLength, long pts) { + private void writeSingleNALUnit(byte[] data, int offset, int dataLength, long pts) { RtpPacket rtpPacket = getRtpPacket(); byte[] dest = rtpPacket.getBuffer(); writeRtpHeader(dest, pts); writeNextPacket(dest); - System.arraycopy(data, 4, dest, RTP_HEADER_LENGTH, dataLength - 4); + System.arraycopy(data, offset + 4, dest, RTP_HEADER_LENGTH, dataLength - 4); send(rtpPacket, RTP_HEADER_LENGTH + dataLength - 4, pts); } @@ -122,10 +130,10 @@ private void writeSingleNALUnit(byte[] data, int dataLength, long pts) { //|S|E|R| Type | //+---------------+ - private void writeFragmentationUnits(byte[] data, int dataLength, long pts) { + private void writeFragmentationUnits(byte[] data, int srcOffset, int dataLength, long pts) { dataLength -= 5; - int offset = 5; + int offset = srcOffset + 5; while (dataLength > 0) { RtpPacket rtpPacket = getRtpPacket(); byte[] dest = rtpPacket.getBuffer(); @@ -133,12 +141,12 @@ private void writeFragmentationUnits(byte[] data, int dataLength, long pts) { writeRtpHeader(dest, pts); // Set FU-A indicator - dest[RTP_HEADER_LENGTH] = (byte) (data[4] & 0xE0); + dest[RTP_HEADER_LENGTH] = (byte) (data[srcOffset + 4] & 0xE0); dest[RTP_HEADER_LENGTH] += 28; // (FU-A) // Set FU-A header - dest[RTP_HEADER_LENGTH + 1] = (byte) (data[4] & 0x1F); - if (offset == 5) { + dest[RTP_HEADER_LENGTH + 1] = (byte) (data[srcOffset + 4] & 0x1F); + if (offset == srcOffset + 5) { // set start flag dest[RTP_HEADER_LENGTH + 1] += 0x80; } @@ -161,4 +169,20 @@ private void writeFragmentationUnits(byte[] data, int dataLength, long pts) { dataLength -= length; } } + + private int searchNalUnit(byte[] data, int offset, int dataLength) { + // NALU_TYPE_SEI (6) よりも小さい nalu type の場合は、後ろに nalu unit が + // 存在する端末がなかったので、ここでは最後までのデータサイズを返却するようにする。 + int naluType = data[offset] & 0x1F; + if (naluType < 6) { + return dataLength; + } + + for (int i = offset; i < dataLength - 4; i++) { + if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x00 && data[i + 3] == 0x01) { + return i; + } + } + return dataLength; + } } diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H265Packetize.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H265Packetize.java index e204239469..82f34b7a83 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H265Packetize.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtp/packet/H265Packetize.java @@ -31,10 +31,18 @@ public void write(byte[] data, int dataLength, long pts) { pts = updateTimestamp(pts * 1000L); - if (dataLength <= MAX_PACKET_SIZE - HEADER_LEN) { - writeSingleNALUnit(data, dataLength, pts); - } else { - writeFragmentationUnits(data, dataLength, pts); + int start = 0; + while (start < dataLength) { + int end = searchNalUnit(data, start + 4, dataLength); + int length = end - start; + + if (length <= MAX_PACKET_SIZE - HEADER_LEN) { + writeSingleNALUnit(data, start, length, pts); + } else { + writeFragmentationUnits(data, start, length, pts); + } + + start += length; } } @@ -51,14 +59,14 @@ public void write(byte[] data, int dataLength, long pts) { // | :...OPTIONAL RTP padding | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - private void writeSingleNALUnit(byte[] data, int dataLength, long pts) { + private void writeSingleNALUnit(byte[] data, int offset, int dataLength, long pts) { RtpPacket rtpPacket = getRtpPacket(); byte[] dest = rtpPacket.getBuffer(); writeRtpHeader(dest, pts); writeNextPacket(dest); - System.arraycopy(data, 4, dest, RTP_HEADER_LENGTH, dataLength - 4); + System.arraycopy(data, offset + 4, dest, RTP_HEADER_LENGTH, dataLength - 4); send(rtpPacket, RTP_HEADER_LENGTH + dataLength - 4, pts); } @@ -91,12 +99,12 @@ private void writeSingleNALUnit(byte[] data, int dataLength, long pts) { // |S|E| FuType | // +---------------+ - private void writeFragmentationUnits(byte[] data, int dataLength, long pts) { - byte naluType = (byte) ((data[4] >> 1) & 0x3F); + private void writeFragmentationUnits(byte[] data, int srcOffset, int dataLength, long pts) { + byte naluType = (byte) ((data[srcOffset + 4] >> 1) & 0x3F); dataLength -= 6; - int offset = 6; + int offset = srcOffset + 6; while (dataLength > 0) { RtpPacket rtpPacket = getRtpPacket(); byte[] dest = rtpPacket.getBuffer(); @@ -106,7 +114,7 @@ private void writeFragmentationUnits(byte[] data, int dataLength, long pts) { dest[RTP_HEADER_LENGTH] = 49 << 1; dest[RTP_HEADER_LENGTH + 1] = 1; dest[RTP_HEADER_LENGTH + 2] = naluType; - if (offset == 6) { + if (offset == srcOffset + 6) { // set start flag dest[RTP_HEADER_LENGTH + 2] += 0x80; } @@ -129,4 +137,20 @@ private void writeFragmentationUnits(byte[] data, int dataLength, long pts) { dataLength -= length; } } + + private int searchNalUnit(byte[] data, int offset, int dataLength) { + // NAL_UNIT_VPS (32) よりも小さい nalu type の場合は、後ろに nalu unit が + // 存在する端末がなかったので、ここでは最後までのデータサイズを返却するようにする。 + int naluType = (data[offset] >> 1) & 0x3F; + if (naluType < 32) { + return dataLength; + } + + for (int i = offset; i < dataLength - 4; i++) { + if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x00 && data[i + 3] == 0x01) { + return i; + } + } + return dataLength; + } } From c3a145734064bfa3b16a75cf10fd13e0d6c41750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Fri, 8 Oct 2021 18:14:39 +0900 Subject: [PATCH 32/40] =?UTF-8?q?=E7=AB=AF=E6=9C=AB=E3=81=AE=E5=9B=9E?= =?UTF-8?q?=E8=BB=A2=E3=82=92=E5=9B=BA=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mediaStreamRecording.json | 3666 ++++++++--------- .../recorder/settings/SettingsActivity.java | 6 + .../HostMediaStreamingRecordingProfile.java | 267 +- .../host/recorder/AbstractMediaRecorder.java | 29 +- .../host/recorder/HostMediaRecorder.java | 20 +- .../host/recorder/camera/Camera2Recorder.java | 8 +- .../camera/CameraSurfaceDrawingThread.java | 12 +- .../recorder/screen/ScreenCastRecorder.java | 2 + .../ScreenCastSurfaceDrawingThread.java | 25 +- .../app/src/main/res/values-ja/strings.xml | 1 + .../app/src/main/res/values/arrays.xml | 17 + .../app/src/main/res/values/strings.xml | 1 + .../res/xml/settings_host_recorder_video.xml | 8 + 13 files changed, 1966 insertions(+), 2096 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json index 9ef4f73879..d54845f1f8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json @@ -1,1903 +1,1885 @@ + { - "swagger": "2.0", - "basePath": "/gotapi/mediaStreamRecording", - "info": { - "title": "Media Stream Recording Profile", - "version": "2.0.0", - "description": "スマートデバイスによる写真撮影、動画録画または音声録音などの操作を行うAPI。" - }, - "consumes": [ - "application/x-www-form-urlencoded", - "multipart/form-data" - ], - "paths": { - "/mediaRecorder": { - "get": { - "operationId": "mediaStreamRecordingMediaRecorderGet", - "x-type": "one-shot", - "summary": "スマートデバイスから使用可能なレコーダーの一覧を取得する。", - "description": "ストリーミング配信する機能をレコーダーとして扱うことができる。 例えば、スマートフォンのカメラの映像や スマートフォンのデスクトップのスクリーンキャストなどをレコーダとして扱ったりすることができる。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "レコーダの一覧を取得結果を返す。 取得に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/MediaRecorderResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "recorders": [ - { - "id": "photo_0", - "name": "Example Camera Recorder - back", - "state": "inactive", - "imageWidth": 3264, - "imageHeight": 2448, - "previewWidth": 640, - "previewHeight": 480, - "previewMaxFrameRate": 10, - "mimeType": "image/png", - "config": "" - }, - { - "id": "photo_1", - "name": "Example Camera Recorder - front", - "state": "inactive", - "imageWidth": 1280, - "imageHeight": 960, - "previewWidth": 640, - "previewHeight": 480, - "previewMaxFrameRate": 10, - "mimeType": "image/png", - "config": "" - }, - { - "id": "video_0", - "name": "Example Video Recorder - back", - "state": "inactive", - "imageWidth": 640, - "imageHeight": 480, - "mimeType": "video/3gp", - "config": "" - }, - { - "id": "video_1", - "name": "Example Video Recorder - front", - "state": "inactive", - "imageWidth": 640, - "imageHeight": 480, - "mimeType": "video/3gp", - "config": "" - }, - { - "id": "audio", - "name": "Example Audio Recorder", - "state": "inactive", - "mimeType": "audio/3gp", - "config": "" - }, - { - "id": "screen", - "name": "Example Screen", - "state": "inactive", - "imageWidth": 1080, - "imageHeight": 1776, - "previewWidth": 270, - "previewHeight": 444, - "previewMaxFrameRate": 10, - "mimeType": "video/x-mjpeg", - "config": "" - } - ] - } - } - } - } - } - }, - "/takePhoto": { - "post": { - "operationId": "mediaStreamRecordingTakePhotoPost", - "x-type": "one-shot", - "summary": "スマートデバイスに対して写真撮影リクエストを送る。", - "description": "targetが指定されていない場合は、GET/mediaStreamRecording/mediaRecorderで 一番最初に見つかるレコーダーが指定される。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "写真撮影結果を返す。 写真撮影に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/TakePhotoResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "uri": "Example URI", - "path": "Example Path" - } - } - } - } - } - }, - "/record": { - "post": { - "operationId": "mediaStreamRecordingRecordPost", - "x-type": "one-shot", - "summary": "スマートデバイスに対して、動画撮影や音声録音の開始リクエストを送る。", - "description": "MediaStreamRecording Stopされない場合は各デバイスが撮影できる最大時間まで 撮影を行い、 その後撮影を停止する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - }, - { - "name": "timeslice", - "description": "タイムスライス。
動画・音声入力などから得られた1フレームを新たに出力先メディアに 書き出すまでの待ち時間。単位はミリ秒。
1000/timesliceが出力メディアの固定フレームレート(フレーム/秒) に相当する。省略された場合には、デバイス毎の挙動でフレーム書き出しを行う。", - "in": "formData", - "required": false, - "minimum": 0, - "type": "integer", - "format": "int64" - } - ], - "responses": { - "200": { - "description": "動画撮影・音声録音開始結果を返す。 開始に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecordResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "uri": "Example URI", - "path": "Example Path" - } - } - } - } - } - }, - "/pause": { - "put": { - "operationId": "mediaStreamRecordingPausePut", - "x-type": "one-shot", - "summary": "スマートデバイスに対して動画撮影または音声録音の一時停止リクエストを送る。", - "description": "すでに撮影または録音が一時停止になっている場合や撮影または録音が行われていない場合は、 エラーを返す。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "動画撮影または音声録音の一時停止送信結果を返す。 一時停止送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } - } - } - } - } + "swagger": "2.0", + "info": { + "description": "スマートデバイスによる写真撮影、動画録画または音声録音などの操作を行うAPI。", + "version": "2.0.0", + "title": "Media Stream Recording Profile" }, - "/resume": { - "put": { - "operationId": "mediaStreamRecordingResumePut", - "x-type": "one-shot", - "summary": "スマートデバイスに対して一時停止状態にある動画撮影または音声録音の 再開リクエストを送る。", - "description": "すでに撮影または録音状態になっている場合はエラーを返す。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "再開リクエストを送った結果を返す。 リクエストの送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" + "basePath": "/gotapi/mediaStreamRecording", + "consumes": ["application/x-www-form-urlencoded", "multipart/form-data"], + "paths": { + "/crop": { + "put": { + "summary": "配信を行う映像の範囲を指定します。", + "description": "現在の配信範囲から指定された範囲まで移動します。", + "operationId": "mediaStreamRecordingCropPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "範囲指定を行うサーバの名前。 省略された場合は、全てのサーバに範囲指定を行います。", + "required": false, + "type": "string" + }, + { + "name": "left", + "in": "formData", + "description": "範囲の左座標を指定します。", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "top", + "in": "formData", + "description": "範囲の上座標を指定します。", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "right", + "in": "formData", + "description": "範囲の右座標を指定します。", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "bottom", + "in": "formData", + "description": "範囲の下座標を指定します。", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "duration", + "in": "formData", + "description": "指定された時間(ミリ秒)をかけて、指定された座標に移動します。 省略された場合には、瞬時に移動します。", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "プレビュー配信を行う範囲を設定結果を返す。", + "schema": { + "$ref": "#/definitions/CommonResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + "delete": { + "summary": "プレビュー配信を行う範囲を解除します。", + "description": "プレビュー配信を行う範囲を解除します。", + "operationId": "mediaStreamRecordingCropDelete", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "query", + "description": "解除するサーバの名前。 省略された場合は、全ての範囲指定を解除します。", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "プレビュー配信を行う範囲を解除結果を返す。 解除に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/CommonResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/stop": { - "put": { - "operationId": "mediaStreamRecordingStopPut", - "x-type": "one-shot", - "summary": "スマートデバイスに対して動画撮影または音声録音の終了リクエストを送る。", - "description": "すでに撮影または録音が行われていない場合はエラーを返す。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "終了リクエストを送った結果を返す。 終了リクエストの送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderStopResponse" + }, + "/encoder": { + "put": { + "summary": "配信を行うエンコーダの設定を行います。", + "description": "配信を行うエンコーダの設定を行います。", + "operationId": "mediaStreamRecordingEncoderPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "エンコーダの設定を行うサーバの名前。 省略された場合は、全てのエンコーダに対して処理を行います。", + "required": false, + "type": "string" + }, + { + "name": "mimeType", + "in": "formData", + "description": "マイムタイプを指定します。
以下のマイムタイプをサポートします。
- video/x-mjpeg
- video/x-rtp
- video/MP2T
- video/x-rtmp", + "required": false, + "type": "string" + }, + { + "name": "width", + "in": "formData", + "description": "配信する映像の横幅を指定します。", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "height", + "in": "formData", + "description": "配信する映像の縦幅を指定します。", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "frameRate", + "in": "formData", + "description": "配信する映像のフレームレートを指定します。", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "bitrate", + "in": "formData", + "description": "配信する映像のビットレートを指定します。", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "keyFrameInterval", + "in": "formData", + "description": "配信する映像のキーフレームインターバルを指定します。", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "codec", + "in": "formData", + "description": "配信する映像のコーデックを指定します。
サポートするコーデック
- h264
- h265", + "required": false, + "type": "string" + }, + { + "name": "profile", + "in": "formData", + "description": "配信する映像のコーデックのプロファイルを指定します。
サポートされるプロファイルは端末ごとに異なります。", + "required": false, + "type": "string" + }, + { + "name": "level", + "in": "formData", + "description": "配信する映像のコーデックのレベルを指定します。
サポートされるレベルは端末ごとに異なります。", + "required": false, + "type": "string" + }, + { + "name": "useSoftwareEncoder", + "in": "formData", + "description": "配信する映像のエンコーダにソフトウェアエンコーダを指定します。", + "required": false, + "type": "boolean" + }, + { + "name": "intraRefresh", + "in": "formData", + "description": "配信する映像のイントラリフレッシュを指定します。", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "jpegQuality", + "in": "formData", + "description": "配信する映像のJPEGの品質を指定します。
video/x-mjpeg のみサポートします。", + "required": false, + "type": "number", + "maximum": 1.0, + "minimum": 0.0 + }, + { + "name": "broadcastUri", + "in": "formData", + "description": "配信先の URI を指定します。
video/x-rtmp のみサポートします。", + "required": false, + "type": "string" + }, + { + "name": "retryCount", + "in": "formData", + "description": "配信先の URI への接続リトライ回数を指定します。
video/x-rtmp のみサポートします。", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "retryInterval", + "in": "formData", + "description": "配信先の URI への接続リトライの間隔を指定します。
video/x-rtmp のみサポートします。", + "required": false, + "type": "integer", + "format": "int32" + } + ], + "responses": { + "200": { + "description": "配信するエンコーダへのパラメータ設定処理の結果を返す。 設定に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/CommonResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + "delete": { + "summary": "配信を行うエンコーダの削除を行います。", + "description": "配信を行うエンコーダの削除を行います。", + "operationId": "mediaStreamRecordingEncoderDelete", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "query", + "description": "削除するエンコーダの名前。", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "指定されたエンコーダの削除結果を返す。 削除に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/CommonResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/muteTrack": { - "put": { - "operationId": "mediaStreamRecordingMuteTrackPut", - "x-type": "one-shot", - "summary": "スマートデバイスに対して動画撮影や音声録音のミュートリクエストを送る。", - "description": "ビデオとオーディオなどの複数トラックが含まれる撮影中メディアにおいては、 オーディオトラックがミュートされる。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "ミュートリクエストを送った結果を返す。 ミュートリクエストの送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/mediaRecorder": { + "get": { + "summary": "スマートデバイスから使用可能なレコーダーの一覧を取得する。", + "description": "ストリーミング配信する機能をレコーダーとして扱うことができる。 例えば、スマートフォンのカメラの映像や スマートフォンのデスクトップのスクリーンキャストなどをレコーダとして扱ったりすることができる。", + "operationId": "mediaStreamRecordingMediaRecorderGet", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "レコーダの一覧を取得結果を返す。 取得に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/MediaRecorderResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "recorders": [ + { + "id": "photo_0", + "name": "Example Camera Recorder - back", + "state": "inactive", + "imageWidth": 3264, + "imageHeight": 2448, + "previewWidth": 640, + "previewHeight": 480, + "previewMaxFrameRate": 10, + "mimeType": "image/png", + "config": "" + }, + { + "id": "photo_1", + "name": "Example Camera Recorder - front", + "state": "inactive", + "imageWidth": 1280, + "imageHeight": 960, + "previewWidth": 640, + "previewHeight": 480, + "previewMaxFrameRate": 10, + "mimeType": "image/png", + "config": "" + }, + { + "id": "video_0", + "name": "Example Video Recorder - back", + "state": "inactive", + "imageWidth": 640, + "imageHeight": 480, + "mimeType": "video/3gp", + "config": "" + }, + { + "id": "video_1", + "name": "Example Video Recorder - front", + "state": "inactive", + "imageWidth": 640, + "imageHeight": 480, + "mimeType": "video/3gp", + "config": "" + }, + { + "id": "audio", + "name": "Example Audio Recorder", + "state": "inactive", + "mimeType": "audio/3gp", + "config": "" + }, + { + "id": "screen", + "name": "Example Screen", + "state": "inactive", + "imageWidth": 1080, + "imageHeight": 1776, + "previewWidth": 270, + "previewHeight": 444, + "previewMaxFrameRate": 10, + "mimeType": "video/x-mjpeg", + "config": "" + } + ] + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/unmuteTrack": { - "put": { - "operationId": "mediaStreamRecordingUnmuteTrackPut", - "x-type": "one-shot", - "summary": "スマートデバイスに対して動画撮影や音声録音のミュート解除リクエストを送る。", - "description": "ビデオとオーディオなどの複数トラックが含まれる撮影中メディアにおいては、 オーディオトラックがミュート解除される。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "ミュート解除リクエストを送った結果を返す。 ミュート解除リクエストの送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/muteTrack": { + "put": { + "summary": "スマートデバイスに対して動画撮影や音声録音のミュートリクエストを送る。", + "description": "ビデオとオーディオなどの複数トラックが含まれる撮影中メディアにおいては、 オーディオトラックがミュートされる。", + "operationId": "mediaStreamRecordingMuteTrackPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "ミュートリクエストを送った結果を返す。 ミュートリクエストの送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/options": { - "get": { - "operationId": "mediaStreamRecordingOptionsGet", - "x-type": "one-shot", - "summary": "スマートデバイスからサポートしている写真撮影、 動画撮影や音声録音のオプションを取得する。", - "description": "ターゲットがサポートしている解像度などの一覧を返す。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "指定したターゲットのPreview/Picture解像度の一覧などを返す。", - "schema": { - "$ref": "#/definitions/OptionsResponse" + }, + "/onPhoto": { + "get": { + "summary": "スマートデバイスの写真撮影通知イベントを取得する。", + "description": "プラグイン側でキャッシュしている最新のイベントメッセージを1つ取得する。", + "operationId": "mediaStreamRecordingOnPhotoGet", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "写真撮影された情報を返す。 情報が返せない場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/PhotoResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "uri": "Example URI", + "path": "Example Path" + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "imageSize": [ - { - "width": 3264, - "height": 2448 - }, - { - "width": 3200, - "height": 2400 - }, - { - "width": 2592, - "height": 1944 - }, - { - "width": 2048, - "height": 1536 - }, - { - "width": 1920, - "height": 1080 - }, - { - "width": 1600, - "height": 1200 - }, - { - "width": 1280, - "height": 960 - }, - { - "width": 1280, - "height": 768 - }, - { - "width": 1280, - "height": 720 - }, - { - "width": 1024, - "height": 768 - }, - { - "width": 800, - "height": 600 - }, - { - "width": 800, - "height": 480 - }, - { - "width": 720, - "height": 480 - }, - { - "width": 640, - "height": 480 - }, - { - "width": 352, - "height": 288 - }, - { - "width": 320, - "height": 240 - }, - { - "width": 176, - "height": 144 - } + "put": { + "summary": "スマートデバイスの写真撮影通知イベントを開始する。", + "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", + "operationId": "mediaStreamRecordingOnPhotoPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "interval", + "in": "formData", + "description": "デバイスプラグインがイベントを送信する間隔。", + "required": false, + "type": "integer", + "format": "int64" + } ], - "previewSize": [ - { - "width": 1920, - "height": 1080 - }, - { - "width": 1600, - "height": 1200 - }, - { - "width": 1280, - "height": 960 - }, - { - "width": 1280, - "height": 768 - }, - { - "width": 1280, - "height": 720 - }, - { - "width": 1024, - "height": 768 - }, - { - "width": 800, - "height": 600 - }, - { - "width": 800, - "height": 480 - }, - { - "width": 720, - "height": 480 - }, - { - "width": 640, - "height": 480 - }, - { - "width": 352, - "height": 288 - }, - { - "width": 320, - "height": 240 - }, - { - "width": 176, - "height": 144 - } + "responses": { + "200": { + "description": "通知イベントの開始結果を返す。 開始に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/EventRegistrationResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "event", + "x-event": { + "schema": { + "$ref": "#/definitions/PhotoEvent" + }, + "examples": { + "application/json": { + "serviceId": "Host.exampleId.localhost.deviceconnect.org", + "profile": "mediastreamrecording", + "attribute": "onphoto", + "photo": { + "uri": "Example URI", + "path": "Example Path", + "mimeType": "image/png" + } + } + } + } + }, + "delete": { + "summary": "スマートデバイスの写真撮影通知イベントを停止する。", + "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", + "operationId": "mediaStreamRecordingOnPhotoDelete", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + } ], - "mimeType": [ - "image/png" - ] - } + "responses": { + "200": { + "description": "通知イベントの停止結果を返す。 停止に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/EventUnregistrationResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "event" } - } - } - }, - "put": { - "operationId": "mediaStreamRecordingOptionsPut", - "x-type": "one-shot", - "summary": "スマートデバイスからサポートしている写真撮影、 動画撮影や音声録音のオプションを設定する。", - "description": "スマートデバイスのPreviewSize,PictureSizeや最大フレームレートなどを設定する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - }, - { - "name": "imageWidth", - "description": "撮影時の横幅。単位はピクセル。previewHeightを指定するときは省略不可。Options API GETで返された値以外を指定した場合はパラメータエラー。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "imageHeight", - "description": "撮影時の縦幅。単位はピクセル。previewWidthを指定するときは省略不可。
Options API GETで返された値以外を指定した場合はパラメータエラー。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewWidth", - "description": "プレビュー時の横幅。単位はピクセル。previewHeightを指定するときは省略不可。
Options API GETで返された値以外を指定した場合はパラメータエラー。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewHeight", - "description": "プレビュー時の縦幅。単位はピクセル。previewWidthを指定するときは省略不可。
Options API GETで返された値以外を指定した場合はパラメータエラー。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewMaxFrameRate", - "description": "プレビュー時の最大フレームレート。単位はfps。範囲は0.0より大きい小数値。", - "in": "formData", - "required": false, - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - { - "name": "previewBitRate", - "description": "プレビュー時のビットレート設定。単位はbps。範囲は0より大きい整数値。", - "in": "formData", - "required": false, - "type": "integer", - "minimum": 0, - "exclusiveMinimum": true - }, - { - "name": "previewKeyFrameInterval", - "description": "プレビューのキーフレーム送信間隔。単位は秒。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewEncoder", - "description": "プレビュー配信を行う時のエンコーダ。
h264 とサポートしている場合には、h265を指定できる。", - "in": "formData", - "required": false, - "enum": [ - "h264", "h265" - ], - "type": "string" - }, - { - "name": "previewProfile", - "description": "エンコーダーのプロファイル名。", - "in": "formData", - "required": false, - "type": "string" - }, - { - "name": "previewLevel", - "description": "エンコーダーのレベル名。", - "in": "formData", - "required": false, - "type": "string" - }, - { - "name": "previewIntraRefresh", - "description": "イントラリフレッシュの値。
0 が指定された場合にはイントラリフレッシュを使用しない。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewJpegQuality", - "description": "Motion JPEG を配信する時のクオリティ。
0.0 から 1.0 の範囲で設定できる。", - "in": "formData", - "required": false, - "minimum": 0.0, - "exclusiveMinimum": false, - "maximum": 1.0, - "exclusiveMaximum": false, - "type": "number" - }, - { - "name": "previewClipLeft", - "description": "プレビューの切り抜き範囲のx座標の開始位置。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewClipTop", - "description": "プレビューの切り抜き範囲のy座標の開始位置。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewClipRight", - "description": "プレビューの切り抜き範囲のx座標の終了位置。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewClipBottom", - "description": "プレビューの切り抜き範囲のy座標の終了位置。", - "in": "formData", - "required": false, - "type": "integer" - }, - { - "name": "previewClipReset", - "description": "true が指定された時にプレビューの切り抜き範囲を解除する。
previewClipLeft などと同時に指定された場合には、previewClipReset を優先する。", - "in": "formData", - "required": false, - "type": "boolean" - }, - { - "name": "mimeType", - "description": "MimeType。動画録画・音声録音するときのエンコードするタイプ。", - "in": "formData", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "オプションの設定結果を返す。 設定に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" + }, + "/onRecordingChange": { + "get": { + "summary": "スマートデバイスでの写真撮影、動画撮影または音声録音の状態変化通知イベントを 取得する。", + "description": "プラグイン側でキャッシュしている最新のイベントメッセージを1つ取得する。", + "operationId": "mediaStreamRecordingOnRecordingChangeGet", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "撮影または録音の状態を返す。 状態を返せない場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecordingStatusResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "media": { + "status": "stop", + "path": "Example Path", + "mimeType": "video/3gp" + } + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } - } - } - } - } - }, - "/onPhoto": { - "get": { - "operationId": "mediaStreamRecordingOnPhotoGet", - "x-type": "one-shot", - "summary": "スマートデバイスの写真撮影通知イベントを取得する。", - "description": "プラグイン側でキャッシュしている最新のイベントメッセージを1つ取得する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "写真撮影された情報を返す。 情報が返せない場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/PhotoResponse" + "put": { + "summary": "スマートデバイスでの写真撮影、動画撮影または音声録音の状態変化通知イベントを 開始する。", + "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", + "operationId": "mediaStreamRecordingOnRecordingChangePut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "interval", + "in": "formData", + "description": "デバイスプラグインがイベントを送信する間隔。", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "撮影または録音の状態変化通知イベントの開始結果を返す。 開始に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/EventRegistrationResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "event", + "x-event": { + "schema": { + "$ref": "#/definitions/RecordingStatusEvent" + }, + "examples": { + "application/json": { + "serviceId": "Host.exampleId.localhost.deviceconnect.org", + "profile": "mediastreamrecording", + "attribute": "onrecordingchange", + "media": { + "status": "stop", + "path": "Example Path", + "mimeType": "image/png" + } + } + } + } }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "uri": "Example URI", - "path": "Example Path" - } + "delete": { + "summary": "スマートデバイスでの写真撮影、動画撮影または音声録音の状態変化通知イベントを 停止する。", + "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", + "operationId": "mediaStreamRecordingOnRecordingChangeDelete", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "撮影または録音の状態変化通知イベントの停止結果を返す。 停止に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/EventUnregistrationResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "event" } - } - } - }, - "put": { - "operationId": "mediaStreamRecordingOnPhotoPut", - "x-type": "event", - "summary": "スマートデバイスの写真撮影通知イベントを開始する。", - "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "interval", - "description": "デバイスプラグインがイベントを送信する間隔。", - "in": "formData", - "required": false, - "type": "integer", - "format": "int64" - } - ], - "responses": { - "200": { - "description": "通知イベントの開始結果を返す。 開始に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/EventRegistrationResponse" + }, + "/options": { + "get": { + "summary": "スマートデバイスからサポートしている写真撮影、 動画撮影や音声録音のオプションを取得する。", + "description": "ターゲットがサポートしている解像度などの一覧を返す。", + "operationId": "mediaStreamRecordingOptionsGet", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "query", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "指定したターゲットのPreview/Picture解像度の一覧などを返す。", + "schema": { + "$ref": "#/definitions/OptionsResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "imageSize": [ + { + "width": 3264, + "height": 2448 + }, + { + "width": 3200, + "height": 2400 + }, + { + "width": 2592, + "height": 1944 + }, + { + "width": 2048, + "height": 1536 + }, + { + "width": 1920, + "height": 1080 + }, + { + "width": 1600, + "height": 1200 + }, + { + "width": 1280, + "height": 960 + }, + { + "width": 1280, + "height": 768 + }, + { + "width": 1280, + "height": 720 + }, + { + "width": 1024, + "height": 768 + }, + { + "width": 800, + "height": 600 + }, + { + "width": 800, + "height": 480 + }, + { + "width": 720, + "height": 480 + }, + { + "width": 640, + "height": 480 + }, + { + "width": 352, + "height": 288 + }, + { + "width": 320, + "height": 240 + }, + { + "width": 176, + "height": 144 + } + ], + "previewSize": [ + { + "width": 1920, + "height": 1080 + }, + { + "width": 1600, + "height": 1200 + }, + { + "width": 1280, + "height": 960 + }, + { + "width": 1280, + "height": 768 + }, + { + "width": 1280, + "height": 720 + }, + { + "width": 1024, + "height": 768 + }, + { + "width": 800, + "height": 600 + }, + { + "width": 800, + "height": 480 + }, + { + "width": 720, + "height": 480 + }, + { + "width": 640, + "height": 480 + }, + { + "width": 352, + "height": 288 + }, + { + "width": 320, + "height": 240 + }, + { + "width": 176, + "height": 144 + } + ], + "mimeType": ["image/png"] + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + "put": { + "summary": "スマートデバイスからサポートしている写真撮影、 動画撮影や音声録音のオプションを設定する。", + "description": "スマートデバイスのPreviewSize,PictureSizeや最大フレームレートなどを設定する。", + "operationId": "mediaStreamRecordingOptionsPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + }, + { + "name": "imageWidth", + "in": "formData", + "description": "撮影時の横幅。単位はピクセル。previewHeightを指定するときは省略不可。Options API GETで返された値以外を指定した場合はパラメータエラー。", + "required": false, + "type": "integer" + }, + { + "name": "imageHeight", + "in": "formData", + "description": "撮影時の縦幅。単位はピクセル。previewWidthを指定するときは省略不可。
Options API GETで返された値以外を指定した場合はパラメータエラー。", + "required": false, + "type": "integer" + }, + { + "name": "previewWidth", + "in": "formData", + "description": "プレビュー時の横幅。単位はピクセル。previewHeightを指定するときは省略不可。
Options API GETで返された値以外を指定した場合はパラメータエラー。", + "required": false, + "type": "integer" + }, + { + "name": "previewHeight", + "in": "formData", + "description": "プレビュー時の縦幅。単位はピクセル。previewWidthを指定するときは省略不可。
Options API GETで返された値以外を指定した場合はパラメータエラー。", + "required": false, + "type": "integer" + }, + { + "name": "previewMaxFrameRate", + "in": "formData", + "description": "プレビュー時の最大フレームレート。単位はfps。範囲は0.0より大きい小数値。", + "required": false, + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + { + "name": "mimeType", + "in": "formData", + "description": "MimeType。動画録画・音声録音するときのエンコードするタイプ。", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "オプションの設定結果を返す。 設定に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } }, - "x-event": { - "schema": { - "$ref": "#/definitions/PhotoEvent" - }, - "examples": { - "application/json": { - "serviceId": "Host.exampleId.localhost.deviceconnect.org", - "profile": "mediastreamrecording", - "attribute": "onphoto", - "photo": { - "uri": "Example URI", - "path": "Example Path", - "mimeType": "image/png" - } - } - } - } - }, - "delete": { - "operationId": "mediaStreamRecordingOnPhotoDelete", - "x-type": "event", - "summary": "スマートデバイスの写真撮影通知イベントを停止する。", - "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "通知イベントの停止結果を返す。 停止に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/EventUnregistrationResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + "/pause": { + "put": { + "summary": "スマートデバイスに対して動画撮影または音声録音の一時停止リクエストを送る。", + "description": "すでに撮影または録音が一時停止になっている場合や撮影または録音が行われていない場合は、 エラーを返す。", + "operationId": "mediaStreamRecordingPausePut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "動画撮影または音声録音の一時停止送信結果を返す。 一時停止送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/preview": { - "get": { - "operationId": "mediaStreamRecordingPreviewGet", - "x-type": "one-shot", - "summary": "スマートデバイスでの動画撮影中に、動画のプレビュー画像を取得する。", - "description": "このイベント通知を行うタイミングに規定は無く、 デバイスプラグインの実装依存とする。
JPEGでデータを受信する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "ストリーミング映像のURIを返す。 映像のURIを返せない場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/PreviewGetResponse" + }, + "/preview": { + "get": { + "summary": "スマートデバイスでの動画撮影中に、動画のプレビュー画像を取得する。", + "description": "このイベント通知を行うタイミングに規定は無く、 デバイスプラグインの実装依存とする。
JPEGでデータを受信する。", + "operationId": "mediaStreamRecordingPreviewGet", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "query", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "ストリーミング映像のURIを返す。 映像のURIを返せない場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/PreviewGetResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "uri": "Example Preview URI" + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "uri": "Example Preview URI" - } - } - } - } - }, - "put": { - "operationId": "mediaStreamRecordingPreviewPut", - "x-type": "streaming", - "summary": "スマートデバイスでの動画撮影中に、動画のプレビュー画像を受信を開始する。", - "description": "このイベント通知を行うタイミングに規定は無く、 デバイスプラグインの実装依存とする。MotionJPEGでデータを受信する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "ストリーミング映像のURIを返す。 映像のURIを返せない場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/PreviewStartResponse" + "put": { + "summary": "スマートデバイスでの動画撮影中に、動画のプレビュー画像を受信を開始する。", + "description": "このイベント通知を行うタイミングに規定は無く、 デバイスプラグインの実装依存とする。MotionJPEGでデータを受信する。", + "operationId": "mediaStreamRecordingPreviewPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "ストリーミング映像のURIを返す。 映像のURIを返せない場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/PreviewStartResponse" + }, + "examples": { + "application/json": { + "streams": [ + { + "mimeType": "video/x-mjpeg", + "uri": "http://localhost:9000/xxxxxx" + }, + { + "mimeType": "video/x-rtp", + "uri": "rtsp://localhost:8086" + } + ], + "result": 0, + "product": "Example System", + "version": "1.0.0", + "uri": "Example Preview URI" + } + } + } + }, + "x-type": "streaming" }, - "examples": { - "application/json": { - "streams": [ - { - "mimeType": "video/x-mjpeg", - "uri": "http://localhost:9000/xxxxxx" - }, - { - "mimeType": "video/x-rtp", - "uri": "rtsp://localhost:8086" - } + "delete": { + "summary": "スマートデバイスでの動画撮影中に、動画のプレビュー画像を受信を停止する。", + "description": "このイベント通知を行うタイミングに規定は無く、 デバイスプラグインの実装依存とする。
MotionJPEGなどでデータを受信する。", + "operationId": "mediaStreamRecordingPreviewDelete", + "parameters": [ + { + "name": "serviceId", + "in": "query", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "query", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } ], - "result": 0, - "product": "Example System", - "version": "1.0.0", - "uri": "Example Preview URI" - } + "responses": { + "200": { + "description": "ストリーミング映像のURIを返す。 映像のURIを返せない場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/PreviewStopResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "streaming" } - } - } - }, - "delete": { - "operationId": "mediaStreamRecordingPreviewDelete", - "x-type": "streaming", - "summary": "スマートデバイスでの動画撮影中に、動画のプレビュー画像を受信を停止する。", - "description": "このイベント通知を行うタイミングに規定は無く、 デバイスプラグインの実装依存とする。
MotionJPEGなどでデータを受信する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "ストリーミング映像のURIを返す。 映像のURIを返せない場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/PreviewStopResponse" + }, + "/preview/mute": { + "put": { + "summary": "音声が付加されているPreview映像に対し、 その映像の音声をミュート状態にするリクエストを送る。", + "description": "例えば、Preview映像がRTSPの場合に、このAPIにより音声をミュート状態にする。", + "operationId": "mediaStreamRecordingPreviewMutePut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "Previewの音声をミュート状態にするリクエストを送った結果を返す。 ミュートリクエストの送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + "delete": { + "summary": "音声が付加されているPreview映像に対し、 その映像の音声のミュート状態を解除するリクエストを送る。", + "description": "例えば、Preview映像がRTSPの場合に、このAPIにより音声のミュート状態を解除する。", + "operationId": "mediaStreamRecordingPreviewMuteDelete", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "Previewの音声のミュート状態を解除するリクエストを送った結果を返す。 ミュート解除リクエストの送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/preview/mute": { - "put": { - "operationId": "mediaStreamRecordingPreviewMutePut", - "x-type": "one-shot", - "summary": "音声が付加されているPreview映像に対し、 その映像の音声をミュート状態にするリクエストを送る。", - "description": "例えば、Preview映像がRTSPの場合に、このAPIにより音声をミュート状態にする。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "Previewの音声をミュート状態にするリクエストを送った結果を返す。 ミュートリクエストの送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/record": { + "post": { + "summary": "スマートデバイスに対して、動画撮影や音声録音の開始リクエストを送る。", + "description": "MediaStreamRecording Stopされない場合は各デバイスが撮影できる最大時間まで 撮影を行い、 その後撮影を停止する。", + "operationId": "mediaStreamRecordingRecordPost", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + }, + { + "name": "timeslice", + "in": "formData", + "description": "タイムスライス。
動画・音声入力などから得られた1フレームを新たに出力先メディアに 書き出すまでの待ち時間。単位はミリ秒。
1000/timesliceが出力メディアの固定フレームレート(フレーム/秒) に相当する。省略された場合には、デバイス毎の挙動でフレーム書き出しを行う。", + "required": false, + "type": "integer", + "minimum": 0, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "動画撮影・音声録音開始結果を返す。 開始に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecordResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "uri": "Example URI", + "path": "Example Path" + } + } + } + }, + "x-type": "one-shot" } - } - } - }, - "delete": { - "operationId": "mediaStreamRecordingPreviewMuteDelete", - "x-type": "one-shot", - "summary": "音声が付加されているPreview映像に対し、 その映像の音声のミュート状態を解除するリクエストを送る。", - "description": "例えば、Preview映像がRTSPの場合に、このAPIにより音声のミュート状態を解除する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "Previewの音声のミュート状態を解除するリクエストを送った結果を返す。 ミュート解除リクエストの送信に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/resume": { + "put": { + "summary": "スマートデバイスに対して一時停止状態にある動画撮影または音声録音の 再開リクエストを送る。", + "description": "すでに撮影または録音状態になっている場合はエラーを返す。", + "operationId": "mediaStreamRecordingResumePut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "再開リクエストを送った結果を返す。 リクエストの送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/preview/requestKeyFrame": { - "post": { - "operationId": "mediaStreamRecordingPreviewRequestKeyFramePost", - "x-type": "one-shot", - "summary": "プレビューのキーフレーム送信を要求する。", - "description": "プレビューのエンコーダに対して映像のキーフレームを生成・送信するように要求する。
1つのターゲットについて複数の映像形式をサポートする場合、起動中のすべてのエンコーダに対して要求する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "formData", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "プレビューのキーフレーム送信リクエストの結果を返す。 失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecorderControlResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/stop": { + "put": { + "summary": "スマートデバイスに対して動画撮影または音声録音の終了リクエストを送る。", + "description": "すでに撮影または録音が行われていない場合はエラーを返す。", + "operationId": "mediaStreamRecordingStopPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "終了リクエストを送った結果を返す。 終了リクエストの送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderStopResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } - } - } - }, - "/broadcast": { - "put": { - "operationId": "mediaStreamRecordingBroadcastPut", - "x-type": "one-shot", - "summary": "スマートデバイスでの動画を指定された URI に配信を開始する。", - "description": "スマートデバイスでの動画を指定された URI に配信を開始する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "broadcastURI", - "description": "配信先のサーバURI。
rtmp://、srt:// のプロトコルに対応", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "配信開始の結果を返す。", - "schema": { - "$ref": "#/definitions/BroadcastStartResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/takePhoto": { + "post": { + "summary": "スマートデバイスに対して写真撮影リクエストを送る。", + "description": "targetが指定されていない場合は、GET/mediaStreamRecording/mediaRecorderで 一番最初に見つかるレコーダーが指定される。", + "operationId": "mediaStreamRecordingTakePhotoPost", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "写真撮影結果を返す。 写真撮影に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/TakePhotoResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0", + "uri": "Example URI", + "path": "Example Path" + } + } + } + }, + "x-type": "one-shot" } - } - } - }, - "delete": { - "operationId": "mediaStreamRecordingBroadcastDelete", - "x-type": "streaming", - "summary": "スマートデバイスでの撮影している映像の配信を停止する。", - "description": "スマートデバイスでの撮影している映像の配信を停止する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "target", - "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "映像の配信停止の結果を返す。", - "schema": { - "$ref": "#/definitions/BroadcastStopResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } + }, + "/unmuteTrack": { + "put": { + "summary": "スマートデバイスに対して動画撮影や音声録音のミュート解除リクエストを送る。", + "description": "ビデオとオーディオなどの複数トラックが含まれる撮影中メディアにおいては、 オーディオトラックがミュート解除される。", + "operationId": "mediaStreamRecordingUnmuteTrackPut", + "parameters": [ + { + "name": "serviceId", + "in": "formData", + "description": "サービスID。取得対象スマートデバイス", + "required": true, + "type": "string" + }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "ミュート解除リクエストを送った結果を返す。 ミュート解除リクエストの送信に失敗した場合はエラーを返す。", + "schema": { + "$ref": "#/definitions/RecorderControlResponse" + }, + "examples": { + "application/json": { + "result": 0, + "product": "Example System", + "version": "1.0.0" + } + } + } + }, + "x-type": "one-shot" } - } } - } }, - "/onRecordingChange": { - "get": { - "operationId": "mediaStreamRecordingOnRecordingChangeGet", - "x-type": "one-shot", - "summary": "スマートデバイスでの写真撮影、動画撮影または音声録音の状態変化通知イベントを 取得する。", - "description": "プラグイン側でキャッシュしている最新のイベントメッセージを1つ取得する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "撮影または録音の状態を返す。 状態を返せない場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/RecordingStatusResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0", - "media": { - "status": "stop", - "path": "Example Path", - "mimeType": "video/3gp" + "definitions": { + "MediaRecorderResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "type": "object", + "required": ["recorders"], + "properties": { + "recorders": { + "type": "array", + "description": "レコーダー情報の配列。", + "title": "レコーダーリスト", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "レコーダーを識別するID。", + "title": "レコーダーID" + }, + "name": { + "type": "string", + "description": "レコーダー名。", + "title": "レコーダー名" + }, + "state": { + "type": "string", + "description": "レコーダーの状態。", + "title": "レコーダーの状態", + "enum": ["inactive", "recording", "paused"] + }, + "mimeType": { + "type": "string", + "description": "レコーダーのエンコードするタイプ。", + "title": "MIME Type" + }, + "config": { + "type": "string", + "description": "カメラに設定がある場合には、ここに文字列としてデータが格納されている。", + "title": "コンフィグ" + }, + "imageWidth": { + "type": "integer", + "description": "レコーダーの現在の横幅。単位はピクセル。録音の場合は省略可。", + "title": "画像の横幅" + }, + "imageHeight": { + "type": "integer", + "description": "レコーダーの現在の縦幅。単位はピクセル。録音の場合は省略可。", + "title": "画像の縦幅" + }, + "previewWidth": { + "type": "integer", + "description": "プレビューの現在の横幅。単位はピクセル。録音の場合は省略可。", + "title": "プレビューの横幅" + }, + "previewHeight": { + "type": "integer", + "description": "プレビューの現在の縦幅。単位はピクセル。録音の場合は省略可。", + "title": "プレビューの縦幅" + }, + "previewMaxFrameRate": { + "type": "number", + "description": "現在のプレビューのフレームレートの最大値。単位はfps。
録音の場合は省略可。", + "title": "プレビューの最大フレームレート" + }, + "audio": { + "type": "object", + "description": "録音する音声に関する情報。", + "title": "音声情報", + "properties": { + "channels": { + "type": "integer", + "description": "音声のチャンネル数。", + "title": "チャンネル数" + }, + "sampleRate": { + "type": "number", + "description": "音声のサンプルレート。単位はHz。", + "title": "サンプルレート" + }, + "sampleSize": { + "type": "integer", + "description": "音声のサンプルサイズ。単位はビット。", + "title": "サンプルサイズ" + }, + "blockSize": { + "type": "integer", + "description": "音声のブロックサイズ。単位はバイト。", + "title": "ブロックサイズ" + } + }, + "required": ["blockSize", "channels", "sampleRate", "sampleSize"] + } + }, + "required": ["config", "id", "mimeType", "name", "state"] + } + } + } } - } - } - } - } - }, - "put": { - "operationId": "mediaStreamRecordingOnRecordingChangePut", - "x-type": "event", - "summary": "スマートデバイスでの写真撮影、動画撮影または音声録音の状態変化通知イベントを 開始する。", - "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "interval", - "description": "デバイスプラグインがイベントを送信する間隔。", - "in": "formData", - "required": false, - "type": "integer", - "format": "int64" - } - ], - "responses": { - "200": { - "description": "撮影または録音の状態変化通知イベントの開始結果を返す。 開始に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/EventRegistrationResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } - } - } - }, - "x-event": { - "schema": { - "$ref": "#/definitions/RecordingStatusEvent" - }, - "examples": { - "application/json": { - "serviceId": "Host.exampleId.localhost.deviceconnect.org", - "profile": "mediastreamrecording", - "attribute": "onrecordingchange", - "media": { - "status": "stop", - "path": "Example Path", - "mimeType": "image/png" - } - } - } - } - }, - "delete": { - "operationId": "mediaStreamRecordingOnRecordingChangeDelete", - "x-type": "event", - "summary": "スマートデバイスでの写真撮影、動画撮影または音声録音の状態変化通知イベントを 停止する。", - "description": "スマートデバイスでサポートしていないパラメータがある場合には、 そのパラメータを省略する。", - "parameters": [ - { - "name": "serviceId", - "description": "サービスID。取得対象スマートデバイス", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "撮影または録音の状態変化通知イベントの停止結果を返す。 停止に失敗した場合はエラーを返す。", - "schema": { - "$ref": "#/definitions/EventUnregistrationResponse" - }, - "examples": { - "application/json": { - "result": 0, - "product": "Example System", - "version": "1.0.0" - } - } - } - } - } - } - }, - "definitions": { - "MediaRecorderResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + ] }, - { - "type": "object", - "required": [ - "recorders" - ], - "properties": { - "recorders": { - "type": "array", - "title": "レコーダーリスト", - "description": "レコーダー情報の配列。", - "items": { - "type": "object", - "required": [ - "id", - "name", - "state", - "mimeType", - "config" - ], - "properties": { - "id": { - "type": "string", - "title": "レコーダーID", - "description": "レコーダーを識別するID。" - }, - "name": { - "type": "string", - "title": "レコーダー名", - "description": "レコーダー名。" - }, - "state": { - "type": "string", - "title": "レコーダーの状態", - "description": "レコーダーの状態。", - "enum": [ - "inactive", - "recording", - "paused" - ] - }, - "mimeType": { - "type": "string", - "title": "MIME Type", - "description": "レコーダーのエンコードするタイプ。" - }, - "config": { - "type": "string", - "title": "コンフィグ", - "description": "カメラに設定がある場合には、ここに文字列としてデータが格納されている。" - }, - "imageWidth": { - "type": "integer", - "title": "画像の横幅", - "description": "レコーダーの現在の横幅。単位はピクセル。録音の場合は省略可。" - }, - "imageHeight": { - "type": "integer", - "title": "画像の縦幅", - "description": "レコーダーの現在の縦幅。単位はピクセル。録音の場合は省略可。" - }, - "previewWidth": { - "type": "integer", - "title": "プレビューの横幅", - "description": "プレビューの現在の横幅。単位はピクセル。録音の場合は省略可。" - }, - "previewHeight": { - "type": "integer", - "title": "プレビューの縦幅", - "description": "プレビューの現在の縦幅。単位はピクセル。録音の場合は省略可。" - }, - "previewMaxFrameRate": { - "type": "number", - "title": "プレビューの最大フレームレート", - "description": "現在のプレビューのフレームレートの最大値。単位はfps。
録音の場合は省略可。" - }, - "previewKeyFrameInterval": { - "type": "integer", - "title": "プレビューのキーフレーム送信間隔", - "description": "現在のプレビューのキーフレーム送信間隔。単位はミリ秒。
録音の場合は省略可。" - }, - "previewEncoderBitRate": { - "type": "integer", - "title": "プレビューのエンコーダのビットレート設定", - "description": "現在のプレビューのエンコーダのビットレート設定。エンコーダへの設定のため、実際の出力ビットレートはエンコーダに依存する。単位はbps。
録音の場合は省略可。" - }, - "audio": { + "TakePhotoResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { "type": "object", - "title": "音声情報", - "description": "録音する音声に関する情報。", - "required": [ - "channels", - "sampleRate", - "sampleSize", - "blockSize" - ], + "required": ["path", "uri"], "properties": { - "channels": { - "type": "integer", - "title": "チャンネル数", - "description": "音声のチャンネル数。" - }, - "sampleRate": { - "type": "number", - "title": "サンプルレート", - "description": "音声のサンプルレート。単位はHz。" - }, - "sampleSize": { - "type": "integer", - "title": "サンプルサイズ", - "description": "音声のサンプルサイズ。単位はビット。" - }, - "blockSize": { - "type": "integer", - "title": "ブロックサイズ", - "description": "音声のブロックサイズ。単位はバイト。" - } + "uri": { + "type": "string", + "description": "撮影された写真のURI。", + "title": "URI" + }, + "path": { + "type": "string", + "description": "撮影された写真へのファイルパス。ルートはデバイスプラグインごとに異なる。
File APIのパラメータとして使用可能。", + "title": "ファイルパス" + } } - } } - } - } - } - } - ] - }, - "TakePhotoResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - }, - { - "type": "object", - "required": [ - "uri", - "path" - ], - "properties": { - "uri": { - "type": "string", - "title": "URI", - "description": "撮影された写真のURI。" - }, - "path": { - "type": "string", - "title": "ファイルパス", - "description": "撮影された写真へのファイルパス。ルートはデバイスプラグインごとに異なる。
File APIのパラメータとして使用可能。" - } - } - } - ] - }, - "RecordResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + ] }, - { - "type": "object", - "properties": { - "uri": { - "type": "string", - "title": "URI", - "description": "動画または音声のURI。" - }, - "path": { - "type": "string", - "title": "ファイルパス", - "description": "動画または音声へのファイルパス。ルートはデバイスプラグインごとに異なる。
File APIのパラメータとして使用可能。" - } - } - } - ] - }, - "OptionsResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + "RecordResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "type": "object", + "properties": { + "uri": { + "type": "string", + "description": "動画または音声のURI。", + "title": "URI" + }, + "path": { + "type": "string", + "description": "動画または音声へのファイルパス。ルートはデバイスプラグインごとに異なる。
File APIのパラメータとして使用可能。", + "title": "ファイルパス" + } + } + } + ] }, - { - "required": [ - "mimeType" - ], - "properties": { - "mimeType": { - "type": "array", - "title": "MIME Type", - "description": "録画・録音する際のエンコードするタイプの一覧", - "items": { - "type": "string" - } - }, - "imageSizes": { - "type": "array", - "title": "撮影時の解像度の一覧", - "description": "レコーダーのサポートする画像の解像度の一覧。録音の場合は省略可。
プラグイン側でリサイズすることでサポートするサイズも含めてよい。", - "items": { - "type": "object", - "title": "解像度", - "description": "画像の解像度。", - "required": [ - "width", - "height" - ], - "properties": { - "width": { - "type": "integer", - "title": "横幅", - "description": "画像の横幅。単位はピクセル。" - }, - "height": { - "type": "integer", - "title": "縦幅", - "description": "画像の縦幅。単位はピクセル。" - } + "OptionsResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "array", + "description": "録画・録音する際のエンコードするタイプの一覧", + "title": "MIME Type", + "items": { + "type": "string" + } + }, + "imageSizes": { + "type": "array", + "description": "レコーダーのサポートする画像の解像度の一覧。録音の場合は省略可。
プラグイン側でリサイズすることでサポートするサイズも含めてよい。", + "title": "撮影時の解像度の一覧", + "items": { + "type": "object", + "description": "画像の解像度。", + "title": "解像度", + "properties": { + "width": { + "type": "integer", + "description": "画像の横幅。単位はピクセル。", + "title": "横幅" + }, + "height": { + "type": "integer", + "description": "画像の縦幅。単位はピクセル。", + "title": "縦幅" + } + }, + "required": ["height", "width"] + } + }, + "previewSizes": { + "type": "array", + "description": "プレビューで利用可能な解像度の一覧。
録音の場合、またはプレビューを提供しない場合は省略可。
プラグイン側でリサイズすることでサポートするサイズも含めてよい。", + "title": "プレビュー時の解像度の一覧", + "items": { + "type": "object", + "description": "画像の解像度。", + "title": "解像度", + "properties": { + "width": { + "type": "integer", + "description": "画像の横幅。単位はピクセル。", + "title": "横幅" + }, + "height": { + "type": "integer", + "description": "画像の縦幅。単位はピクセル。", + "title": "縦幅" + } + }, + "required": ["height", "width"] + } + } + } } - } - }, - "previewSizes": { - "type": "array", - "title": "プレビュー時の解像度の一覧", - "description": "プレビューで利用可能な解像度の一覧。
録音の場合、またはプレビューを提供しない場合は省略可。
プラグイン側でリサイズすることでサポートするサイズも含めてよい。", - "items": { - "type": "object", - "title": "解像度", - "description": "画像の解像度。", - "required": [ - "width", - "height" - ], - "properties": { - "width": { - "type": "integer", - "title": "横幅", - "description": "画像の横幅。単位はピクセル。" - }, - "height": { - "type": "integer", - "title": "縦幅", - "description": "画像の縦幅。単位はピクセル。" - } + ] + }, + "PhotoResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "$ref": "#/definitions/PhotoInfo" } - } - } - } - } - ] - }, - "PhotoResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + ] }, - { - "$ref": "#/definitions/PhotoInfo" - } - ] - }, - "PhotoEvent": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonEvent" + "PhotoEvent": { + "allOf": [ + { + "$ref": "#/definitions/CommonEvent" + }, + { + "$ref": "#/definitions/PhotoInfo" + } + ] }, - { - "$ref": "#/definitions/PhotoInfo" - } - ] - }, - "PhotoInfo": { - "type": "object", - "required": [ - "photo" - ], - "properties": { - "photo": { - "type": "object", - "title": "写真データ", - "description": "撮影された写真データ。", - "required": [ - "path", - "mimeType" - ], - "properties": { - "uri": { - "type": "string", - "title": "URI", - "description": "撮影された写真のURI。" - }, - "path": { - "type": "string", - "title": "ファイルパス", - "description": "ファイルが存在するパス。ルートはデバイスプラグインごとに異なる。
File APIで使用可能。" - }, - "mimeType": { - "type": "string", - "title": "MIME Type", - "description": "撮影された写真のMIME Type。動画・音声を識別するために使用する。" + "PhotoInfo": { + "type": "object", + "required": ["photo"], + "properties": { + "photo": { + "type": "object", + "description": "撮影された写真データ。", + "title": "写真データ", + "properties": { + "uri": { + "type": "string", + "description": "撮影された写真のURI。", + "title": "URI" + }, + "path": { + "type": "string", + "description": "ファイルが存在するパス。ルートはデバイスプラグインごとに異なる。
File APIで使用可能。", + "title": "ファイルパス" + }, + "mimeType": { + "type": "string", + "description": "撮影された写真のMIME Type。動画・音声を識別するために使用する。", + "title": "MIME Type" + } + }, + "required": ["mimeType", "path"] + } } - } - } - } - }, - "PreviewGetResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" }, - { - "type": "object", - "required": [ - "uri" - ], - "properties": { - "uri": { - "type": "string", - "title": "プレビュー画像URI", - "description": "プレビュー画像URI。形式はJPEGとする。" - } - } - } - ] - }, - "PreviewStartResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + "PreviewGetResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "type": "object", + "required": ["uri"], + "properties": { + "uri": { + "type": "string", + "description": "プレビュー画像URI。形式はJPEGとする。", + "title": "プレビュー画像URI" + } + } + } + ] }, - { - "type": "object", - "required": [ - "uri" - ], - "properties": { - "uri": { - "type": "string", - "title": "プレビュー配信URI", - "description": "開始したプレビューの配信用URI。形式はMotionJPEGとする。" - }, - "streams": { - "type": "array", - "title": "ストリームのリスト", - "description": "ストリーム情報の配列。", - "items": { - "type": "object", - "required": [ - "mimeType", - "uri" - ], - "properties": { - "mimeType": { - "type": "string", - "title": "ストリームのMIMEType", - "description": "ストリームのMIMEType。" - }, - "uri": { - "type": "string", - "title": "ストリームのURI", - "description": "ストリームのURI。" - } + "PreviewStartResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "type": "object", + "required": ["uri"], + "properties": { + "uri": { + "type": "string", + "description": "開始したプレビューの配信用URI。形式はMotionJPEGとする。", + "title": "プレビュー配信URI" + }, + "streams": { + "type": "array", + "description": "ストリーム情報の配列。", + "title": "ストリームのリスト", + "items": { + "type": "object", + "properties": { + "mimeType": { + "type": "string", + "description": "ストリームのMIMEType。", + "title": "ストリームのMIMEType" + }, + "uri": { + "type": "string", + "description": "ストリームのURI。", + "title": "ストリームのURI" + } + }, + "required": ["mimeType", "uri"] + } + }, + "audio": { + "type": "object", + "description": "音声配信に関する情報。音声のみの配信をサポートしない場合は省略可。", + "title": "音声配信情報", + "properties": { + "uri": { + "type": "string", + "description": "音声配信URI。", + "title": "音声配信URI" + } + }, + "required": ["uri"] + } + } } - } - }, - "audio": { - "type": "object", - "title": "音声配信情報", - "description": "音声配信に関する情報。音声のみの配信をサポートしない場合は省略可。", - "required": [ - "uri" - ], - "properties": { - "uri": { - "type": "string", - "title": "音声配信URI", - "description": "音声配信URI。" + ] + }, + "PreviewStopResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" } - } - } - } - } - ] - }, - "PreviewStopResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - } - ] - }, - "BroadcastStartResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - } - ] - }, - "BroadcastStopResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - } - ] - }, - "RecordingStatusResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + ] }, - { - "$ref": "#/definitions/RecordingStatusInfo" - } - ] - }, - "RecordingStatusEvent": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonEvent" + "RecordingStatusResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "$ref": "#/definitions/RecordingStatusInfo" + } + ] }, - { - "$ref": "#/definitions/RecordingStatusInfo" - } - ] - }, - "RecordingStatusInfo": { - "type": "object", - "required": [ - "media" - ], - "properties": { - "media": { - "type": "object", - "title": "レコーディング情報", - "description": "レコーディング情報", - "required": [ - "status", - "mimeType" - ], - "properties": { - "status": { - "type": "string", - "title": "レコーディングの状態", - "description": "レコーディングの状態を識別する文字列。", - "enum": [ - "recording", - "stop", - "pause", - "resume", - "mutetrack", - "unmutetrack", - "error", - "warning" - ] - }, - "uri": { - "type": "string", - "title": "URI", - "description": "動画または音声のURI。" - }, - "path": { - "type": "string", - "title": "ファイルパス", - "description": "ファイルが存在するパス。ルートはデバイスプラグインごとに違う。" - }, - "mimeType": { - "type": "string", - "title": "MIME Type", - "description": "録画・録音が開始されたメディアのMIME Type。
このタイプで、動画、音声などを識別する。" - }, - "errorMessasge": { - "type": "string", - "title": "エラーメッセージ", - "description": "エラー、警告内容を伝える文字列。
状態が error、warning の時のみ付加される。省略可能。" - } - } - } - } - }, - "RecorderStopResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" + "RecordingStatusEvent": { + "allOf": [ + { + "$ref": "#/definitions/CommonEvent" + }, + { + "$ref": "#/definitions/RecordingStatusInfo" + } + ] }, - { - "type": "object", - "properties": { - "uri": { - "type": "string", - "title": "URI", - "description": "動画または音声のURI。" - }, - "path": { - "type": "string", - "title": "ファイルパス", - "description": "動画または音声へのファイルパス。
ルートはデバイスプラグインごとに異なる。
File APIのパラメータとして使用可能。" + "RecordingStatusInfo": { + "type": "object", + "required": ["media"], + "properties": { + "media": { + "type": "object", + "description": "レコーディング情報", + "title": "レコーディング情報", + "properties": { + "status": { + "type": "string", + "description": "レコーディングの状態を識別する文字列。", + "title": "レコーディングの状態", + "enum": ["recording", "stop", "pause", "resume", "mutetrack", "unmutetrack", "error", "warning"] + }, + "uri": { + "type": "string", + "description": "動画または音声のURI。", + "title": "URI" + }, + "path": { + "type": "string", + "description": "ファイルが存在するパス。ルートはデバイスプラグインごとに違う。", + "title": "ファイルパス" + }, + "mimeType": { + "type": "string", + "description": "録画・録音が開始されたメディアのMIME Type。
このタイプで、動画、音声などを識別する。", + "title": "MIME Type" + }, + "errorMessasge": { + "type": "string", + "description": "エラー、警告内容を伝える文字列。
状態が error、warning の時のみ付加される。省略可能。", + "title": "エラーメッセージ" + } + }, + "required": ["mimeType", "status"] + } } - } - } - ] - }, - "RecorderControlResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - } - ] - }, - "EventRegistrationResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - } - ] - }, - "EventUnregistrationResponse": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/CommonResponse" - } - ] - }, - "CommonResponse": { - "type": "object", - "required": [ - "result", - "product", - "version" - ], - "properties": { - "result": { - "type": "integer", - "title": "処理結果", - "description": "0: 正常応答
0以外: 異常応答" }, - "product": { - "type": "string", - "title": "システム名", - "description": "DeviceConnectシステムの名前。" + "RecorderStopResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + }, + { + "type": "object", + "properties": { + "uri": { + "type": "string", + "description": "動画または音声のURI。", + "title": "URI" + }, + "path": { + "type": "string", + "description": "動画または音声へのファイルパス。
ルートはデバイスプラグインごとに異なる。
File APIのパラメータとして使用可能。", + "title": "ファイルパス" + } + } + } + ] }, - "version": { - "type": "string", - "title": "システムバージョン", - "description": "DeviceConnectシステムのバージョン名。" + "RecorderControlResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + } + ] }, - "hmac": { - "type": "string", - "title": "署名", - "description": "レスポンスに対する署名。
アプリケーション側から事前にHMACキーを共有されていた場合は必須。" - } - } - }, - "CommonEvent": { - "type": "object", - "required": [ - "serviceId", - "profile", - "interface", - "attribute" - ], - "properties": { - "serviceId": { - "type": "string", - "title": "サービスID", - "description": "イベントを送信したサービスのID" + "EventRegistrationResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + } + ] }, - "profile": { - "type": "string", - "title": "プロファイル名", - "description": "プロファイル名。" + "EventUnregistrationResponse": { + "allOf": [ + { + "$ref": "#/definitions/CommonResponse" + } + ] }, - "interface": { - "type": "string", - "title": "インターフェース名", - "description": "インターフェース名。" + "CommonResponse": { + "type": "object", + "required": ["product", "result", "version"], + "properties": { + "result": { + "type": "integer", + "description": "0: 正常応答
0以外: 異常応答", + "title": "処理結果" + }, + "product": { + "type": "string", + "description": "DeviceConnectシステムの名前。", + "title": "システム名" + }, + "version": { + "type": "string", + "description": "DeviceConnectシステムのバージョン名。", + "title": "システムバージョン" + }, + "hmac": { + "type": "string", + "description": "レスポンスに対する署名。
アプリケーション側から事前にHMACキーを共有されていた場合は必須。", + "title": "署名" + } + } }, - "attribute": { - "type": "string", - "title": "アトリビュート名", - "description": "アトリビュート名。" + "CommonEvent": { + "type": "object", + "required": ["attribute", "interface", "profile", "serviceId"], + "properties": { + "serviceId": { + "type": "string", + "description": "イベントを送信したサービスのID", + "title": "サービスID" + }, + "profile": { + "type": "string", + "description": "プロファイル名。", + "title": "プロファイル名" + }, + "interface": { + "type": "string", + "description": "インターフェース名。", + "title": "インターフェース名" + }, + "attribute": { + "type": "string", + "description": "アトリビュート名。", + "title": "アトリビュート名" + } + } } - } } - } -} +} \ No newline at end of file diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java index b5f40c8613..121aa14074 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsActivity.java @@ -3,6 +3,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.os.Bundle; import android.view.MenuItem; @@ -28,6 +29,11 @@ public void onCreate(Bundle savedInstanceState) { } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + @Override public boolean onOptionsItemSelected(final MenuItem item) { if (item.getItemId() == android.R.id.home) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 1df1137ee1..6b5259fe29 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -764,222 +764,6 @@ public boolean onRequest(final Intent request, final Intent response) { } }); - // POST /gotapi/mediaStreamRecording/encoder - addApi(new PostApi() { - @Override - public String getAttribute() { - return "encoder"; - } - - @Override - public boolean onRequest(Intent request, Intent response) { - String target = getTarget(request); - String name = request.getStringExtra("name"); - String mimeTypeValue = request.getStringExtra("mimeType"); - Integer port = parseInteger(request, "port"); - Integer width = parseInteger(request, "width"); - Integer height = parseInteger(request, "height"); - Integer frameRate = parseInteger(request, "frameRate"); - Integer bitRate = parseInteger(request, "bitRate"); - Integer keyFrameInterval = parseInteger(request, "keyFrameInterval"); - String codec = request.getStringExtra("codec"); - String profile = request.getStringExtra("profile"); - String level = request.getStringExtra("level"); - Integer intraRefresh = parseInteger("intraRefresh"); - Boolean useSoftwareEncoder = parseBoolean("useSoftwareEncoder"); - Double jpegQuality = parseDouble(request, "jpegQuality"); - String broadcastUri = request.getStringExtra("broadcastUri"); - Integer retryCount = parseInteger(request, "retryCount"); - Integer retryInterval = parseInteger(request, "retryInterval"); - - if (name == null) { - MessageUtils.setInvalidRequestParameterError(response, "name is not set."); - return true; - } - - if (mimeTypeValue == null) { - MessageUtils.setInvalidRequestParameterError(response, "mimeType is not set."); - return true; - } - - HostMediaRecorder.MimeType mimeType = HostMediaRecorder.MimeType.typeOf(mimeTypeValue); - if (mimeType == HostMediaRecorder.MimeType.UNKNOWN) { - MessageUtils.setInvalidRequestParameterError(response, "mimeType is unknown."); - return true; - } - - final HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); - if (recorder == null) { - MessageUtils.setInvalidRequestParameterError(response, "target is invalid."); - return true; - } - - String encoderId = recorder.getId() + "-" + name; - - List encoderIdList = recorder.getSettings().getEncoderIdList(); - if (encoderIdList.contains(encoderId)) { - MessageUtils.setInvalidRequestParameterError(response, "name is already exists."); - return true; - } - - if (width != null && width < 0) { - MessageUtils.setInvalidRequestParameterError(response, - "width is invalid. value=" + width); - return true; - } - - if (height != null && height < 0) { - MessageUtils.setInvalidRequestParameterError(response, - "height is invalid. value=" + height); - return true; - } - - if (frameRate != null && frameRate <= 0) { - MessageUtils.setInvalidRequestParameterError(response, - "frameRate is invalid. value=" + frameRate); - return true; - } - - if (bitRate != null && bitRate <= 0) { - MessageUtils.setInvalidRequestParameterError(response, - "bitRate. value=" + bitRate); - return true; - } - - if (keyFrameInterval != null && keyFrameInterval <= 0) { - MessageUtils.setInvalidRequestParameterError(response, - "keyFrameInterval. value=" + keyFrameInterval); - return true; - } - - if (intraRefresh != null && intraRefresh < 0) { - MessageUtils.setInvalidRequestParameterError(response, - "intraRefresh is invalid. value=" + intraRefresh); - return true; - } - - if (jpegQuality != null) { - if (jpegQuality < 0.0 || jpegQuality > 1.0) { - MessageUtils.setInvalidRequestParameterError(response, - "jpegQuality is invalid. value=" + jpegQuality); - return true; - } - } - - if (retryCount != null) { - if (retryCount < 0) { - MessageUtils.setInvalidRequestParameterError(response, - "retryCount is invalid. value=" + retryCount); - return true; - } - } - - if (retryInterval != null) { - if (retryInterval <= 0) { - MessageUtils.setInvalidRequestParameterError(response, - "retryInterval is invalid. value=" + retryInterval); - return true; - } - } - - HostMediaRecorder.ProfileLevel profileLevel = convertProfileLevel(recorder, codec, profile, level); - - recorder.requestPermission(new HostMediaRecorder.PermissionCallback() { - @Override - public void onAllowed() { - HostMediaRecorder.EncoderSettings encoderSettings = new HostMediaRecorder.EncoderSettings(getContext(), encoderId); - encoderSettings.setName(name); - encoderSettings.setMimeType(mimeType); - - if (width != null && height != null) { - encoderSettings.setPreviewSize(new Size(width, height)); - } else { - encoderSettings.setPreviewSize(new Size(640, 480)); - } - - if (port != null) { - encoderSettings.setPort(port); - } else { - encoderSettings.setPort(14000); - } - - if (frameRate != null) { - encoderSettings.setPreviewMaxFrameRate(frameRate); - } else { - encoderSettings.setPreviewMaxFrameRate(30); - } - - if (bitRate != null) { - encoderSettings.setPreviewBitRate(bitRate * 1024); - } else { - encoderSettings.setPreviewBitRate(2 * 1024 * 1024); - } - - if (keyFrameInterval != null) { - encoderSettings.setPreviewKeyFrameInterval(keyFrameInterval); - } else { - encoderSettings.setPreviewKeyFrameInterval(5); - } - - if (codec != null) { - encoderSettings.setPreviewEncoder(codec); - // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする - encoderSettings.setProfileLevel(null); - } - - if (profileLevel != null) { - encoderSettings.setProfileLevel(profileLevel); - } - - if (intraRefresh != null) { - encoderSettings.setIntraRefresh(intraRefresh); - } - - if (useSoftwareEncoder != null) { - encoderSettings.setUseSoftwareEncoder(useSoftwareEncoder); - } - - if (jpegQuality != null) { - encoderSettings.setPreviewQuality((int) (jpegQuality * 100)); - } else { - encoderSettings.setPreviewQuality(80); - } - - if (broadcastUri != null) { - encoderSettings.setBroadcastURI(broadcastUri); - } else { - encoderSettings.setBroadcastURI("rtmp://localhost:1935"); - } - - if (retryCount != null) { - encoderSettings.setRetryCount(retryCount); - } else { - encoderSettings.setRetryCount(0); - } - - if (retryInterval != null) { - encoderSettings.setRetryInterval(retryInterval); - } else { - encoderSettings.setRetryInterval(3000); - } - - ((AbstractMediaRecorder) recorder).addEncoder(encoderId, encoderSettings); - - setResult(response, DConnectMessage.RESULT_OK); - sendResponse(response); - } - - @Override - public void onDisallowed() { - MessageUtils.setUnknownError(response, "Permission for camera is not granted."); - sendResponse(response); - } - }); - - return false; - } - }); - // PUT /gotapi/mediaStreamRecording/encoder addApi(new PutApi() { @Override @@ -990,6 +774,7 @@ public String getAttribute() { @Override public boolean onRequest(Intent request, Intent response) { String target = getTarget(request); + String mimeTypeValue = request.getStringExtra("mimeType"); String name = request.getStringExtra("name"); Integer width = parseInteger(request, "width"); Integer height = parseInteger(request, "height"); @@ -1006,7 +791,6 @@ public boolean onRequest(Intent request, Intent response) { Integer retryCount = parseInteger(request, "retryCount"); Integer retryInterval = parseInteger(request, "retryInterval"); - HostMediaRecorder recorder = mRecorderMgr.getRecorder(target); if (recorder == null) { MessageUtils.setInvalidRequestParameterError(response, @@ -1014,12 +798,7 @@ public boolean onRequest(Intent request, Intent response) { return true; } - List encoderIdList = getEncoderSettings(recorder, name); - if (encoderIdList.isEmpty()) { - MessageUtils.setInvalidRequestParameterError(response, - "name is invalid."); - return true; - } + List encoderIdList = getEncoderSettings(recorder, name, false); if (width != null && width < 0) { MessageUtils.setInvalidRequestParameterError(response, @@ -1088,6 +867,39 @@ public boolean onRequest(Intent request, Intent response) { public void onAllowed() { HostMediaRecorder.Settings settings = recorder.getSettings(); + for (String encoderId : encoderIdList) { + HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); + if (encoderSettings == null) { + if (mimeTypeValue == null) { + MessageUtils.setInvalidRequestParameterError(response, "mimeType is not set."); + sendResponse(response); + return; + } + + HostMediaRecorder.MimeType mimeType = HostMediaRecorder.MimeType.typeOf(mimeTypeValue); + if (mimeType == HostMediaRecorder.MimeType.UNKNOWN) { + MessageUtils.setInvalidRequestParameterError(response, "mimeType is unknown."); + sendResponse(response); + return; + } + + encoderSettings = new HostMediaRecorder.EncoderSettings(getContext(), encoderId); + encoderSettings.setName(name); + encoderSettings.setMimeType(mimeType); + encoderSettings.setPreviewSize(new Size(640, 480)); + encoderSettings.setPort(14000); + encoderSettings.setPreviewMaxFrameRate(30); + encoderSettings.setPreviewBitRate(2 * 1024 * 1024); + encoderSettings.setPreviewKeyFrameInterval(5); + encoderSettings.setPreviewQuality(80); + encoderSettings.setBroadcastURI("rtmp://localhost:1935"); + encoderSettings.setRetryCount(0); + encoderSettings.setRetryInterval(3000); + + ((AbstractMediaRecorder) recorder).addEncoder(encoderId, encoderSettings); + } + } + if (width != null && height != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setPreviewSize(new Size(width, height)); @@ -1257,7 +1069,7 @@ public boolean onRequest(Intent request, Intent response) { return true; } - List encoderIdList = getEncoderSettings(recorder, name); + List encoderIdList = getEncoderSettings(recorder, name, true); if (encoderIdList.isEmpty()) { MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); return true; @@ -1307,7 +1119,7 @@ public boolean onRequest(Intent request, Intent response) { return true; } - List encoderIdList = getEncoderSettings(recorder, name); + List encoderIdList = getEncoderSettings(recorder, name, true); if (encoderIdList.isEmpty()) { MessageUtils.setInvalidRequestParameterError(response, "name is invalid."); return true; @@ -1447,7 +1259,7 @@ private HostDevicePlugin getHostDevicePlugin() { return (HostDevicePlugin) getContext(); } - private List getEncoderSettings(HostMediaRecorder recorder, String name) { + private List getEncoderSettings(HostMediaRecorder recorder, String name, boolean checkExist) { List encoderIdList = new ArrayList<>(); if (name == null) { encoderIdList.addAll(recorder.getSettings().getEncoderIdList()); @@ -1455,7 +1267,7 @@ private List getEncoderSettings(HostMediaRecorder recorder, String name) String[] names = name.split(","); for (String n : names) { String encoderId = recorder.getId() + "-" + n; - if (recorder.getSettings().existEncoderId(encoderId)) { + if (!checkExist || recorder.getSettings().existEncoderId(encoderId)) { encoderIdList.add(encoderId); } } @@ -1716,7 +1528,6 @@ private void setOptions(final Intent request, final Intent response) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); if (encoderId != null && mimeType.equals(encoderSettings.getMimeType().getValue())) { encoderSettings.setPreviewSize(new Size(previewWidth, previewHeight)); - } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java index 9602d1a140..3423c3d57a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java @@ -176,7 +176,9 @@ public boolean isPreviewRunning() { @Override public void onDisplayRotation(int rotation) { - onConfigChange(); + if (getSettings().getOrientation() == -1) { + onConfigChange(); + } } @Override @@ -444,19 +446,20 @@ public void addEncoder(String encoderId, EncoderSettings encoderSettings) { * @param encoderId エンコーダ */ public void removeEncoder(String encoderId) { - EncoderSettings encoderSettings = getSettings().getEncoderSetting(encoderId); - switch (encoderSettings.getMimeType()) { - case MJPEG: - case RTSP: - case SRT: - getServerProvider().removeLiveStreaming(encoderId); - break; - case RTMP: - getBroadcasterProvider().removeLiveStreaming(encoderId); - break; + if (getSettings().existEncoderId(encoderId)) { + EncoderSettings encoderSettings = getSettings().getEncoderSetting(encoderId); + switch (encoderSettings.getMimeType()) { + case MJPEG: + case RTSP: + case SRT: + getServerProvider().removeLiveStreaming(encoderId); + break; + case RTMP: + getBroadcasterProvider().removeLiveStreaming(encoderId); + break; + } + getSettings().removeEncoder(encoderId); } - - getSettings().removeEncoder(encoderId); } /** diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index e807396236..af4bf62b90 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -1241,7 +1241,7 @@ public void addEncoder(String encoderId) { */ public void removeEncoder(String encoderId) { EncoderSettings encoderSettings = getEncoderSetting(encoderId); - if (encoderId != null) { + if (encoderId != null && encoderSettings != null) { encoderSettings.clear(); } @@ -1252,6 +1252,24 @@ public void removeEncoder(String encoderId) { // カメラ設定 + /** + * カメラの向きを取得します. + * + * @return カメラの向き + */ + public int getOrientation() { + return mProperty.getInteger("camera_orientation", -1); + } + + /** + * カメラの向きを設定します. + * + * @param orientation カメラの向き + */ + public void setOrientation(int orientation) { + mProperty.put("camera_orientation", orientation); + } + /** * 写真サイズを取得します. * diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java index cfa3ca2f42..db69ceede8 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/Camera2Recorder.java @@ -216,6 +216,7 @@ private void initSupportedSettings() { if (supportedFpsList.size() > 0) { mSettings.setPreviewFps(supportedFpsList.get(supportedFpsList.size() - 1)); } + mSettings.setOrientation(Surface.ROTATION_90); // 音声設定 mSettings.setPreviewAudioSource(null); @@ -480,8 +481,13 @@ private void takePhotoInternal(final @NonNull OnPhotoEventListener listener) { return; } + int currentRotation = mCurrentRotation; + if (getSettings().getOrientation() != -1) { + currentRotation = getSettings().getOrientation(); + } + byte[] jpeg = ImageUtil.convertToJPEG(photo); - int deviceRotation = ROTATIONS.get(mCurrentRotation); + int deviceRotation = ROTATIONS.get(currentRotation); int cameraRotation = mCameraWrapper.getSensorOrientation(); int degrees = (360 - deviceRotation + cameraRotation) % 360; if (mFacing == CameraFacing.FRONT) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java index 2163a537f4..92d52d0122 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/camera/CameraSurfaceDrawingThread.java @@ -29,11 +29,15 @@ public CameraSurfaceDrawingThread(Camera2Recorder recorder) { @Override public int getDisplayRotation() { - WindowManager wm = (WindowManager) mRecorder.getContext().getSystemService(Context.WINDOW_SERVICE); - if (wm == null) { - throw new RuntimeException("WindowManager is not supported."); + int orientation = mRecorder.getSettings().getOrientation(); + if (orientation == -1) { + WindowManager wm = (WindowManager) mRecorder.getContext().getSystemService(Context.WINDOW_SERVICE); + if (wm == null) { + throw new RuntimeException("WindowManager is not supported."); + } + return wm.getDefaultDisplay().getRotation(); } - return wm.getDefaultDisplay().getRotation(); + return orientation; } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java index 88bf1c6a4e..d11bb0a370 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastRecorder.java @@ -21,6 +21,7 @@ import android.util.Range; import android.util.Size; import android.view.Display; +import android.view.Surface; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; @@ -135,6 +136,7 @@ private void initSupportedSettings() { if (!mSettings.isInitialized()) { mSettings.setPreviewSize(mSettings.getSupportedPreviewSizes().get(0)); mSettings.setPictureSize(mSettings.getSupportedPictureSizes().get(0)); + mSettings.setOrientation(Surface.ROTATION_90); // 音声設定 mSettings.setPreviewAudioSource(null); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java index 978e38fcca..1be49f8399 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/screen/ScreenCastSurfaceDrawingThread.java @@ -44,7 +44,20 @@ public int getDisplayRotation() { @Override public boolean isSwappedDimensions() { - return mRecorder.getScreenCastMgr().isSwappedDimensions(); + int orientation = mRecorder.getSettings().getOrientation(); + if (orientation == -1) { + return mRecorder.getScreenCastMgr().isSwappedDimensions(); + } + + switch (orientation) { + case Surface.ROTATION_0: + case Surface.ROTATION_180: + return false; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + default: + return true; + } } @Override @@ -61,12 +74,10 @@ protected void onStopped() { public void start() { HostMediaRecorder.Settings settings = mRecorder.getSettings(); Size previewSize = settings.getPreviewSize(); - if (previewSize != null) { - int width = isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); - int height = isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); - setSize(width, height); - super.start(); - } + int width = isSwappedDimensions() ? previewSize.getHeight() : previewSize.getWidth(); + int height = isSwappedDimensions() ? previewSize.getWidth() : previewSize.getHeight(); + setSize(width, height); + super.start(); } private void startScreenCast(SurfaceTexture surfaceTexture) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml index 35a2a9dcdd..dc12202571 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml @@ -90,6 +90,7 @@ 静止画解像度 プレビュー解像度 フレームレート (fps) + 向き ビットレート (bps) ビットレートモード キーフレームインターバル(秒) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/arrays.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/arrays.xml index 652a70c2e0..fed36f6760 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/arrays.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/arrays.xml @@ -5,11 +5,28 @@ フロント バック + 0 1 + + 自由回転 + 0度 + 90度 + 180度 + 270度 + + + + -1 + 0 + 1 + 2 + 3 + + H264 H265 diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml index e0ec654918..a0dc2f71aa 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml @@ -88,6 +88,7 @@ Still Image Resolution Preview Resolution Frame rate (fps) + Orientation Bitrate (bps) Bitrate Mode Key frame interval (sec) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml index e1b314bdea..6a6c24aa55 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/xml/settings_host_recorder_video.xml @@ -24,6 +24,14 @@ app:iconSpaceReserved="false" app:useSimpleSummaryProvider="true" /> + + Date: Mon, 11 Oct 2021 17:44:20 +0900 Subject: [PATCH 33/40] =?UTF-8?q?=E3=82=AD=E3=83=BC=E3=83=95=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=81=AE=E9=96=93=E9=9A=94=E3=81=AB=E5=AF=BE?= =?UTF-8?q?=E3=81=97=E3=81=A6=200=20=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../host/profile/HostMediaStreamingRecordingProfile.java | 6 +++--- .../deviceplugin/host/recorder/HostMediaRecorder.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 6b5259fe29..393e50298f 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -784,8 +784,8 @@ public boolean onRequest(Intent request, Intent response) { String codec = request.getStringExtra("codec"); String profile = request.getStringExtra("profile"); String level = request.getStringExtra("level"); - Integer intraRefresh = parseInteger("intraRefresh"); - Boolean useSoftwareEncoder = parseBoolean("useSoftwareEncoder"); + Integer intraRefresh = parseInteger(request,"intraRefresh"); + Boolean useSoftwareEncoder = parseBoolean(request,"useSoftwareEncoder"); Double jpegQuality = parseDouble(request, "jpegQuality"); String broadcastUri = request.getStringExtra("broadcastUri"); Integer retryCount = parseInteger(request, "retryCount"); @@ -824,7 +824,7 @@ public boolean onRequest(Intent request, Intent response) { return true; } - if (keyFrameInterval != null && keyFrameInterval <= 0) { + if (keyFrameInterval != null && keyFrameInterval < 0) { MessageUtils.setInvalidRequestParameterError(response, "keyFrameInterval. value=" + keyFrameInterval); return true; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index af4bf62b90..372cf7e223 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -789,8 +789,8 @@ public int getPreviewKeyFrameInterval() { * @param previewKeyFrameInterval キーフレームを発行する間隔(ミリ秒) */ public void setPreviewKeyFrameInterval(int previewKeyFrameInterval) { - if (previewKeyFrameInterval <= 0) { - throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative."); + if (previewKeyFrameInterval < 0) { + throw new IllegalArgumentException("previewKeyFrameInterval is negative."); } mProperty.put("preview_i_frame_interval", previewKeyFrameInterval); } From 570d2b8ebcb92756900c694cb25268409756a54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Tue, 12 Oct 2021 18:39:11 +0900 Subject: [PATCH 34/40] =?UTF-8?q?=E3=82=AB=E3=83=A1=E3=83=A9=E3=81=8C?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=95=E3=82=8C=E3=81=9F=E6=99=82=E3=81=A8?= =?UTF-8?q?=E5=89=8A=E9=99=A4=E3=81=95=E3=82=8C=E3=81=9F=E6=99=82=E3=81=AE?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mediaStreamRecording.json | 28 ++ .../fragment/AlertDialogFragment.java | 15 +- .../settings/SettingsAudioFragment.java | 3 + .../settings/SettingsEncoderFragment.java | 6 + .../settings/SettingsMainFragment.java | 21 +- .../settings/SettingsVideoFragment.java | 8 +- .../host/camera/Camera2Helper.java | 2 +- .../host/camera/CameraWrapperManager.java | 80 +++++- .../profile/HostLiveStreamingProfile.java | 8 + .../HostMediaStreamingRecordingProfile.java | 8 + .../recorder/AbstractBroadcastProvider.java | 2 - .../host/recorder/AbstractLiveStreaming.java | 1 + .../AbstractLiveStreamingProvider.java | 3 +- .../host/recorder/AbstractMediaRecorder.java | 2 + .../host/recorder/HostMediaRecorder.java | 1 - .../recorder/HostMediaRecorderManager.java | 249 ++++++++++++------ .../host/recorder/PreviewServerProvider.java | 2 - .../navigation_recorder_settings.xml | 16 +- .../app/src/main/res/values-ja/strings.xml | 3 + .../app/src/main/res/values/strings.xml | 3 + 20 files changed, 345 insertions(+), 116 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json index d54845f1f8..5372a2a9c7 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json @@ -22,6 +22,13 @@ "required": true, "type": "string" }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + }, { "name": "name", "in": "formData", @@ -99,6 +106,13 @@ "required": true, "type": "string" }, + { + "name": "target", + "in": "query", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + }, { "name": "name", "in": "query", @@ -138,6 +152,13 @@ "required": true, "type": "string" }, + { + "name": "target", + "in": "formData", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + }, { "name": "name", "in": "formData", @@ -290,6 +311,13 @@ "required": true, "type": "string" }, + { + "name": "target", + "in": "query", + "description": "ターゲット。レコーダーを識別するID。
省略された場合にはデフォルトのレコーダーを使用する。
デバイスが音声・動画の両方のレコーダーをサポートする場合、 どちらのレコーダーが使用されるかどうかはデバイスプラグイン依存とする。", + "required": false, + "type": "string" + }, { "name": "name", "in": "query", diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/fragment/AlertDialogFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/fragment/AlertDialogFragment.java index 8d802290d7..29b5ac6d5b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/fragment/AlertDialogFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/fragment/AlertDialogFragment.java @@ -67,7 +67,15 @@ public static AlertDialogFragment create(final String tag, final String title, f * @return AlertDialogFragmentのインスタンス */ public static AlertDialogFragment create(final String tag, final String title, final String message, - final String positive, final String negative) { + final String positive, final String negative) { + Bundle args = createParam(tag, title, message, positive, negative); + AlertDialogFragment dialog = new AlertDialogFragment(); + dialog.setArguments(args); + return dialog; + } + + public static Bundle createParam(final String tag, final String title, final String message, + final String positive, final String negative) { Bundle args = new Bundle(); args.putString(KEY_TAG, tag); args.putString(KEY_TITLE, title); @@ -78,10 +86,7 @@ public static AlertDialogFragment create(final String tag, final String title, f if (negative != null) { args.putString(KEY_NEGATIVE, negative); } - - AlertDialogFragment dialog = new AlertDialogFragment(); - dialog.setArguments(args); - return dialog; + return args; } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java index 4b04b9b9ed..8e07d50191 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsAudioFragment.java @@ -24,6 +24,9 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @Override public void onBindService() { mMediaRecorder = getRecorder(); + if (mMediaRecorder == null) { + return; + } setPreviewAudioSource(mMediaRecorder.getSettings()); setPreviewSampleRate(mMediaRecorder.getSettings()); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java index 8482301a10..ada9ec59bd 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsEncoderFragment.java @@ -25,12 +25,18 @@ import java.util.Comparator; import java.util.List; +import static androidx.navigation.fragment.NavHostFragment.findNavController; + public abstract class SettingsEncoderFragment extends SettingsParameterFragment { private HostMediaRecorder mMediaRecorder; @Override public void onBindService() { mMediaRecorder = getRecorder(); + if (mMediaRecorder == null) { + findNavController(this).popBackStack(); + return; + } HostMediaRecorder.EncoderSettings settings = getEncoderSetting(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java index 5178a4dba4..017e78a91d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsMainFragment.java @@ -6,6 +6,7 @@ import androidx.preference.PreferenceCategory; import org.deviceconnect.android.deviceplugin.host.R; +import org.deviceconnect.android.deviceplugin.host.activity.fragment.AlertDialogFragment; import org.deviceconnect.android.deviceplugin.host.recorder.HostMediaRecorder; import java.util.List; @@ -26,6 +27,12 @@ public void onResume() { @Override public boolean onPreferenceTreeClick(final Preference preference) { + HostMediaRecorder recorder = getRecorder(); + if (recorder == null) { + showErrorDialog(); + return false; + } + if ("recorder_settings_video".equals(preference.getKey())) { findNavController(this).navigate(R.id.action_main_to_video); } else if ("recorder_settings_audio".equals(preference.getKey())) { @@ -33,7 +40,6 @@ public boolean onPreferenceTreeClick(final Preference preference) { } else { Bundle params = new Bundle(); params.putString("encoder_id", preference.getKey()); - HostMediaRecorder recorder = getRecorder(); HostMediaRecorder.Settings settings = recorder.getSettings(); HostMediaRecorder.EncoderSettings s = settings.getEncoderSetting(preference.getKey()); if (s != null) { @@ -61,12 +67,25 @@ public void onBindService() { addEncoderList(); } + private void showErrorDialog() { + Bundle args = AlertDialogFragment.createParam("error-dialog", + getString(R.string.host_recorder_settings_error_not_found_camera_title), + getString(R.string.host_recorder_settings_error_not_found_camera_message), + getString(R.string.host_recorder_settings_error_not_found_camera_btn), + null); + findNavController(this).navigate(R.id.action_open_error_dialog, args); + } + private boolean isNotExistPreference(String key) { return findPreference(key) == null; } private void addEncoderList() { HostMediaRecorder recorder = getRecorder(); + if (recorder == null) { + return; + } + HostMediaRecorder.Settings settings = recorder.getSettings(); List encoderList = settings.getEncoderIdList(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java index 174e9ce0cf..e6b5940012 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/activity/recorder/settings/SettingsVideoFragment.java @@ -23,18 +23,24 @@ import java.util.Comparator; import java.util.List; +import static androidx.navigation.fragment.NavHostFragment.findNavController; + public class SettingsVideoFragment extends SettingsParameterFragment { private HostMediaRecorder mMediaRecorder; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName(getRecorderId().replaceAll("/", "_")); + getPreferenceManager().setSharedPreferencesName(getRecorderId()); setPreferencesFromResource(R.xml.settings_host_recorder_video, rootKey); } @Override public void onBindService() { mMediaRecorder = getRecorder(); + if (mMediaRecorder == null) { + findNavController(this).popBackStack(); + return; + } HostMediaRecorder.Settings settings = mMediaRecorder.getSettings(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/Camera2Helper.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/Camera2Helper.java index 75daa0d193..f90cb8d687 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/Camera2Helper.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/Camera2Helper.java @@ -49,7 +49,7 @@ public static int getSensorOrientation(final CameraManager cameraManager, final CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); Integer sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); return sensorOrientation == null ? 0 : sensorOrientation; - } catch (CameraAccessException e) { + } catch (Exception e) { return 0; } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/CameraWrapperManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/CameraWrapperManager.java index ae49eaa14d..97a714da3a 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/CameraWrapperManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/camera/CameraWrapperManager.java @@ -14,6 +14,7 @@ import org.deviceconnect.android.deviceplugin.host.BuildConfig; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -31,27 +32,17 @@ public class CameraWrapperManager { * カメラ操作クラスの一覧. */ private final Map mCameras = new LinkedHashMap<>(); + private final Context mContext; /** * コンストラクタ. * @param context コンテキスト */ public CameraWrapperManager(final Context context) { + mContext = context; try { - CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); - for (String cameraId : cameraManager.getCameraIdList()) { - CameraWrapper camera = null; - try { - camera = new CameraWrapper(context, cameraId); - mCameras.put(cameraId, camera); - } catch (Exception e) { - // ignore. - if (BuildConfig.DEBUG) { - Log.w("CameraWrapperManager", "Failed to create a CameraWrapper.", e); - } - } - } - } catch (CameraAccessException e) { + loadCamera(); + } catch (Exception e) { // No camera is available now. } } @@ -64,6 +55,67 @@ public synchronized List getCameraList() { return new ArrayList<>(mCameras.values()); } + /** + * 指定された ID のカメラを取得する. + * + * @param cameraId カメラの識別子 + * @return CameraWrapper、存在しない場合は null + */ + public synchronized CameraWrapper getCameraById(String cameraId) { + return mCameras.get(cameraId); + } + + /** + * ロストしたカメラを削除します. + * + * @throws CameraAccessException カメラへのアクセス失敗した場合に発生 + */ + private void removeLostCamera() throws CameraAccessException { + CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); + List cameraIdList = Arrays.asList(cameraManager.getCameraIdList()); + for (String key : mCameras.keySet()) { + if (cameraIdList.contains(key)) { + continue; + } + CameraWrapper cameraWrapper = mCameras.remove(key); + if (cameraWrapper != null) { + cameraWrapper.destroy(); + } + } + } + + /** + * カメラを読み込みます. + * + * @throws CameraAccessException カメラへのアクセス失敗した場合に発生 + */ + private void loadCamera() throws CameraAccessException { + CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); + for (String cameraId : cameraManager.getCameraIdList()) { + if (mCameras.containsKey(cameraId)) { + continue; + } + + try { + mCameras.put(cameraId, new CameraWrapper(mContext, cameraId)); + } catch (Exception e) { + // ignore. + } + } + } + + /** + * カメラを再読み込みする. + */ + public synchronized void reload() { + try { + removeLostCamera(); + loadCamera(); + } catch (Exception e) { + // No camera is available now. + } + } + /** * カメラ操作クラスを全て破棄する. * アプリケーションを終了するときにのみ実行すること. diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java index 69763f2bb5..00a89c7e13 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostLiveStreamingProfile.java @@ -110,6 +110,14 @@ public void onRecordingResume(HostMediaRecorder recorder) { public void onRecordingStopped(HostMediaRecorder recorder, String fileName) { } + @Override + public void onFoundRecorder(HostMediaRecorder recorder) { + } + + @Override + public void onLostRecorder(HostMediaRecorder recorder) { + } + @Override public void onError(HostMediaRecorder recorder, Exception e) { } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 393e50298f..0dbd55f9c9 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -120,6 +120,14 @@ public void onRecordingStopped(HostMediaRecorder recorder, String fileName) { "/" + fileName, recorder.getMimeType(), null); } + @Override + public void onFoundRecorder(HostMediaRecorder recorder) { + } + + @Override + public void onLostRecorder(HostMediaRecorder recorder) { + } + @Override public void onError(HostMediaRecorder recorder, Exception e) { } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java index 7432a307bf..b4e2053344 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractBroadcastProvider.java @@ -15,8 +15,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; public abstract class AbstractBroadcastProvider extends AbstractLiveStreamingProvider implements BroadcasterProvider { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java index 7d540e5d0d..2cc34e1fb6 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java @@ -102,6 +102,7 @@ public void onConfigChange() { @Override public void release() { + stop(); stopMovingRectThread(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java index f364b5b502..ca5134685e 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java @@ -50,7 +50,6 @@ public void addLiveStreaming(LiveStreaming streaming) { public void removeLiveStreaming(String encoderId) { for (LiveStreaming streaming : mLiveStreamingList) { if (streaming.getId().equals(encoderId)) { - streaming.stop(); streaming.release(); mLiveStreamingList.remove(streaming); return; @@ -165,6 +164,8 @@ public void setOnEventListener(OnEventListener listener) { @Override public void release() { + stop(); + for (LiveStreaming streaming : getLiveStreamingList()) { streaming.release(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java index 3423c3d57a..a01331ab6d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java @@ -160,6 +160,8 @@ public void destroy() { previewProvider.release(); } + stopRecordingInternal(null); + mRequestHandler.getLooper().quit(); } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index 372cf7e223..10b7a06166 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -9,7 +9,6 @@ import android.content.Context; import android.graphics.Rect; import android.os.Build; -import android.util.Log; import android.util.Range; import android.util.Size; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java index 023d28537a..600f27734d 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorderManager.java @@ -118,7 +118,7 @@ public HostMediaRecorderManager(final DevicePluginContext pluginContext, final F mHostDevicePluginContext = pluginContext; mMediaProjectionProvider = new MediaProjectionProvider(pluginContext.getContext()); mFileManager = fileManager; - initRecorders(); + createRecorders(); } private Context getContext() { @@ -126,13 +126,13 @@ private Context getContext() { } /** - * レコーダの初期化処理を行います. + * 使用するレコーダの一覧を作成します. * *

* ここで使用できるレコーダの登録を行います。 *

*/ - private void initRecorders() { + private void createRecorders() { if (checkCameraHardware(getContext())) { try { mCameraWrapperManager = new CameraWrapperManager(getContext()); @@ -157,80 +157,6 @@ private void initRecorders() { // ignore. } } - - for (HostMediaRecorder recorder : mRecorders) { - recorder.setOnEventListener(new HostMediaRecorder.OnEventListener() { - @Override - public void onMuteChanged(boolean mute) { - postOnMuteChanged(recorder, mute); - } - - @Override - public void onConfigChanged() { - postOnConfigChanged(recorder); - } - - @Override - public void onPreviewStarted(List servers) { - postOnPreviewStarted(recorder, servers); - } - - @Override - public void onPreviewStopped() { - postOnPreviewStopped(recorder); - } - - @Override - public void onPreviewError(Exception e) { - postOnPreviewError(recorder, e); - } - - @Override - public void onBroadcasterStarted(List broadcasters) { - postOnBroadcasterStarted(recorder, broadcasters); - } - - @Override - public void onBroadcasterStopped() { - postOnBroadcasterStopped(recorder); - } - - @Override - public void onBroadcasterError(LiveStreaming broadcaster, Exception e) { - postOnBroadcasterError(recorder, broadcaster, e); - } - - @Override - public void onError(Exception e) { - postOnError(recorder, e); - } - - @Override - public void onTakePhoto(String uri, String filePath, String mimeType) { - postOnTakePhoto(recorder, uri, filePath, mimeType); - } - - @Override - public void onRecordingStarted(String fileName) { - postOnRecordingStarted(recorder, fileName); - } - - @Override - public void onRecordingPause() { - postOnRecordingPause(recorder); - } - - @Override - public void onRecordingResume() { - postOnRecordingResume(recorder); - } - - @Override - public void onRecordingStopped(String fileName) { - postOnRecordingStopped(recorder, fileName); - } - }); - } } /** @@ -270,12 +196,92 @@ private void createCameraRecorders(final CameraWrapperManager cameraMgr, final F } } + /** + * レコーダの初期化処理を行います. + * + * @param recorder 初期化するレコーダ + */ + private void initRecorder(HostMediaRecorder recorder) { + recorder.setOnEventListener(new HostMediaRecorder.OnEventListener() { + @Override + public void onMuteChanged(boolean mute) { + postOnMuteChanged(recorder, mute); + } + + @Override + public void onConfigChanged() { + postOnConfigChanged(recorder); + } + + @Override + public void onPreviewStarted(List servers) { + postOnPreviewStarted(recorder, servers); + } + + @Override + public void onPreviewStopped() { + postOnPreviewStopped(recorder); + } + + @Override + public void onPreviewError(Exception e) { + postOnPreviewError(recorder, e); + } + + @Override + public void onBroadcasterStarted(List broadcasters) { + postOnBroadcasterStarted(recorder, broadcasters); + } + + @Override + public void onBroadcasterStopped() { + postOnBroadcasterStopped(recorder); + } + + @Override + public void onBroadcasterError(LiveStreaming broadcaster, Exception e) { + postOnBroadcasterError(recorder, broadcaster, e); + } + + @Override + public void onError(Exception e) { + postOnError(recorder, e); + } + + @Override + public void onTakePhoto(String uri, String filePath, String mimeType) { + postOnTakePhoto(recorder, uri, filePath, mimeType); + } + + @Override + public void onRecordingStarted(String fileName) { + postOnRecordingStarted(recorder, fileName); + } + + @Override + public void onRecordingPause() { + postOnRecordingPause(recorder); + } + + @Override + public void onRecordingResume() { + postOnRecordingResume(recorder); + } + + @Override + public void onRecordingStopped(String fileName) { + postOnRecordingStopped(recorder, fileName); + } + }); + recorder.initialize(); + } + /** * 初期化処理を行います. */ public void initialize() { for (HostMediaRecorder recorder : getRecorders()) { - recorder.initialize(); + initRecorder(recorder); } onDisplayRotationChanged(getContext()); @@ -318,14 +324,70 @@ public void destroy() { mCameraWrapperManager.destroy(); } + /** + * レコーダが存在するか確認します. + * + * @param cameraId カメラID + * @return 存在する場合は true、それ以外は false + */ + private boolean existRecorder(String cameraId) { + for (HostMediaRecorder recorder : mRecorders) { + if (recorder instanceof Camera2Recorder) { + String id = ((Camera2Recorder) recorder).getCameraWrapper().getId(); + if (cameraId.equals(id)) { + return true; + } + } + } + return false; + } + + /** + * ロストしたカメラを一旦レコーダから削除します. + */ + private void removeLostCamera() { + List removeList = new ArrayList<>(); + for (HostMediaRecorder recorder : mRecorders) { + if (recorder instanceof Camera2Recorder) { + String cameraId = ((Camera2Recorder) recorder).getCameraWrapper().getId(); + if (mCameraWrapperManager.getCameraById(cameraId) == null) { + removeList.add((Camera2Recorder) recorder); + } + } + } + + for (Camera2Recorder recorder : removeList) { + recorder.destroy(); + mRecorders.remove(recorder); + postOnLostRecorder(recorder); + } + } + /** * 端末が対応しているレコーダを読み込みし直す */ public void reloadRecorders() { - destroy(); - mRecorders.clear(); - initRecorders(); + try { + mCameraWrapperManager.reload(); + + removeLostCamera(); + + for (CameraWrapper camera : mCameraWrapperManager.getCameraList()) { + if (existRecorder(camera.getId())) { + continue; + } + + Camera2Recorder recorder = new Camera2Recorder(getContext(), camera, mFileManager, mMediaProjectionProvider); + initRecorder(recorder); + mRecorders.add(recorder); + + postOnFoundRecorder(recorder); + } + } catch (Exception e) { + // ignore. + } } + /** * 指定されたレコーダが使用できるか確認します. * @@ -418,6 +480,8 @@ public List getCameraRecorders() { * @return レコーダ */ public HostMediaRecorder getRecorder(final String id) { + reloadRecorders(); + if (mRecorders.size() == 0) { return null; } @@ -658,6 +722,26 @@ private void postOnRecordingStopped(HostMediaRecorder recorder, String fileName) } } + private void postOnFoundRecorder(HostMediaRecorder recorder) { + for (OnEventListener l : mOnEventListeners) { + try { + l.onFoundRecorder(recorder); + } catch (Exception e) { + // ignore. + } + } + } + + private void postOnLostRecorder(HostMediaRecorder recorder) { + for (OnEventListener l : mOnEventListeners) { + try { + l.onLostRecorder(recorder); + } catch (Exception e) { + // ignore. + } + } + } + private void postOnError(HostMediaRecorder recorder, Exception e) { for (OnEventListener l : mOnEventListeners) { l.onError(recorder, e); @@ -683,6 +767,9 @@ public interface OnEventListener { void onRecordingResume(HostMediaRecorder recorder); void onRecordingStopped(HostMediaRecorder recorder, String fileName); + void onFoundRecorder(HostMediaRecorder recorder); + void onLostRecorder(HostMediaRecorder recorder); + void onError(HostMediaRecorder recorder, Exception e); } } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java index 555026f5fa..a37d3133df 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/PreviewServerProvider.java @@ -6,8 +6,6 @@ */ package org.deviceconnect.android.deviceplugin.host.recorder; -import java.util.List; - public interface PreviewServerProvider extends LiveStreamingProvider { } diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml index cd4a992175..348aca366e 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/navigation/navigation_recorder_settings.xml @@ -64,15 +64,23 @@ app:popEnterAnim="@anim/nav_slide_pop_enter_anim" app:popExitAnim="@anim/nav_slide_pop_exit_anim" /> + +
+ + + - @@ -97,12 +105,6 @@ - - - - diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml index dc12202571..214279677a 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values-ja/strings.xml @@ -85,6 +85,9 @@ RTMP ポート設定 サーバ設定 + 接続エラー + カメラが接続されていません。 + はい 静止画解像度 diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml index a0dc2f71aa..864baf67e5 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/res/values/strings.xml @@ -84,6 +84,9 @@ RTMP Port Settings Server Settings + Error + The camera is not connected. + OK Still Image Resolution Preview Resolution From 91578b6b78378fde35af1c555f2d9c43c1889f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Wed, 20 Oct 2021 16:46:31 +0900 Subject: [PATCH 35/40] =?UTF-8?q?=E3=83=93=E3=83=83=E3=83=88=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=8C=E5=A4=89=E6=9B=B4=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AB=E3=81=AF=E3=80=81MediaCode?= =?UTF-8?q?c=20=E3=81=AE=E5=86=8D=E8=B5=B7=E5=8B=95=E3=81=AA=E3=81=97?= =?UTF-8?q?=E3=81=A7=E5=80=A4=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HostMediaStreamingRecordingProfile.java | 19 ++++++++++++++-- .../host/recorder/AbstractLiveStreaming.java | 18 +++++++++++++-- .../AbstractLiveStreamingProvider.java | 22 ++++++++++++++----- .../recorder/AbstractMJPEGPreviewServer.java | 12 ++++++++++ .../host/recorder/AbstractMediaRecorder.java | 21 ++++++++++++++++++ .../host/recorder/HostMediaRecorder.java | 10 +++++++++ .../host/recorder/LiveStreaming.java | 14 ++++++++++++ .../host/recorder/LiveStreamingProvider.java | 16 ++++++++++---- .../gles/SurfaceTextureRenderer.java | 2 +- .../streaming/video/VideoEncoder.java | 12 ++++++++-- 10 files changed, 129 insertions(+), 17 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 0dbd55f9c9..7afb2c19e9 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -874,6 +874,7 @@ public boolean onRequest(Intent request, Intent response) { @Override public void onAllowed() { HostMediaRecorder.Settings settings = recorder.getSettings(); + boolean isChangeConfig = false; for (String encoderId : encoderIdList) { HostMediaRecorder.EncoderSettings encoderSettings = settings.getEncoderSetting(encoderId); @@ -912,24 +913,29 @@ public void onAllowed() { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setPreviewSize(new Size(width, height)); } + isChangeConfig = true; } if (frameRate != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setPreviewMaxFrameRate(frameRate); } + isChangeConfig = true; } if (bitRate != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setPreviewBitRate(bitRate * 1024); } + + recorder.requestBitRate(); } if (keyFrameInterval != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setPreviewKeyFrameInterval(keyFrameInterval); } + isChangeConfig = true; } if (codec != null) { @@ -937,36 +943,42 @@ public void onAllowed() { settings.getEncoderSetting(encoderId).setPreviewEncoder(codec); settings.getEncoderSetting(encoderId).setProfileLevel(null); } + isChangeConfig = true; } if (profileLevel != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setProfileLevel(profileLevel); } + isChangeConfig = true; } if (intraRefresh != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setIntraRefresh(intraRefresh); } + isChangeConfig = true; } if (useSoftwareEncoder != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setUseSoftwareEncoder(useSoftwareEncoder); } + isChangeConfig = true; } if (jpegQuality != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setPreviewQuality((int) (jpegQuality * 100)); } + recorder.requestJpegQuality(); } if (broadcastUri != null) { for (String encoderId : encoderIdList) { settings.getEncoderSetting(encoderId).setBroadcastURI(broadcastUri); } + isChangeConfig = true; } if (retryCount != null) { @@ -982,7 +994,9 @@ public void onAllowed() { } try { - recorder.onConfigChange(); + if (isChangeConfig) { + recorder.onConfigChange(); + } setResult(response, DConnectMessage.RESULT_OK); } catch (Exception e) { MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config."); @@ -1563,7 +1577,7 @@ private void setOptions(final Intent request, final Intent response) { } } - isChangeConfig = true; + recorder.requestBitRate(); } if (previewKeyFrameInterval != null) { @@ -1618,6 +1632,7 @@ private void setOptions(final Intent request, final Intent response) { encoderSettings.setPreviewQuality((int) (previewJpegQuality * 100)); } } + recorder.requestJpegQuality(); } if (previewClipReset != null && previewClipReset) { diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java index 2cc34e1fb6..9bb4562943 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreaming.java @@ -125,12 +125,26 @@ public boolean isMuted() { public boolean requestSyncFrame() { VideoEncoder videoEncoder = getVideoEncoder(); if (videoEncoder != null) { - videoEncoder.requestSyncKeyFrame(); - return true; + return videoEncoder.requestSyncKeyFrame(); } return false; } + @Override + public boolean requestBitRate() { + VideoEncoder videoEncoder = getVideoEncoder(); + if (videoEncoder != null) { + videoEncoder.getVideoQuality().setBitRate(getEncoderSettings().getPreviewBitRate()); + return videoEncoder.requestBitRate(); + } + return false; + } + + @Override + public boolean requestJpegQuality() { + return false; + } + @Override public long getBPS() { return 0; diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java index ca5134685e..9a5877b0ec 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractLiveStreamingProvider.java @@ -133,14 +133,24 @@ public void stop() { } @Override - public List requestSyncFrame() { - List result = new ArrayList<>(); + public void requestSyncFrame() { for (LiveStreaming streaming : getLiveStreamingList()) { - if (streaming.requestSyncFrame()) { - result.add(streaming); - } + streaming.requestSyncFrame(); + } + } + + @Override + public void requestBitRate() { + for (LiveStreaming streaming : getLiveStreamingList()) { + streaming.requestBitRate(); + } + } + + @Override + public void requestJpegQuality() { + for (LiveStreaming streaming : getLiveStreamingList()) { + streaming.requestJpegQuality(); } - return result; } @Override diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java index 899d5972bc..b86d76adfe 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMJPEGPreviewServer.java @@ -100,6 +100,18 @@ public long getBPS() { return mMJPEGServer != null ? mMJPEGServer.getBPS() : 0; } + @Override + public boolean requestJpegQuality() { + if (mMJPEGServer != null) { + MJPEGEncoder encoder = mMJPEGServer.getMJPEGEncoder(); + if (encoder != null) { + encoder.getMJPEGQuality().setQuality(getEncoderSettings().getPreviewQuality()); + return true; + } + } + return false; + } + @Override public void onConfigChange() { setEncoderQuality(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java index a01331ab6d..59b031699b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/AbstractMediaRecorder.java @@ -269,6 +269,27 @@ public void requestKeyFrame() { } } + @Override + public void requestBitRate() { + PreviewServerProvider previewProvider = getServerProvider(); + if (previewProvider != null) { + previewProvider.requestBitRate(); + } + + BroadcasterProvider broadcasterProvider = getBroadcasterProvider(); + if (broadcasterProvider != null) { + broadcasterProvider.requestBitRate(); + } + } + + @Override + public void requestJpegQuality() { + PreviewServerProvider previewProvider = getServerProvider(); + if (previewProvider != null) { + previewProvider.requestJpegQuality(); + } + } + @Override public void setMute(boolean mute) { Settings settings = getSettings(); diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java index 10b7a06166..28ccf450da 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/HostMediaRecorder.java @@ -188,6 +188,16 @@ public interface HostMediaRecorder extends HostDevicePhotoRecorder, HostDeviceSt */ void requestKeyFrame(); + /** + * ビットレートの更新を要求します. + */ + void requestBitRate(); + + /** + * JPEG 品質の更新を要求します. + */ + void requestJpegQuality(); + /** * ミュート設定を行います. * diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java index c56d2db5a2..858dbd5d92 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreaming.java @@ -66,6 +66,20 @@ public interface LiveStreaming { */ boolean requestSyncFrame(); + /** + * 映像のエンコーダーにビットレートの更新を要求する. + * + * @return 更新を受け付けた場合はtrue, そうでない場合はfalse + */ + boolean requestBitRate(); + + /** + * JPEG 品質の更新を要求する. + * + * @return 更新を受け付けた場合はtrue, そうでない場合はfalse + */ + boolean requestJpegQuality(); + /** * プレビューサーバから配信したデータの BPS を取得します. * diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java index 1c3f196973..55f2bc5c3b 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/recorder/LiveStreamingProvider.java @@ -61,11 +61,19 @@ public interface LiveStreamingProvider { void stop(); /** - * 全てのサーバの映像のエンコーダーに対して sync frame の即時生成を要求する. - * - * @return 実際に即時生成を受け付けたサーバのリスト + * 映像のエンコーダーに対して sync frame の即時生成を要求する. + */ + void requestSyncFrame(); + + /** + * 映像のエンコーダーに対してビットレートの更新を要求する。 + */ + void requestBitRate(); + + /** + * 映像のエンコーダーに対して JPEG の品質の更新を要求する。 */ - List requestSyncFrame(); + void requestJpegQuality(); /** * 設定が変更されたことを通知します. diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java index 6e1b6d5735..84eaaeb9cc 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/gles/SurfaceTextureRenderer.java @@ -77,7 +77,7 @@ public class SurfaceTextureRenderer { * コンストラクタ. * @param inverse テクスチャの反転フラグ */ - SurfaceTextureRenderer(boolean inverse) { + public SurfaceTextureRenderer(boolean inverse) { mInverse = inverse; mTriangleVertices = ByteBuffer.allocateDirect( TRIANGLE_VERTICES_DATA.length * FLOAT_SIZE_BYTES) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java index ee779cf15b..7e40b741e8 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/video/VideoEncoder.java @@ -63,17 +63,21 @@ protected void release() { /** * キーフレームを要求します. + * + * @return 要求を受け付けた場合はtrue、それ以外はfalse */ - public void requestSyncKeyFrame() { + public boolean requestSyncKeyFrame() { if (mMediaCodec != null) { Bundle b = new Bundle(); b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); try { mMediaCodec.setParameters(b); + return true; } catch (Exception e) { // ignore. } } + return false; } /** @@ -83,17 +87,21 @@ public void requestSyncKeyFrame() { * エンコード中にビットレートを変更したい場合に指定します。 * {@link VideoQuality#getBitRate()} で取得できるビットレートを再設定します。 *

+ * + * @return 要求を受け付けた場合はtrue、それ以外はfalse */ - public void requestBitRate() { + public boolean requestBitRate() { if (mMediaCodec != null) { Bundle b = new Bundle(); b.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, getVideoQuality().getBitRate()); try { mMediaCodec.setParameters(b); + return true; } catch (Exception e) { // ignore. } } + return false; } /** From a75aca3594ddc8740f1573b4952860312b453197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Thu, 21 Oct 2021 17:04:04 +0900 Subject: [PATCH 36/40] =?UTF-8?q?=E3=82=B9=E3=83=9A=E3=83=AB=E3=83=9F?= =?UTF-8?q?=E3=82=B9=E3=82=92=E4=BF=AE=E6=AD=A3=20=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE=E3=83=91=E3=83=A9?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=BF=E3=81=AB=E4=B8=8D=E8=B6=B3=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E6=83=85=E5=A0=B1=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mediaStreamRecording.json | 22 +++++++++++++++++-- .../HostMediaStreamingRecordingProfile.java | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json index 5372a2a9c7..5d85065860 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/assets/org_deviceconnect_android_deviceplugin_host/api/mediaStreamRecording.json @@ -198,7 +198,7 @@ "format": "int64" }, { - "name": "bitrate", + "name": "bitRate", "in": "formData", "description": "配信する映像のビットレートを指定します。", "required": false, @@ -937,7 +937,25 @@ "type": "number", "minimum": 0, "exclusiveMinimum": true - }, + }, + { + "name": "previewBitRate", + "in": "formData", + "description": "プレビューのビットレート。単位はKbps。", + "required": false, + "type": "integer" + }, + { + "name": "previewJpegQuality", + "in": "formData", + "description": "JPEGの品質。範囲は0.0から1.0。MotionJPEGのみサポート。", + "required": false, + "type": "number", + "minimum": 0, + "exclusiveMinimum": false, + "maximum": 1, + "exclusiveMaximum": false + }, { "name": "mimeType", "in": "formData", diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java index 7afb2c19e9..17c8a6a422 100755 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/src/main/java/org/deviceconnect/android/deviceplugin/host/profile/HostMediaStreamingRecordingProfile.java @@ -1802,7 +1802,7 @@ private Bundle createVideoEncoder(HostMediaRecorder.EncoderSettings s) { bundle.putString("mimeType", s.getMimeType().getValue()); bundle.putInt("width", s.getPreviewSize().getWidth()); bundle.putInt("height", s.getPreviewSize().getHeight()); - bundle.putInt("bitrate", s.getPreviewBitRate() / 1024); + bundle.putInt("bitRate", s.getPreviewBitRate() / 1024); if ("video/x-mjpeg".equals(s.getMimeType().getValue())) { bundle.putFloat("jpegQuality", s.getPreviewQuality() / 100.0f); From b0614779ca813275352f781266d29366e2a9e767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9E=97=E4=BC=B8=E9=83=8E?= Date: Sun, 24 Oct 2021 16:57:11 +0900 Subject: [PATCH 37/40] =?UTF-8?q?=E9=9F=B3=E5=A3=B0=E3=81=AE=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=88=E7=95=AA=E5=8F=B7=E3=81=AE=E3=83=87=E3=83=95?= =?UTF-8?q?=E3=82=A9=E3=83=AB=E3=83=88=E5=80=A4=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libmedia/streaming/rtsp/session/audio/AudioStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtsp/session/audio/AudioStream.java b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtsp/session/audio/AudioStream.java index 6b26af3036..9d693afa66 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtsp/session/audio/AudioStream.java +++ b/dConnectSDK/dConnectLibStreaming/libmedia/src/main/java/org/deviceconnect/android/libmedia/streaming/rtsp/session/audio/AudioStream.java @@ -7,7 +7,7 @@ public abstract class AudioStream extends MediaStream { /** * 音声を配信するポート番号を定義します. */ - private static final int AUDIO_PORT = 5006; + private static final int AUDIO_PORT = 5004; public AudioStream() { setDestinationPort(AUDIO_PORT); From 39263e8a56b791644b920f2073f1f46f87b6c752 Mon Sep 17 00:00:00 2001 From: TakayukiHoshi1984 Date: Mon, 25 Oct 2021 14:58:32 +0900 Subject: [PATCH 38/40] =?UTF-8?q?libmedia=E3=81=AE=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=921.3.0=E3=81=AB=E3=82=A2?= =?UTF-8?q?=E3=83=83=E3=83=97=E3=83=87=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dConnectSDK/dConnectLibStreaming/libmedia/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dConnectSDK/dConnectLibStreaming/libmedia/build.gradle b/dConnectSDK/dConnectLibStreaming/libmedia/build.gradle index 7aa10cfb21..4b08475163 100644 --- a/dConnectSDK/dConnectLibStreaming/libmedia/build.gradle +++ b/dConnectSDK/dConnectLibStreaming/libmedia/build.gradle @@ -8,7 +8,7 @@ if (githubPropertiesFile.exists()) { } def getVersionName = { -> - return "1.2.2" + return "1.3.0" } def getArtificatId = { -> From fb99a651e7f9ea8ea35cda2eeaf0a18c7ebf5c13 Mon Sep 17 00:00:00 2001 From: TakayukiHoshi1984 Date: Mon, 25 Oct 2021 14:59:05 +0900 Subject: [PATCH 39/40] =?UTF-8?q?libsrt=E3=81=AE=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=921.3.0=E3=81=AB=E3=82=A2?= =?UTF-8?q?=E3=83=83=E3=83=97=E3=83=87=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dConnectSDK/dConnectLibStreaming/libsrt/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dConnectSDK/dConnectLibStreaming/libsrt/build.gradle b/dConnectSDK/dConnectLibStreaming/libsrt/build.gradle index c668d06bf6..7792a80219 100644 --- a/dConnectSDK/dConnectLibStreaming/libsrt/build.gradle +++ b/dConnectSDK/dConnectLibStreaming/libsrt/build.gradle @@ -6,7 +6,7 @@ if (githubPropertiesFile.exists()) { githubProperties.load(new FileInputStream(githubPropertiesFile)) } def getVersionName = { -> - return "1.2.3" // Replace with version Name + return "1.3.0" // Replace with version Name } def getArtificatId = { -> @@ -93,7 +93,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'org.deviceconnect:libmedia:1.2.2' + implementation 'org.deviceconnect:libmedia:1.3.0' // implementation project(':libmedia') testImplementation 'junit:junit:4.13.2' From a65bfc9828e377275596669032a4b97359536a64 Mon Sep 17 00:00:00 2001 From: TakayukiHoshi1984 Date: Mon, 25 Oct 2021 14:59:57 +0900 Subject: [PATCH 40/40] =?UTF-8?q?libmedia,libsrt=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=83=83=E3=83=97=E3=83=87=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dConnectDeviceHost/app/build.gradle | 6 ++-- .../dConnectDeviceUVC/app/build.gradle | 4 +-- .../app/src/main/AndroidManifest.xml | 15 +++----- .../dConnectDeviceUVC/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- dConnectManager/dConnectManager/build.gradle | 2 +- .../dconnect-manager-app-things/build.gradle | 2 +- .../dconnect-manager-app/build.gradle | 4 +-- .../android/manager/DConnectService.java | 36 +------------------ .../gradle/wrapper/gradle-wrapper.properties | 2 +- 10 files changed, 18 insertions(+), 57 deletions(-) diff --git a/dConnectDevicePlugin/dConnectDeviceHost/app/build.gradle b/dConnectDevicePlugin/dConnectDeviceHost/app/build.gradle index 521ff4afb1..ad665e41e6 100644 --- a/dConnectDevicePlugin/dConnectDeviceHost/app/build.gradle +++ b/dConnectDevicePlugin/dConnectDeviceHost/app/build.gradle @@ -33,7 +33,7 @@ android { buildConfigField "String", "DEMO_DIR", "\"demo\"" buildConfigField "String", "DEMO_ZIP", "\"" + DEMO_ZIP_NAME + "\"" buildConfigField "long", "STATS_INTERVAL", "5000L" - } + } signingConfigs { releaseConfig { @@ -103,8 +103,8 @@ dependencies { implementation 'com.github.pedroSG94.rtmp-rtsp-stream-client-java:rtplibrary:1.9.7' implementation 'org.deviceconnect:dconnect-device-plugin-sdk:2.8.6' implementation 'org.deviceconnect:dconnect-demo-lib:1.0.1' - implementation 'org.deviceconnect:libmedia:1.2.2' - implementation 'org.deviceconnect:libsrt:1.2.2' + implementation 'org.deviceconnect:libmedia:1.3.0' + implementation 'org.deviceconnect:libsrt:1.3.0' // implementation project(':libmedia') // implementation project(':libsrt') } diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/build.gradle b/dConnectDevicePlugin/dConnectDeviceUVC/app/build.gradle index 646eae2b8e..2d77707789 100644 --- a/dConnectDevicePlugin/dConnectDeviceUVC/app/build.gradle +++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/build.gradle @@ -95,7 +95,7 @@ dependencies { implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation 'com.github.pedroSG94.rtmp-rtsp-stream-client-java:rtplibrary:1.9.7' implementation 'org.deviceconnect:dconnect-device-plugin-sdk:2.8.6' - implementation 'org.deviceconnect:libmedia:1.2.2' - implementation 'org.deviceconnect:libsrt:1.2.2' + implementation 'org.deviceconnect:libmedia:1.3.0' + implementation 'org.deviceconnect:libsrt:1.3.0' implementation project(':libuvc') } diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml index 8706604fb7..289e4e954d 100644 --- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml +++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/AndroidManifest.xml @@ -8,7 +8,11 @@ - + - - - - - diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle b/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle index 6a0e51ad96..b67c2de44c 100644 --- a/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle +++ b/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'com.android.tools.build:gradle:4.1.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/gradle/wrapper/gradle-wrapper.properties b/dConnectDevicePlugin/dConnectDeviceUVC/gradle/wrapper/gradle-wrapper.properties index 7a872b986b..09334b7fd7 100644 --- a/dConnectDevicePlugin/dConnectDeviceUVC/gradle/wrapper/gradle-wrapper.properties +++ b/dConnectDevicePlugin/dConnectDeviceUVC/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/dConnectManager/dConnectManager/build.gradle b/dConnectManager/dConnectManager/build.gradle index aa10319315..dbc42e486e 100644 --- a/dConnectManager/dConnectManager/build.gradle +++ b/dConnectManager/dConnectManager/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'com.android.tools.build:gradle:4.1.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/dConnectManager/dConnectManager/dconnect-manager-app-things/build.gradle b/dConnectManager/dConnectManager/dconnect-manager-app-things/build.gradle index 2327ffcc79..1e7c5f616e 100644 --- a/dConnectManager/dConnectManager/dconnect-manager-app-things/build.gradle +++ b/dConnectManager/dConnectManager/dconnect-manager-app-things/build.gradle @@ -38,7 +38,7 @@ android { repositories { maven { name = "DeviceConnect-Android" - url = uri("https://maven.pkg.github.com/DeviceConnect/DeviceConnect-Android") + url = uri("https://maven.pkg.github.com/TakayukiHoshi1984/DeviceConnect-Android") credentials { username = System.getenv("GPR_USER") ?: githubProperties['gpr.usr'] diff --git a/dConnectManager/dConnectManager/dconnect-manager-app/build.gradle b/dConnectManager/dConnectManager/dconnect-manager-app/build.gradle index c26c28191d..671271bfc5 100644 --- a/dConnectManager/dConnectManager/dconnect-manager-app/build.gradle +++ b/dConnectManager/dConnectManager/dconnect-manager-app/build.gradle @@ -82,7 +82,7 @@ android { repositories { maven { name = "DeviceConnect-Android" - url = uri("https://maven.pkg.github.com/DeviceConnect/DeviceConnect-Android") + url = uri("https://maven.pkg.github.com/TakayukiHoshi1984/DeviceConnect-Android") credentials { username = System.getenv("GPR_USER") ?: githubProperties['gpr.usr'] @@ -116,7 +116,7 @@ dependencies { } android.testVariants.all { variant -> - task("generateJavadocForManagerJUnit", type: Javadoc, overwrite: true) { + tasks.register("generate${variant.name.capitalize()}JavadocForManagerJUnit", Javadoc) { title = "Android Device Connect Manager Unit Test" description = "Generates Javadoc for JUnit" source = android.sourceSets.androidTest.java.sourceFiles diff --git a/dConnectManager/dConnectManager/dconnect-manager-app/src/main/java/org/deviceconnect/android/manager/DConnectService.java b/dConnectManager/dConnectManager/dconnect-manager-app/src/main/java/org/deviceconnect/android/manager/DConnectService.java index 0cde35690b..b69f379f7d 100644 --- a/dConnectManager/dConnectManager/dconnect-manager-app/src/main/java/org/deviceconnect/android/manager/DConnectService.java +++ b/dConnectManager/dConnectManager/dconnect-manager-app/src/main/java/org/deviceconnect/android/manager/DConnectService.java @@ -304,41 +304,7 @@ public void onError(final Exception e) { } } } - - /** - * Hostプラグインを追加します. - */ - private void addDevicePlugin() { - String packageName = getPackageName(); - String className = HostDevicePlugin.class.getName(); - - DevicePlugin plugin; - try { - plugin = new DevicePlugin.Builder(this) - .setClassName(className) - .setPackageName(packageName) - .setConnectionType(ConnectionType.DIRECT) - .setDeviceName(getString(R.string.app_name_host)) - .setPluginIconId(R.drawable.dconnect_icon) - .setVersionName(org.deviceconnect.android.deviceplugin.host.BuildConfig.VERSION_NAME) - .setPluginXml(DevicePluginXmlUtil.getXml(getApplicationContext(), - R.xml.org_deviceconnect_android_deviceplugin_host)) - .setPluginId(DConnectUtil.toMD5(packageName + className)) - .setPluginSdkVersionName(VersionName.parse("2.0.0")) - .addProviderAuthority("org.deviceconnect.android.deviceplugin.host.provider.included") - .build(); - mManager.getPluginManager().addDevicePlugin(plugin); - } catch (UnsupportedEncodingException e) { - if (DEBUG) { - Log.e(TAG, "add plugin error.", e); - } - } catch (NoSuchAlgorithmException e) { - if (DEBUG) { - Log.e(TAG, "add plugin error.", e); - } - } - } - /** + /** * DConnectManagerを停止します. */ private void stopManager() { diff --git a/dConnectManager/dConnectManager/gradle/wrapper/gradle-wrapper.properties b/dConnectManager/dConnectManager/gradle/wrapper/gradle-wrapper.properties index 693811bef3..3e80d63286 100644 --- a/dConnectManager/dConnectManager/gradle/wrapper/gradle-wrapper.properties +++ b/dConnectManager/dConnectManager/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip