OSDN Git Service

静止画キャプチャだけでなく、ビデオ録画もできるようにしてみた。
authorMRSa <mrsa@myad.jp>
Sun, 21 Feb 2021 13:14:01 +0000 (22:14 +0900)
committerMRSa <mrsa@myad.jp>
Sun, 21 Feb 2021 13:14:01 +0000 (22:14 +0900)
app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/ThetaControl.kt
app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/operation/ThetaMovieRecordingControl.kt [new file with mode: 0644]
app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/status/ICaptureModeReceiver.kt [new file with mode: 0644]
app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/status/ThetaCameraStatusWatcher.kt
app/src/main/java/jp/osdn/gokigen/thetaview/scene/IChangeScene.kt
app/src/main/java/jp/osdn/gokigen/thetaview/scene/MainButtonHandler.kt
app/src/main/java/jp/osdn/gokigen/thetaview/scene/SceneChanger.kt
app/src/main/res/drawable/ic_baseline_videocam_24.xml [new file with mode: 0644]
app/src/main/res/layout/activity_main.xml
app/src/main/res/xml/preference_main.xml

index 5147835..4a2d36e 100644 (file)
@@ -2,13 +2,18 @@ package jp.osdn.gokigen.thetaview.camera.theta
 
 import android.util.Log
 import android.view.View
+import android.widget.ImageButton
 import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
 import jp.osdn.gokigen.thetaview.IShowInformation
 import jp.osdn.gokigen.thetaview.R
 import jp.osdn.gokigen.thetaview.camera.ICameraStatusReceiver
 import jp.osdn.gokigen.thetaview.camera.theta.connection.ThetaCameraConnection
 import jp.osdn.gokigen.thetaview.camera.theta.liveview.ThetaLiveViewControl
+import jp.osdn.gokigen.thetaview.camera.theta.operation.ThetaMovieRecordingControl
+import jp.osdn.gokigen.thetaview.camera.theta.operation.ThetaOptionUpdateControl
 import jp.osdn.gokigen.thetaview.camera.theta.operation.ThetaSingleShotControl
+import jp.osdn.gokigen.thetaview.camera.theta.status.ICaptureModeReceiver
 import jp.osdn.gokigen.thetaview.camera.theta.status.ThetaCameraStatusWatcher
 import jp.osdn.gokigen.thetaview.camera.theta.status.ThetaSessionHolder
 import jp.osdn.gokigen.thetaview.liveview.ILiveView
@@ -19,7 +24,7 @@ import jp.osdn.gokigen.thetaview.operation.ICameraControl
 import jp.osdn.gokigen.thetaview.scene.ICameraConnectionStatus
 import jp.osdn.gokigen.thetaview.scene.IIndicator
 
-class ThetaControl(private val context: AppCompatActivity, private val showInformation : IShowInformation, statusReceiver : ICameraStatusReceiver) : ILiveViewController, ICameraControl, View.OnClickListener
+class ThetaControl(private val context: AppCompatActivity, private val showInformation : IShowInformation, statusReceiver : ICameraStatusReceiver) : ILiveViewController, ICameraControl, View.OnClickListener, ICaptureModeReceiver
 {
     private val sessionIdHolder = ThetaSessionHolder()
     private val cameraConnection = ThetaCameraConnection(context, statusReceiver, sessionIdHolder, sessionIdHolder, this)
@@ -27,7 +32,7 @@ class ThetaControl(private val context: AppCompatActivity, private val showInfor
     private val liveViewControl = ThetaLiveViewControl(liveViewListener)
     private var indicator : IIndicator? = null
 
-    private val statusWatcher = ThetaCameraStatusWatcher(sessionIdHolder)
+    private val statusWatcher = ThetaCameraStatusWatcher(sessionIdHolder, this)
     private var isStatusWatch = false
 
     fun setIndicator(indicator : IIndicator)
@@ -35,6 +40,46 @@ class ThetaControl(private val context: AppCompatActivity, private val showInfor
         this.indicator = indicator
     }
 
+    fun changeCaptureMode()
+    {
+        val options = if (statusWatcher.captureMode.contains("image"))
+        {
+            // image -> video
+            "\"captureMode\" : \"video\""
+        }
+        else
+        {
+            // video -> image
+            "\"captureMode\" : \"image\""
+        }
+        ThetaOptionUpdateControl(sessionIdHolder).setOptions(options, sessionIdHolder.isApiLevelV21())
+    }
+
+    override fun changedCaptureMode(captureMode : String)
+    {
+        try
+        {
+            val isImage = captureMode.contains("image")
+            context.runOnUiThread {
+                try
+                {
+                    val view : ImageButton = context.findViewById(R.id.button_camera)
+                    val iconId = if (isImage) { R.drawable.ic_baseline_videocam_24 } else { R.drawable.ic_baseline_camera_alt_24 }
+                    view.setImageDrawable(ContextCompat.getDrawable(context, iconId))
+                    view.invalidate()
+                }
+                catch (e : Exception)
+                {
+                    e.printStackTrace()
+                }
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
     override fun initialize()
     {
         // TODO("Not yet implemented")
@@ -116,7 +161,17 @@ class ThetaControl(private val context: AppCompatActivity, private val showInfor
         }
         when (v.id)
         {
-            R.id.button_camera -> { ThetaSingleShotControl(sessionIdHolder, showInformation, liveViewControl).singleShot(sessionIdHolder.isApiLevelV21()) }
+            R.id.button_camera -> {
+                if (statusWatcher.captureMode.contains("image")) {
+                    // image
+                    ThetaSingleShotControl(sessionIdHolder, showInformation, liveViewControl).singleShot(sessionIdHolder.isApiLevelV21())
+                }
+                else
+                {
+                    // video
+                    ThetaMovieRecordingControl(sessionIdHolder, showInformation, liveViewControl).movieControl(sessionIdHolder.isApiLevelV21())
+                }
+            }
             else -> { }
         }
     }
diff --git a/app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/operation/ThetaMovieRecordingControl.kt b/app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/operation/ThetaMovieRecordingControl.kt
new file mode 100644 (file)
index 0000000..5c94792
--- /dev/null
@@ -0,0 +1,134 @@
+package jp.osdn.gokigen.thetaview.camera.theta.operation
+
+import android.util.Log
+import jp.osdn.gokigen.thetaview.IShowInformation
+import jp.osdn.gokigen.thetaview.camera.theta.status.IThetaSessionIdProvider
+import jp.osdn.gokigen.thetaview.liveview.ILiveViewController
+import jp.osdn.gokigen.thetaview.utils.communication.SimpleHttpClient
+
+class ThetaMovieRecordingControl(private val sessionIdProvider: IThetaSessionIdProvider, private val statusDrawer: IShowInformation, private val liveViewControl : ILiveViewController)
+{
+    private val httpClient = SimpleHttpClient()
+    private var isCapturing = false
+
+    fun movieControl(useOSCv2 : Boolean)
+    {
+        try
+        {
+            if (!(isCapturing))
+            {
+                startCapture(useOSCv2)
+            }
+            else
+            {
+                stopCapture(useOSCv2)
+            }
+            statusDrawer.invalidate()
+            isCapturing = !isCapturing
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    private fun startCapture(useOSCv2 : Boolean)
+    {
+        Log.v(TAG, "startCapture() (API v2.1 : $useOSCv2)")
+        try
+        {
+            val thread = Thread {
+                try
+                {
+                    val shootUrl = "http://192.168.1.1/osc/commands/execute"
+                    val postData = if (useOSCv2) "{\"name\":\"camera.startCapture\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera._startCapture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\"}}"
+
+                    Log.v(TAG, " start Capture : $postData")
+                    val result: String? = httpClient.httpPostWithHeader(shootUrl, postData, null, "application/json;charset=utf-8", timeoutMs)
+                    if ((result != null)&&(result.isNotEmpty()))
+                    {
+                        Log.v(TAG, " startCapture() : $result")
+                    }
+                    else
+                    {
+                        Log.v(TAG, "startCapture() reply is null.  $postData")
+                    }
+                }
+                catch (e: Exception)
+                {
+                    e.printStackTrace()
+                }
+            }
+            thread.start()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    private fun stopCapture(useOSCv2 : Boolean)
+    {
+        Log.v(TAG, "stopCapture() (API v2.1 : $useOSCv2)")
+        try
+        {
+            val thread = Thread {
+                try
+                {
+                    val shootUrl = "http://192.168.1.1/osc/commands/execute"
+                    val postData = if (useOSCv2) "{\"name\":\"camera.stopCapture\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera._stopCapture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\"}}"
+
+                    Log.v(TAG, " stop Capture : $postData")
+
+                    val result: String? = httpClient.httpPostWithHeader(shootUrl, postData, null, "application/json;charset=utf-8", timeoutMs)
+                    if ((result != null)&&(result.isNotEmpty()))
+                    {
+                        Log.v(TAG, " stopCapture() : $result")
+                        if (!useOSCv2)
+                        {
+                            // THETA V / THETA Z1 は、videoモードでライブビューができるので...
+                            liveViewControl.stopLiveView()
+                            waitMs() // ちょっと待つ...
+                            liveViewControl.startLiveView()
+                        }
+                    }
+                    else
+                    {
+                        Log.v(TAG, "stopCapture() reply is null. $postData")
+                    }
+                }
+                catch (e: Exception)
+                {
+                    e.printStackTrace()
+                }
+            }
+            thread.start()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     *
+     *
+     */
+    private fun waitMs(waitMs: Int = 300)
+    {
+        try
+        {
+            Thread.sleep(waitMs.toLong())
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    companion object
+    {
+        private val TAG = ThetaMovieRecordingControl::class.java.simpleName
+        private const val timeoutMs = 6000
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/status/ICaptureModeReceiver.kt b/app/src/main/java/jp/osdn/gokigen/thetaview/camera/theta/status/ICaptureModeReceiver.kt
new file mode 100644 (file)
index 0000000..5f0dab2
--- /dev/null
@@ -0,0 +1,7 @@
+package jp.osdn.gokigen.thetaview.camera.theta.status
+
+interface ICaptureModeReceiver
+{
+    fun changedCaptureMode(captureMode : String)
+
+}
\ No newline at end of file
index 012a374..f9621ce 100644 (file)
@@ -6,7 +6,7 @@ import jp.osdn.gokigen.thetaview.scene.IIndicator
 import jp.osdn.gokigen.thetaview.utils.communication.SimpleHttpClient
 import org.json.JSONObject
 
-class ThetaCameraStatusWatcher(private val sessionIdProvider: IThetaSessionIdProvider, private val executeUrl : String = "http://192.168.1.1") : ICameraStatusWatcher, IThetaStatusHolder
+class ThetaCameraStatusWatcher(private val sessionIdProvider: IThetaSessionIdProvider, private val captureModeReceiver : ICaptureModeReceiver, private val executeUrl : String = "http://192.168.1.1") : ICameraStatusWatcher, IThetaStatusHolder
 {
     private val httpClient = SimpleHttpClient()
     private var whileFetching = false
@@ -125,6 +125,7 @@ class ThetaCameraStatusWatcher(private val sessionIdProvider: IThetaSessionIdPro
                 {
                     Log.v(TAG, " CapMode : $currentCaptureMode -> $captureMode")
                     currentCaptureMode = captureMode
+                    captureModeReceiver.changedCaptureMode(captureMode)
                     setMessage(IIndicator.Area.AREA_2, Color.WHITE, "Capture Mode : $captureMode")
                 }
             }
index 7f98507..50a5f72 100644 (file)
@@ -10,4 +10,5 @@ interface IChangeScene
     fun changeToConfiguration()
     fun changeToDebugInformation()
     fun exitApplication()
+    fun changeCaptureMode()
 }
index 3f52bf5..851b23b 100644 (file)
@@ -56,7 +56,7 @@ class MainButtonHandler(private val activity : AppCompatActivity, private val co
     private fun camera()
     {
         Log.v(TAG, " - - - - - - - - - CAMERA - - - - - - - - -")
-        sceneChanger.changeToDebugInformation()
+        sceneChanger.changeCaptureMode()
     }
 
     private fun configure()
index 616c006..dd00e6b 100644 (file)
@@ -146,6 +146,8 @@ class SceneChanger(private val activity: AppCompatActivity, private val informat
         changeFragment(logCatFragment)
     }
 
+
+
     override fun exitApplication()
     {
         val dialog = ConfirmationDialog.newInstance(activity)
@@ -161,6 +163,19 @@ class SceneChanger(private val activity: AppCompatActivity, private val informat
         )
     }
 
+    override fun changeCaptureMode()
+    {
+        try
+        {
+            // CaptureModeの変更
+            thetaControl.changeCaptureMode()
+        }
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
     private fun changeFragment(fragment: Fragment)
     {
         val transaction : FragmentTransaction = activity.supportFragmentManager.beginTransaction()
diff --git a/app/src/main/res/drawable/ic_baseline_videocam_24.xml b/app/src/main/res/drawable/ic_baseline_videocam_24.xml
new file mode 100644 (file)
index 0000000..340bff2
--- /dev/null
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
+</vector>
index a76deb4..09ba871 100644 (file)
@@ -36,7 +36,7 @@
         android:layout_marginRight="2dp"
         android:layout_alignTop="@id/button_configure"
         android:layout_alignParentBottom="true"
-        android:src="@drawable/ic_baseline_change_circle_24"
+        android:src="@drawable/ic_baseline_videocam_24"
         />
 
     <TextView
index b7eda24..2c6613d 100644 (file)
 
     <PreferenceCategory
         app:title="@string/pref_cat_application_settings">
-
+<!--
         <SwitchPreferenceCompat
             android:key="use_camera_x_preview"
             android:title="@string/pref_use_camerax_preview"/>
-
+-->
         <SwitchPreferenceCompat
             android:key="save_local_location"
             android:title="@string/save_local_location"/>