OSDN Git Service

動画撮影もできるようにした。
authorMRSa <mrsa@myad.jp>
Mon, 11 Jan 2021 13:15:06 +0000 (22:15 +0900)
committerMRSa <mrsa@myad.jp>
Mon, 11 Jan 2021 13:15:06 +0000 (22:15 +0900)
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/ThetaCameraController.kt
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/ThetaFeatureDispatcher.kt
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/ThetaSessionHolder.kt
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/connection/ThetaCameraConnectSequence.kt
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/liveview/ThetaLiveViewControl.kt
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaBracketingControl.kt [new file with mode: 0644]
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaMovieRecordingControl.kt [new file with mode: 0644]
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaOptionUpdateControl.kt [new file with mode: 0644]
wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaSingleShotControl.kt
wear/src/main/java/jp/sfjp/gokigen/a01c/utils/SimpleLiveviewSlicer.java

index 8742c3b..b8d618f 100644 (file)
@@ -23,7 +23,10 @@ import jp.sfjp.gokigen.a01c.preference.PreferenceAccessWrapper
 import jp.sfjp.gokigen.a01c.thetacamerawrapper.connection.ThetaCameraConnection
 import jp.sfjp.gokigen.a01c.thetacamerawrapper.liveview.ThetaLiveViewControl
 import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaDummyOperation
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaMovieRecordingControl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaOptionUpdateControl
 import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaSingleShotControl
+import java.util.*
 
 class ThetaCameraController(val context: AppCompatActivity, private val focusFrameDisplay: IAutoFocusFrameDisplay, private val showInformation: IShowInformation, private val receiver: ICameraStatusReceiver, private val preferences: PreferenceAccessWrapper) : ICameraController, IIndicatorControl
 {
@@ -33,10 +36,13 @@ class ThetaCameraController(val context: AppCompatActivity, private val focusFra
     private val sessionIdHolder = ThetaSessionHolder()
     private val cameraConnection = ThetaCameraConnection(context, receiver, sessionIdHolder)
     private val singleShot = ThetaSingleShotControl(sessionIdHolder, this, this)
+    private val movieShot = ThetaMovieRecordingControl(context, sessionIdHolder, this, showInformation, this)
+    private val optionSet = ThetaOptionUpdateControl(sessionIdHolder, this, this)
 
     override fun setLiveViewListener(listener: CameraLiveViewListenerImpl)
     {
-        this.liveViewControl = ThetaLiveViewControl(sessionIdHolder, listener)
+        Log.v(TAG, " setLiveViewListener() : ${sessionIdHolder.isApiLevelV21()} ")
+        this.liveViewControl = ThetaLiveViewControl(listener)
     }
 
     override fun changeLiveViewSize(size: String?)
@@ -47,9 +53,20 @@ class ThetaCameraController(val context: AppCompatActivity, private val focusFra
 
     override fun startLiveView()
     {
-        if (::liveViewControl.isInitialized)
+        try
+        {
+            // スチルモードに切り替える
+            changeCaptureImageMode(sessionIdHolder.isApiLevelV21())
+
+            // ライブビューの表示...
+            if (::liveViewControl.isInitialized)
+            {
+                liveViewControl.startLiveView(sessionIdHolder)
+            }
+        }
+        catch (e : Exception)
         {
-            liveViewControl.startLiveView()
+            e.printStackTrace()
         }
     }
 
@@ -63,7 +80,45 @@ class ThetaCameraController(val context: AppCompatActivity, private val focusFra
 
     override fun updateTakeMode()
     {
-        // なにもしない
+        if (::featureDispatcher.isInitialized)
+        {
+            when (featureDispatcher.takeMode)
+            {
+                "P" -> changeCaptureImageMode(sessionIdHolder.isApiLevelV21())
+                "Movie" -> changeCaptureVideoMode(sessionIdHolder.isApiLevelV21())
+            }
+        }
+    }
+
+    private fun changeCaptureImageMode(apiV21 : Boolean)
+    {
+        try
+        {
+            optionSet.setOptions("\"captureMode\" : \"image\"", apiV21)
+        }
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    private fun changeCaptureVideoMode(apiV21 : Boolean)
+    {
+        try
+        {
+            if (apiV21)
+            {
+                optionSet.setOptions("\"captureMode\" : \"video\"", apiV21)
+            }
+            else
+            {
+                optionSet.setOptions("\"captureMode\" : \"_video\"", apiV21)
+            }
+        }
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
     }
 
     override fun driveAutoFocus(event: MotionEvent?): Boolean
@@ -88,7 +143,7 @@ class ThetaCameraController(val context: AppCompatActivity, private val focusFra
 
     override fun movieControl()
     {
-        // TODO("Not yet implemented")
+        movieShot.movieControl(sessionIdHolder.isApiLevelV21())
     }
 
     override fun bracketingShot(bracketingStyle: Int, bracketingCount: Int, durationSeconds: Int)
@@ -184,4 +239,21 @@ class ThetaCameraController(val context: AppCompatActivity, private val focusFra
     {
         //TODO("Not yet implemented")
     }
+
+    private fun waitMs(waitMs: Int)
+    {
+        try
+        {
+            Thread.sleep(waitMs.toLong())
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    companion object
+    {
+        private val TAG = ThetaCameraController::class.java.simpleName
+    }
 }
index ddfb8be..89fca91 100644 (file)
@@ -1,5 +1,6 @@
 package jp.sfjp.gokigen.a01c.thetacamerawrapper
 
+import android.graphics.Color
 import android.util.Log
 import android.view.MotionEvent
 import androidx.appcompat.app.AppCompatActivity
@@ -14,6 +15,7 @@ import jp.sfjp.gokigen.a01c.olycamerawrapper.takepicture.IBracketingShotStyle
 
 class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: IShowInformation, val camera: ICameraController, private val preferences: PreferenceDataStore, val liveImageView: ILiveImageStatusNotify): ICameraFeatureDispatcher
 {
+    private var takeMode : String = "P"
 
     /**
      * 指定した機能を実行する
@@ -34,17 +36,21 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
         // 機能実行の割り当て...
         var duration = IShowInformation.VIBRATE_PATTERN_SIMPLE_SHORT
         when (featureNumber) {
-            ICameraFeatureDispatcher.FEATURE_TOGGLE_SHOW_GRID ->                  // グリッド標示ON/OFF
+            ICameraFeatureDispatcher.FEATURE_TOGGLE_SHOW_GRID ->                    // グリッド標示ON/OFF
                 changeShowGrid(objectId)
-            ICameraFeatureDispatcher.FEATURE_SHUTTER_SINGLESHOT ->                 // シャッター
+            ICameraFeatureDispatcher.FEATURE_SHUTTER_SINGLESHOT ->                  // シャッター(一枚撮影)
                 pushShutterButton()
             ICameraFeatureDispatcher.FEATURE_SETTINGS -> {
-                showSettingsScreen()                                              // 設定画面を開く
+                showSettingsScreen()                                                // 設定画面を開く
                 duration = IShowInformation.VIBRATE_PATTERN_NONE
             }
-/*
+            ICameraFeatureDispatcher.FEATURE_CONTROL_MOVIE ->                       // 動画の撮影・撮影終了
+                movieControl()
             ICameraFeatureDispatcher.FEATURE_CHANGE_TAKEMODE ->                    // 撮影モードの変更
                 changeTakeMode()
+            ICameraFeatureDispatcher.FEATURE_CHANGE_TAKEMODE_REVERSE ->            // 撮影モードの変更(逆順)
+                changeTakeModeReverse()
+/*
             ICameraFeatureDispatcher.FEATURE_CHAGE_AE_LOCK_MODE ->                 // AE LOCKのON/OFF切り替え
                 changeAeLockMode()
             ICameraFeatureDispatcher.FEATURE_EXPOSURE_BIAS_DOWN ->                 // 露出補正を1段階下げる
@@ -69,10 +75,6 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
                 changeArtFilterUp()
             ICameraFeatureDispatcher.FEATURE_TOGGLE_SHOW_LEVEL_GAUGE ->                 // デジタル水準器の表示・非表示
                 changeShowLevelGauge()
-            ICameraFeatureDispatcher.FEATURE_CHANGE_TAKEMODE_REVERSE ->                 // 撮影モードの変更(逆順)
-                changeTakeModeReverse()
-            ICameraFeatureDispatcher.FEATURE_CONTROL_MOVIE ->                 // 動画の撮影・撮影終了
-                movieControl()
             ICameraFeatureDispatcher.FEATURE_AE_DOWN ->                 // AE(測光方式)を選択
                 changeAEModeDown()
             ICameraFeatureDispatcher.FEATURE_AE_UP ->                 // AE(測光方式)を選択
@@ -204,7 +206,7 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
      */
     override fun getTakeMode(): String?
     {
-        return ("P")   // 暫定でプログラムモード
+        return (takeMode)   // 暫定でプログラムモード
 
         //val propertyProxy = camera.cameraPropertyProvider
         //return propertyProxy.getCameraPropertyValueTitle(propertyProxy.getCameraPropertyValue(IOlyCameraProperty.TAKE_MODE))
@@ -216,6 +218,20 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
      */
     private fun changeTakeMode()
     {
+        if (takeMode.contains("P"))
+        {
+            takeMode = "Movie"
+        }
+        else
+        {
+            takeMode = "P"
+        }
+        statusDrawer.setMessage(IShowInformation.AREA_1, Color.WHITE, takeMode)
+
+        //  撮影モードの更新
+        camera.updateTakeMode();
+
+/*
         val propertyProxy = camera.cameraPropertyProvider
         val propetyValue = propertyProxy.getCameraPropertyValueTitle(propertyProxy.getCameraPropertyValue(IOlyCameraProperty.TAKE_MODE))
                 ?: // データ取得失敗
@@ -231,12 +247,11 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
             "iAuto" -> "$targetMode/P>"
             else -> "$targetMode/P>"
         }
+
         Log.v(TAG, "changeTakeMode() $targetMode")
         propertyProxy.setCameraPropertyValue(IOlyCameraProperty.TAKE_MODE, targetMode)
         camera.unlockAutoFocus()
-
-        //  撮影モードの更新
-        //camera.updateTakeMode();
+*/
     }
 
     /**
@@ -245,6 +260,8 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
      */
     private fun changeTakeModeReverse()
     {
+        changeTakeMode()
+/*
         val propertyProxy = camera.cameraPropertyProvider
         val propetyValue = propertyProxy.getCameraPropertyValueTitle(propertyProxy.getCameraPropertyValue(IOlyCameraProperty.TAKE_MODE))
                 ?: // データ取得失敗
@@ -266,6 +283,7 @@ class ThetaFeatureDispatcher(val context: AppCompatActivity, val statusDrawer: I
 
         //  撮影モードの更新
         //camera.updateTakeMode();
+*/
     }
 
     /**
index cf8964b..7e4d22b 100644 (file)
@@ -1,5 +1,7 @@
 package jp.sfjp.gokigen.a01c.thetacamerawrapper
 
+import android.util.Log
+
 class ThetaSessionHolder : IThetaSessionIdProvider, IThetaSessionIdNotifier
 {
     private var sessionId : String = ""
@@ -11,14 +13,21 @@ class ThetaSessionHolder : IThetaSessionIdProvider, IThetaSessionIdNotifier
 
     override fun receivedSessionId(sessionId: String?)
     {
-        if (sessionId != null)
+        if (!(sessionId.isNullOrEmpty()))
         {
+            Log.v(TAG, " SESSION ID : $sessionId")
             this.sessionId = sessionId
         }
     }
 
     fun isApiLevelV21() : Boolean
     {
-        return (!(sessionId.isNotEmpty()))
+        return (sessionId.isEmpty())
     }
+
+    companion object
+    {
+        private val TAG = ThetaSessionHolder::class.java.simpleName
+    }
+
 }
index cbf8c38..b60a0f5 100644 (file)
@@ -56,9 +56,15 @@ class ThetaCameraConnectSequence(private val context: AppCompatActivity, private
                 for (index in 0 until size)
                 {
                     val api = apiLevelArray.getInt(index)
+                    if (api == 1)  //if (api == 1 && useThetaV21)
+                    {
+                        apiLevelIsV21 = false
+                        break
+                    }
                     if (api == 2)  //if (api == 2 && useThetaV21)
                     {
                         apiLevelIsV21 = true
+                        break
                     }
                 }
             }
@@ -111,12 +117,14 @@ class ThetaCameraConnectSequence(private val context: AppCompatActivity, private
         }
     }
 
-    private fun connectApiV21() {
+    private fun connectApiV21()
+    {
         val commandsExecuteUrl = "http://192.168.1.1/osc/commands/execute"
         val startSessionData = "{\"name\":\"camera.startSession\",\"parameters\":{\"timeout\":0}}"
         val getStateUrl = "http://192.168.1.1/osc/state"
         val timeoutMs = 5000
-        try {
+        try
+        {
             val responseS: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, startSessionData, null, "application/json;charset=utf-8", timeoutMs)
             Log.v(TAG, " [ $httpClient ] $startSessionData ::: $responseS")
             val response: String? = httpClient.httpPostWithHeader(getStateUrl, "", null, null, timeoutMs)
@@ -125,19 +133,26 @@ class ThetaCameraConnectSequence(private val context: AppCompatActivity, private
             {
                 var apiLevel = 1
                 var sessionId: String? = null
-                val `object` = JSONObject(response)
-                try {
-                    apiLevel = `object`.getJSONObject("state").getInt("_apiVersion")
-                } catch (e: Exception) {
+                val jsonObject = JSONObject(response)
+                try
+                {
+                    apiLevel = jsonObject.getJSONObject("state").getInt("_apiVersion")
+                }
+                catch (e: Exception)
+                {
                     e.printStackTrace()
                 }
-                try {
-                    sessionId = `object`.getJSONObject("state").getString("sessionId")
+                try
+                {
+                    sessionId = jsonObject.getJSONObject("state").getString("sessionId")
                     sessionIdNotifier.receivedSessionId(sessionId)
-                } catch (e: Exception) {
+                }
+                catch (e: Exception)
+                {
                     e.printStackTrace()
                 }
-                if (apiLevel != 2) {
+                if (apiLevel != 2)
+                {
                     val setApiLevelData = "{\"name\":\"camera.setOptions\",\"parameters\":{\"sessionId\" : \"$sessionId\", \"options\":{ \"clientVersion\":2}}}"
                     val response3: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, setApiLevelData, null, "application/json;charset=utf-8", timeoutMs)
                     Log.v(TAG, " $commandsExecuteUrl $setApiLevelData $response3")
index b0648aa..57512bd 100644 (file)
@@ -5,11 +5,11 @@ import jp.sfjp.gokigen.a01c.liveview.CameraLiveViewListenerImpl
 import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider
 import jp.sfjp.gokigen.a01c.utils.SimpleLiveviewSlicer
 
-class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvider, private val liveViewListener: CameraLiveViewListenerImpl)
+class ThetaLiveViewControl(private val liveViewListener: CameraLiveViewListenerImpl)
 {
     private var whileFetching = false
 
-    fun startLiveView()
+    fun startLiveView(sessionIdProvider: IThetaSessionIdProvider)
     {
         Log.v(TAG, " startLiveView()")
         try
@@ -18,7 +18,7 @@ class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvide
                 try
                 {
 
-                    start(!(sessionIdProvider.sessionId.isEmpty()))
+                    start(sessionIdProvider.sessionId)
                 }
                 catch (e: Exception)
                 {
@@ -39,7 +39,7 @@ class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvide
         whileFetching = false
     }
 
-    private fun start(useOscV2 : Boolean)
+    private fun start(sessionId : String)
     {
         if (whileFetching)
         {
@@ -57,12 +57,13 @@ class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvide
                 try
                 {
                     val streamUrl = "http://192.168.1.1/osc/commands/execute"
-                    val paramData = if (useOscV2) "{\"name\":\"camera.getLivePreview\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera._getLivePreview\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.getSessionId().toString() + "\"}}"
-                    Log.v(TAG, " >>>>> START THETA PREVIEW : $streamUrl $paramData")
+                    val paramData = if (sessionId.isEmpty()) "{\"name\":\"camera.getLivePreview\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera._getLivePreview\",\"parameters\":{\"sessionId\": \"$sessionId\"}}"
+                    Log.v(TAG, " >>>>> START THETA PREVIEW($sessionId) : $streamUrl $paramData")
 
                     // Create Slicer to open the stream and parse it.
                     slicer.open(streamUrl, paramData, "application/json;charset=utf-8")
-                    while (whileFetching) {
+                    while (whileFetching)
+                    {
                         val payload: SimpleLiveviewSlicer.Payload? = slicer.nextPayloadForMotionJpeg()
                         if (payload == null)
                         {
@@ -90,7 +91,7 @@ class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvide
                     {
                         // 再度ライブビューのスタートをやってみる。
                         whileFetching = false
-                        start(useOscV2)
+                        start(sessionId)
                     }
                 }
             }
diff --git a/wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaBracketingControl.kt b/wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaBracketingControl.kt
new file mode 100644 (file)
index 0000000..42d0e0b
--- /dev/null
@@ -0,0 +1,214 @@
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation
+
+import android.content.Context
+import android.graphics.Color
+import android.util.Log
+import jp.sfjp.gokigen.a01c.ICameraController
+import jp.sfjp.gokigen.a01c.IShowInformation
+import jp.sfjp.gokigen.a01c.R
+import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider
+import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient
+import org.json.JSONObject
+
+class ThetaBracketingControl(val context: Context, private val sessionIdProvider: IThetaSessionIdProvider, private val indicator: IIndicatorControl, private val statusDrawer: IShowInformation, private val liveViewControl: ICameraController)
+{
+    private val httpClient = SimpleHttpClient()
+    private var isCapturing = false
+
+    fun bracketingControl(useOSCv2 : Boolean)
+    {
+        try
+        {
+            if (!(isCapturing))
+            {
+                startCapture(useOSCv2)
+                statusDrawer.setMessage(IShowInformation.AREA_9, Color.RED, context.getString(R.string.video_recording))
+            }
+            else
+            {
+                stopCapture(useOSCv2)
+                statusDrawer.setMessage(IShowInformation.AREA_9, Color.WHITE, "")
+            }
+            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 + "\"}}"
+                    val postData = if (useOSCv2) "{\"name\":\"camera.startCapture\",\"parameters\":{\"timeout\":0, \"_mode\": \"bracket\"}}" else "{\"name\":\"camera._startCapture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\", \"mode\": \"bracket\"}}}"
+
+                    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")
+                        indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Starting)
+
+                        // 画像処理が終わるまで待つ
+                        //waitChangeStatus()
+
+                        // ライブビューのの再実行を指示する
+                        //indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Stopping)
+                        //liveViewControl.stopLiveView()
+                        //waitMs(300) // ちょっと待つ...
+                        //liveViewControl.startLiveView()
+                    }
+                    else
+                    {
+                        Log.v(TAG, "startCapture() reply is null.")
+                    }
+                }
+                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")
+                        indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Starting)
+
+                        // 画像処理が終わるまで待つ
+                        waitChangeStatus()
+
+                        // ライブビューのの再実行を指示する
+                        indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Stopping)
+                        liveViewControl.stopLiveView()
+                        waitMs(300) // ちょっと待つ...
+                        liveViewControl.startLiveView()
+                    }
+                    else
+                    {
+                        Log.v(TAG, "stopCapture() reply is null.")
+                    }
+                }
+                catch (e: Exception)
+                {
+                    e.printStackTrace()
+                }
+            }
+            thread.start()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     * 撮影状態が変わるまで待つ。
+     * (ただし、タイムアウト時間を超えたらライブビューを再開させる)
+     */
+    private fun waitChangeStatus()
+    {
+        val getStateUrl = "http://192.168.1.1/osc/state"
+        val maxWaitTimeoutMs = 9900 // 最大待ち時間 (単位: ms)
+        var fingerprint = ""
+        try
+        {
+            val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs)
+            if ((result != null)&&(result.isNotEmpty()))
+            {
+                val jsonObject = JSONObject(result)
+                fingerprint = jsonObject.getString("fingerprint")
+
+                //  現在の状態(ログを出す)
+                Log.v(TAG, " $getStateUrl $result ($fingerprint)")
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+
+        try
+        {
+            val firstTime = System.currentTimeMillis()
+            var currentTime = firstTime
+            while (currentTime - firstTime < maxWaitTimeoutMs)
+            {
+                //  ... 状態を見て次に進める
+                val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs)
+                if ((result != null)&&(result.isNotEmpty()))
+                {
+                    val jsonObject = JSONObject(result)
+                    val currentFingerprint = jsonObject.getString("fingerprint")
+
+                    //  ログを出してみる
+                    // Log.v(TAG, " " + getStateUrl + " ( " + result + " ) " + "(" + fingerprint + " " + current_fingerprint + ")");
+                    if (fingerprint != currentFingerprint)
+                    {
+                        // fingerprintが更新された!
+                        break
+                    }
+                    Log.v(TAG, "  -----  NOW PROCESSING  ----- : $fingerprint")
+                }
+                waitMs(1000)
+                currentTime = System.currentTimeMillis()
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     *
+     *
+     */
+    private fun waitMs(waitMs: Int)
+    {
+        try
+        {
+            Thread.sleep(waitMs.toLong())
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    companion object
+    {
+        private val TAG = ThetaBracketingControl::class.java.simpleName
+        private const val timeoutMs = 6000
+    }
+
+}
\ No newline at end of file
diff --git a/wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaMovieRecordingControl.kt b/wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaMovieRecordingControl.kt
new file mode 100644 (file)
index 0000000..8306be0
--- /dev/null
@@ -0,0 +1,213 @@
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation
+
+import android.content.Context
+import android.graphics.Color
+import android.util.Log
+import jp.sfjp.gokigen.a01c.ICameraController
+import jp.sfjp.gokigen.a01c.IShowInformation
+import jp.sfjp.gokigen.a01c.R
+import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider
+import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient
+import org.json.JSONObject
+
+class ThetaMovieRecordingControl(val context: Context, private val sessionIdProvider: IThetaSessionIdProvider, private val indicator: IIndicatorControl, private val statusDrawer: IShowInformation, private val liveViewControl: ICameraController)
+{
+    private val httpClient = SimpleHttpClient()
+    private var isCapturing = false
+
+    fun movieControl(useOSCv2 : Boolean)
+    {
+        try
+        {
+            if (!(isCapturing))
+            {
+                startCapture(useOSCv2)
+                statusDrawer.setMessage(IShowInformation.AREA_9, Color.RED, context.getString(R.string.video_recording))
+            }
+            else
+            {
+                stopCapture(useOSCv2)
+                statusDrawer.setMessage(IShowInformation.AREA_9, Color.WHITE, "")
+            }
+            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 + "\"}}"
+                    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")
+                        indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Starting)
+
+                        // 画像処理が終わるまで待つ
+                        //waitChangeStatus()
+
+                        // ライブビューのの再実行を指示する
+                        //indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Stopping)
+                        //liveViewControl.stopLiveView()
+                        //waitMs(300) // ちょっと待つ...
+                        //liveViewControl.startLiveView()
+                    }
+                    else
+                    {
+                        Log.v(TAG, "startCapture() reply is null.")
+                    }
+                }
+                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")
+                        indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Starting)
+
+                        //// 画像処理が終わるまで待つ
+                        //waitChangeStatus()
+
+                        // ライブビューのの再実行を指示する
+                        indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Stopping)
+                        liveViewControl.stopLiveView()
+                        waitMs(300) // ちょっと待つ...
+                        liveViewControl.startLiveView()
+                    }
+                    else
+                    {
+                        Log.v(TAG, "stopCapture() reply is null.")
+                    }
+                }
+                catch (e: Exception)
+                {
+                    e.printStackTrace()
+                }
+            }
+            thread.start()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     * 撮影状態が変わるまで待つ。
+     * (ただし、タイムアウト時間を超えたらライブビューを再開させる)
+     */
+    private fun waitChangeStatus()
+    {
+        val getStateUrl = "http://192.168.1.1/osc/state"
+        val maxWaitTimeoutMs = 9900 // 最大待ち時間 (単位: ms)
+        var fingerprint = ""
+        try
+        {
+            val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs)
+            if ((result != null)&&(result.isNotEmpty()))
+            {
+                val jsonObject = JSONObject(result)
+                fingerprint = jsonObject.getString("fingerprint")
+
+                //  現在の状態(ログを出す)
+                Log.v(TAG, " $getStateUrl $result ($fingerprint)")
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+
+        try
+        {
+            val firstTime = System.currentTimeMillis()
+            var currentTime = firstTime
+            while (currentTime - firstTime < maxWaitTimeoutMs)
+            {
+                //  ... 状態を見て次に進める
+                val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs)
+                if ((result != null)&&(result.isNotEmpty()))
+                {
+                    val jsonObject = JSONObject(result)
+                    val currentFingerprint = jsonObject.getString("fingerprint")
+
+                    //  ログを出してみる
+                    // Log.v(TAG, " " + getStateUrl + " ( " + result + " ) " + "(" + fingerprint + " " + current_fingerprint + ")");
+                    if (fingerprint != currentFingerprint)
+                    {
+                        // fingerprintが更新された!
+                        break
+                    }
+                    Log.v(TAG, "  -----  NOW PROCESSING  ----- : $fingerprint")
+                }
+                waitMs(1000)
+                currentTime = System.currentTimeMillis()
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     *
+     *
+     */
+    private fun waitMs(waitMs: Int)
+    {
+        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/wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaOptionUpdateControl.kt b/wear/src/main/java/jp/sfjp/gokigen/a01c/thetacamerawrapper/operation/ThetaOptionUpdateControl.kt
new file mode 100644 (file)
index 0000000..9bc54a5
--- /dev/null
@@ -0,0 +1,59 @@
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation
+
+import android.util.Log
+import jp.sfjp.gokigen.a01c.ICameraController
+import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider
+import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient
+
+class ThetaOptionUpdateControl(private val sessionIdProvider: IThetaSessionIdProvider, private val indicator: IIndicatorControl, private val liveViewControl: ICameraController)
+{
+    private val httpClient = SimpleHttpClient()
+
+    /**
+     *
+     *
+     */
+    fun setOptions(options: String, useOSCv2 : Boolean)
+    {
+        Log.v(TAG, "setOptions() useOSCv2 : $useOSCv2  MSG : $options")
+        try
+        {
+            val thread = Thread {
+                try
+                {
+                    val setOptionsUrl = "http://192.168.1.1/osc/commands/execute"
+                    val postData = if (useOSCv2) "{\"name\":\"camera.setOptions\",\"parameters\":{\"timeout\":0, \"options\": {\"$options\"}}}" else "{\"name\":\"camera.setOptions\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\", \"options\": { $options }}}"
+
+                    Log.v(TAG, " OPTIONS : $postData")
+
+                    val result: String? = httpClient.httpPostWithHeader(setOptionsUrl, postData, null, "application/json;charset=utf-8", timeoutMs)
+                    if ((result != null)&&(result.isNotEmpty()))
+                    {
+                        Log.v(TAG, " setOptions() : $result")
+                    }
+                    else
+                    {
+                        Log.v(TAG, "setOptions() reply is null or empty.")
+                    }
+                }
+                catch (e: Exception)
+                {
+                    e.printStackTrace()
+                }
+            }
+            thread.start()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    companion object
+    {
+        private val TAG = ThetaOptionUpdateControl::class.java.simpleName
+        private const val timeoutMs = 3000
+    }
+
+}
\ No newline at end of file
index 8caa898..a1ed55b 100644 (file)
@@ -16,7 +16,7 @@ class ThetaSingleShotControl(private val sessionIdProvider: IThetaSessionIdProvi
      *
      *
      */
-    fun singleShot(useThetaV21 : Boolean)
+    fun singleShot(useOSCv2 : Boolean)
     {
         Log.v(TAG, "singleShot()")
         try
@@ -25,7 +25,7 @@ class ThetaSingleShotControl(private val sessionIdProvider: IThetaSessionIdProvi
                 try
                 {
                     val shootUrl = "http://192.168.1.1/osc/commands/execute"
-                    val postData = if (useThetaV21) "{\"name\":\"camera.takePicture\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera.takePicture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\"}}"
+                    val postData = if (useOSCv2) "{\"name\":\"camera.takePicture\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera.takePicture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\"}}"
                     val result: String? = httpClient.httpPostWithHeader(shootUrl, postData, null, "application/json;charset=utf-8", timeoutMs)
                     if ((result != null)&&(result.isNotEmpty()))
                     {
index 727b8ee..338a46d 100644 (file)
@@ -94,6 +94,10 @@ public class SimpleLiveviewSlicer
             {
                 mInputStream = mHttpConn.getInputStream();
             }
+            else
+            {
+                Log.v(TAG, " RESPONSE NG : " + mHttpConn.getResponseCode() + " " + mHttpConn.getResponseMessage());
+            }
         }
         catch (Exception e)
         {
@@ -355,7 +359,7 @@ public class SimpleLiveviewSlicer
     public @Nullable Payload nextPayloadForMotionJpeg()
     {
         int searchIndex = 0;
-        int[] endmarker = { 0xff, 0xd9 };
+        int[] endMarker = { 0xff, 0xd9 };
         Payload payload = null;
         try
         {
@@ -373,10 +377,10 @@ public class SimpleLiveviewSlicer
                         // 1byteづつの読み込み... 本当は複数バイト読み出しで処理したい
                         int data = mInputStream.read();
                         tmpByteArray.write(data);
-                        if (data == endmarker[searchIndex])
+                        if (data == endMarker[searchIndex])
                         {
                             searchIndex++;
-                            if (searchIndex >= endmarker.length)
+                            if (searchIndex >= endMarker.length)
                             {
                                 break;
                             }