OPC(Olympus Air)用撮影アプリ。
Révision | e978c5e05b4f996aa251f85170c998f94f2ca864 (tree) |
---|---|
l'heure | 2021-01-10 23:28:44 |
Auteur | MRSa <mrsa@myad...> |
Commiter | MRSa |
Thetaのプレビューまで。
@@ -7,10 +7,17 @@ import androidx.annotation.NonNull; | ||
7 | 7 | /** |
8 | 8 | * カメラの接続/切断 |
9 | 9 | * |
10 | - * Created by MRSa on 2017/02/28. | |
11 | 10 | */ |
12 | 11 | public interface ICameraConnection |
13 | 12 | { |
13 | + enum CameraConnectionStatus | |
14 | + { | |
15 | + UNKNOWN, | |
16 | + DISCONNECTED, | |
17 | + CONNECTING, | |
18 | + CONNECTED | |
19 | + }; | |
20 | + | |
14 | 21 | // WIFI 接続系 |
15 | 22 | void startWatchWifiStatus(@NonNull Context context); |
16 | 23 | void stopWatchWifiStatus(@NonNull Context context); |
@@ -15,6 +15,7 @@ import android.widget.TextView; | ||
15 | 15 | import android.Manifest; |
16 | 16 | import android.content.pm.PackageManager; |
17 | 17 | |
18 | +import androidx.annotation.NonNull; | |
18 | 19 | import androidx.appcompat.app.AppCompatActivity; |
19 | 20 | import androidx.core.app.ActivityCompat; |
20 | 21 | import androidx.core.content.ContextCompat; |
@@ -355,8 +356,8 @@ public class MainActivity extends AppCompatActivity implements IChangeScene, IS | ||
355 | 356 | liveView = findViewById(R.id.liveview); |
356 | 357 | } |
357 | 358 | olyAirCoordinator = new OlyCameraCoordinator(this, liveView, this, this); |
358 | - thetaCoordinator = new ThetaCameraController(this, liveView, this, this); | |
359 | - currentCoordinator = olyAirCoordinator; // (connectionMethod.contains(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA)) ? thetaCoordinator : olyAirCoordinator; | |
359 | + thetaCoordinator = new ThetaCameraController(this, liveView, this, this, preferences); | |
360 | + currentCoordinator = (connectionMethod.contains(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA)) ? thetaCoordinator : olyAirCoordinator; | |
360 | 361 | currentCoordinator.setLiveViewListener(new CameraLiveViewListenerImpl(liveView)); |
361 | 362 | listener = new CameraLiveViewOnTouchListener(this, new FeatureDispatcher(this, this, currentCoordinator, preferences, liveView), this); |
362 | 363 | selectionDialog = new FavoriteSettingSelectionDialog(this, currentCoordinator.getCameraPropertyLoadSaveOperations(), this); |
@@ -548,10 +549,29 @@ public class MainActivity extends AppCompatActivity implements IChangeScene, IS | ||
548 | 549 | } |
549 | 550 | |
550 | 551 | /** |
552 | + * カメラと接続失敗 | |
553 | + */ | |
554 | + @Override | |
555 | + public void onCameraConnectError(@NonNull String message) | |
556 | + { | |
557 | + Log.v(TAG, "onCameraOccursException()"); | |
558 | + try | |
559 | + { | |
560 | + setMessage(IShowInformation.AREA_C, Color.YELLOW, message); | |
561 | + listener.setEnableOperation(operation.ONLY_CONNECT); | |
562 | + cameraDisconnectedHappened = true; | |
563 | + } | |
564 | + catch (Exception ee) | |
565 | + { | |
566 | + ee.printStackTrace(); | |
567 | + } | |
568 | + } | |
569 | + | |
570 | + /** | |
551 | 571 | * カメラに例外発生 |
552 | 572 | */ |
553 | 573 | @Override |
554 | - public void onCameraOccursException(String message, Exception e) | |
574 | + public void onCameraOccursException(@NonNull String message, Exception e) | |
555 | 575 | { |
556 | 576 | Log.v(TAG, "onCameraOccursException()"); |
557 | 577 | try |
@@ -899,7 +919,7 @@ public class MainActivity extends AppCompatActivity implements IChangeScene, IS | ||
899 | 919 | else |
900 | 920 | { |
901 | 921 | // 接続方式を Theta に切り替える |
902 | - updateConnectionMethod(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA, olyAirCoordinator); // thetaCoordinator | |
922 | + updateConnectionMethod(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA, thetaCoordinator); | |
903 | 923 | } |
904 | 924 | updateConnectionMethodMessage(); |
905 | 925 | } |
@@ -233,49 +233,54 @@ public class CameraLiveImageView extends View implements IImageDataReceiver, IAu | ||
233 | 233 | public void setImageData(byte[] data, Map<String, Object> metadata) |
234 | 234 | { |
235 | 235 | Bitmap bitmap; |
236 | - int rotationDegrees; | |
236 | + int rotationDegrees = 0; | |
237 | 237 | |
238 | - if (data != null && metadata != null) | |
238 | + if (data != null) | |
239 | 239 | { |
240 | - // Create a bitmap. | |
241 | 240 | try |
242 | 241 | { |
243 | 242 | bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); |
244 | 243 | } |
245 | - catch (OutOfMemoryError e) | |
244 | + catch (Throwable e) | |
246 | 245 | { |
247 | 246 | e.printStackTrace(); |
248 | 247 | return; |
249 | 248 | } |
250 | 249 | |
251 | - // Acquire a rotation degree of image. | |
252 | - int orientation = ExifInterface.ORIENTATION_UNDEFINED; | |
253 | - try | |
250 | + if (metadata != null) | |
254 | 251 | { |
255 | - if (metadata.containsKey(EXIF_ORIENTATION)) | |
252 | + int orientation = ExifInterface.ORIENTATION_UNDEFINED; | |
253 | + try | |
256 | 254 | { |
257 | - orientation = Integer.parseInt((String) metadata.get(EXIF_ORIENTATION)); | |
255 | + if (metadata.containsKey(EXIF_ORIENTATION)) | |
256 | + { | |
257 | + String degree = (String) metadata.get(EXIF_ORIENTATION); | |
258 | + if (degree != null) | |
259 | + { | |
260 | + orientation = Integer.parseInt(degree); | |
261 | + } | |
262 | + } | |
263 | + } | |
264 | + catch (Exception e) | |
265 | + { | |
266 | + e.printStackTrace(); | |
267 | + } | |
268 | + switch (orientation) | |
269 | + { | |
270 | + case ExifInterface.ORIENTATION_ROTATE_90: | |
271 | + rotationDegrees = 90; | |
272 | + break; | |
273 | + case ExifInterface.ORIENTATION_ROTATE_180: | |
274 | + rotationDegrees = 180; | |
275 | + break; | |
276 | + case ExifInterface.ORIENTATION_ROTATE_270: | |
277 | + rotationDegrees = 270; | |
278 | + break; | |
279 | + case ExifInterface.ORIENTATION_NORMAL: | |
280 | + default: | |
281 | + rotationDegrees = 0; | |
282 | + break; | |
258 | 283 | } |
259 | - } | |
260 | - catch (Exception e) | |
261 | - { | |
262 | - e.printStackTrace(); | |
263 | - } | |
264 | - switch (orientation) | |
265 | - { | |
266 | - case ExifInterface.ORIENTATION_ROTATE_90: | |
267 | - rotationDegrees = 90; | |
268 | - break; | |
269 | - case ExifInterface.ORIENTATION_ROTATE_180: | |
270 | - rotationDegrees = 180; | |
271 | - break; | |
272 | - case ExifInterface.ORIENTATION_ROTATE_270: | |
273 | - rotationDegrees = 270; | |
274 | - break; | |
275 | - case ExifInterface.ORIENTATION_NORMAL: | |
276 | - default: | |
277 | - rotationDegrees = 0; | |
278 | - break; | |
279 | 284 | } |
280 | 285 | imageBitmap = bitmap; |
281 | 286 | imageRotationDegrees = rotationDegrees; |
@@ -16,6 +16,7 @@ import jp.co.olympus.camerakit.OLYCameraLiveViewListener; | ||
16 | 16 | */ |
17 | 17 | public class CameraLiveViewListenerImpl implements OLYCameraLiveViewListener, IImageDataReceiver |
18 | 18 | { |
19 | + private final String TAG = toString(); | |
19 | 20 | private final IImageDataReceiver imageView; |
20 | 21 | |
21 | 22 | /** |
@@ -43,9 +44,10 @@ public class CameraLiveViewListenerImpl implements OLYCameraLiveViewListener, II | ||
43 | 44 | @Override |
44 | 45 | public void setImageData(byte[] data, Map<String, Object> metadata) |
45 | 46 | { |
47 | + // Log.v(TAG, " setImageData len : " + data.length); | |
46 | 48 | if (imageView != null) |
47 | 49 | { |
48 | - imageView.setImageData(data, metadata); | |
50 | + imageView.setImageData(data, null); | |
49 | 51 | } |
50 | 52 | } |
51 | 53 | } |
@@ -1,5 +1,7 @@ | ||
1 | 1 | package jp.sfjp.gokigen.a01c.liveview; |
2 | 2 | |
3 | +import androidx.annotation.NonNull; | |
4 | + | |
3 | 5 | /** |
4 | 6 | * |
5 | 7 | * |
@@ -9,5 +11,6 @@ public interface ICameraStatusReceiver | ||
9 | 11 | void onStatusNotify(String message); |
10 | 12 | void onCameraConnected(); |
11 | 13 | void onCameraDisconnected(); |
12 | - void onCameraOccursException(String message, Exception e); | |
14 | + void onCameraConnectError(@NonNull String message); | |
15 | + void onCameraOccursException(@NonNull String message, Exception e); | |
13 | 16 | } |
@@ -3,5 +3,15 @@ package jp.sfjp.gokigen.a01c.olycamerawrapper; | ||
3 | 3 | |
4 | 4 | public interface IIndicatorControl |
5 | 5 | { |
6 | + // 撮影状態の記録 | |
7 | + enum shootingStatus | |
8 | + { | |
9 | + Unknown, | |
10 | + Starting, | |
11 | + Stopping, | |
12 | + } | |
13 | + | |
6 | 14 | void onAfLockUpdate(boolean isAfLocked); |
15 | + void onShootingStatusUpdate(shootingStatus status); | |
16 | + | |
7 | 17 | } |
@@ -498,6 +498,12 @@ public class OlyCameraCoordinator implements ICameraController, IIndicatorContro | ||
498 | 498 | } |
499 | 499 | |
500 | 500 | @Override |
501 | + public void onShootingStatusUpdate(shootingStatus status) | |
502 | + { | |
503 | + Log.v(TAG, " ShootingStatus : " + status); | |
504 | + } | |
505 | + | |
506 | + @Override | |
501 | 507 | public OLYCamera getOLYCamera() |
502 | 508 | { |
503 | 509 | return (camera); |
@@ -0,0 +1,6 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper; | |
2 | + | |
3 | +public interface IThetaSessionIdNotifier | |
4 | +{ | |
5 | + void receivedSessionId(String sessionId); | |
6 | +} |
@@ -0,0 +1,8 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper; | |
2 | + | |
3 | +import androidx.annotation.NonNull; | |
4 | + | |
5 | +public interface IThetaSessionIdProvider | |
6 | +{ | |
7 | + @NonNull String getSessionId(); | |
8 | +} |
@@ -10,19 +10,30 @@ import jp.sfjp.gokigen.a01c.liveview.CameraLiveViewListenerImpl | ||
10 | 10 | import jp.sfjp.gokigen.a01c.liveview.IAutoFocusFrameDisplay |
11 | 11 | import jp.sfjp.gokigen.a01c.liveview.ICameraStatusReceiver |
12 | 12 | import jp.sfjp.gokigen.a01c.olycamerawrapper.ICameraRunMode |
13 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl | |
13 | 14 | import jp.sfjp.gokigen.a01c.olycamerawrapper.ILevelGauge |
14 | 15 | import jp.sfjp.gokigen.a01c.olycamerawrapper.IZoomLensHolder |
15 | 16 | import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ICameraPropertyLoadSaveOperations |
16 | 17 | import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ILoadSaveCameraProperties |
17 | 18 | import jp.sfjp.gokigen.a01c.olycamerawrapper.property.IOlyCameraPropertyProvider |
19 | +import jp.sfjp.gokigen.a01c.preference.PreferenceAccessWrapper | |
20 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.connection.ThetaCameraConnection | |
21 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.liveview.ThetaLiveViewControl | |
22 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaDummyOperation | |
23 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaSingleShotControl | |
18 | 24 | |
19 | -class ThetaCameraController(val context : AppCompatActivity, val focusFrameDisplay : IAutoFocusFrameDisplay, val showInformation : IShowInformation, val receiver : ICameraStatusReceiver) : ICameraController | |
25 | +class ThetaCameraController(val context: AppCompatActivity, private val focusFrameDisplay: IAutoFocusFrameDisplay, private val showInformation: IShowInformation, private val receiver: ICameraStatusReceiver, private val preferences: PreferenceAccessWrapper) : ICameraController, IIndicatorControl | |
20 | 26 | { |
21 | - private lateinit var listener : CameraLiveViewListenerImpl | |
27 | + //private lateinit var listener : CameraLiveViewListenerImpl | |
28 | + private lateinit var liveViewControl : ThetaLiveViewControl | |
29 | + private val dummyOperation = ThetaDummyOperation() | |
30 | + private val sessionIdHolder = ThetaSessionHolder() | |
31 | + private val cameraConnection = ThetaCameraConnection(context, receiver, sessionIdHolder) | |
32 | + private val singleShot = ThetaSingleShotControl(sessionIdHolder, this, this) | |
22 | 33 | |
23 | 34 | override fun setLiveViewListener(listener: CameraLiveViewListenerImpl) |
24 | 35 | { |
25 | - this.listener = listener | |
36 | + this.liveViewControl = ThetaLiveViewControl(sessionIdHolder, listener) | |
26 | 37 | } |
27 | 38 | |
28 | 39 | override fun changeLiveViewSize(size: String?) |
@@ -31,97 +42,134 @@ class ThetaCameraController(val context : AppCompatActivity, val focusFrameDispl | ||
31 | 42 | Log.v(toString(), " changeLiveViewSize: $size") |
32 | 43 | } |
33 | 44 | |
34 | - override fun startLiveView() { | |
35 | - TODO("Not yet implemented") | |
45 | + override fun startLiveView() | |
46 | + { | |
47 | + if (::liveViewControl.isInitialized) | |
48 | + { | |
49 | + liveViewControl.startLiveView() | |
50 | + } | |
36 | 51 | } |
37 | 52 | |
38 | - override fun stopLiveView() { | |
39 | - TODO("Not yet implemented") | |
53 | + override fun stopLiveView() | |
54 | + { | |
55 | + if (::liveViewControl.isInitialized) | |
56 | + { | |
57 | + liveViewControl.stopLiveView() | |
58 | + } | |
40 | 59 | } |
41 | 60 | |
42 | - override fun updateTakeMode() { | |
43 | - TODO("Not yet implemented") | |
61 | + override fun updateTakeMode() | |
62 | + { | |
63 | + // なにもしない | |
44 | 64 | } |
45 | 65 | |
46 | - override fun driveAutoFocus(event: MotionEvent?): Boolean { | |
47 | - TODO("Not yet implemented") | |
66 | + override fun driveAutoFocus(event: MotionEvent?): Boolean | |
67 | + { | |
68 | + return (true) | |
48 | 69 | } |
49 | 70 | |
50 | - override fun unlockAutoFocus() { | |
51 | - TODO("Not yet implemented") | |
71 | + override fun unlockAutoFocus() | |
72 | + { | |
73 | + // なにもしない | |
52 | 74 | } |
53 | 75 | |
54 | - override fun isContainsAutoFocusPoint(event: MotionEvent?): Boolean { | |
55 | - TODO("Not yet implemented") | |
76 | + override fun isContainsAutoFocusPoint(event: MotionEvent?): Boolean | |
77 | + { | |
78 | + return (false) | |
56 | 79 | } |
57 | 80 | |
58 | - override fun singleShot() { | |
59 | - TODO("Not yet implemented") | |
81 | + override fun singleShot() | |
82 | + { | |
83 | + singleShot.singleShot(sessionIdHolder.isApiLevelV21()) | |
60 | 84 | } |
61 | 85 | |
62 | - override fun movieControl() { | |
63 | - TODO("Not yet implemented") | |
86 | + override fun movieControl() | |
87 | + { | |
88 | + // TODO("Not yet implemented") | |
64 | 89 | } |
65 | 90 | |
66 | - override fun bracketingShot(bracketingStyle: Int, bracketingCount: Int, durationSeconds: Int) { | |
67 | - TODO("Not yet implemented") | |
91 | + override fun bracketingShot(bracketingStyle: Int, bracketingCount: Int, durationSeconds: Int) | |
92 | + { | |
93 | + // TODO("Not yet implemented") | |
68 | 94 | } |
69 | 95 | |
70 | - override fun setRecViewMode(isRecViewMode: Boolean) { | |
71 | - TODO("Not yet implemented") | |
96 | + override fun setRecViewMode(isRecViewMode: Boolean) | |
97 | + { | |
98 | + // なにもしない | |
72 | 99 | } |
73 | 100 | |
74 | - override fun toggleAutoExposure() { | |
75 | - TODO("Not yet implemented") | |
101 | + override fun toggleAutoExposure() | |
102 | + { | |
103 | + // なにもしない | |
76 | 104 | } |
77 | 105 | |
78 | - override fun toggleManualFocus() { | |
79 | - TODO("Not yet implemented") | |
106 | + override fun toggleManualFocus() | |
107 | + { | |
108 | + // なにもしない | |
80 | 109 | } |
81 | 110 | |
82 | - override fun isManualFocus(): Boolean { | |
83 | - TODO("Not yet implemented") | |
111 | + override fun isManualFocus(): Boolean | |
112 | + { | |
113 | + return (false) | |
84 | 114 | } |
85 | 115 | |
86 | - override fun isAFLock(): Boolean { | |
87 | - TODO("Not yet implemented") | |
116 | + override fun isAFLock(): Boolean | |
117 | + { | |
118 | + return (false) | |
88 | 119 | } |
89 | 120 | |
90 | - override fun isAELock(): Boolean { | |
91 | - TODO("Not yet implemented") | |
121 | + override fun isAELock(): Boolean | |
122 | + { | |
123 | + return (false) | |
92 | 124 | } |
93 | 125 | |
94 | - override fun updateStatusAll() { | |
95 | - TODO("Not yet implemented") | |
126 | + override fun updateStatusAll() | |
127 | + { | |
128 | + // なにもしない | |
96 | 129 | } |
97 | 130 | |
98 | - override fun getCameraPropertyProvider(): IOlyCameraPropertyProvider { | |
99 | - TODO("Not yet implemented") | |
131 | + override fun getCameraPropertyProvider(): IOlyCameraPropertyProvider | |
132 | + { | |
133 | + return (dummyOperation) | |
100 | 134 | } |
101 | 135 | |
102 | - override fun getCameraPropertyLoadSaveOperations(): ICameraPropertyLoadSaveOperations { | |
103 | - TODO("Not yet implemented") | |
136 | + override fun getCameraPropertyLoadSaveOperations(): ICameraPropertyLoadSaveOperations | |
137 | + { | |
138 | + return (dummyOperation) | |
104 | 139 | } |
105 | 140 | |
106 | - override fun getLoadSaveCameraProperties(): ILoadSaveCameraProperties { | |
107 | - TODO("Not yet implemented") | |
141 | + override fun getLoadSaveCameraProperties(): ILoadSaveCameraProperties | |
142 | + { | |
143 | + return (dummyOperation) | |
108 | 144 | } |
109 | 145 | |
110 | - override fun getChangeRunModeExecutor(): ICameraRunMode { | |
111 | - TODO("Not yet implemented") | |
146 | + override fun getChangeRunModeExecutor(): ICameraRunMode | |
147 | + { | |
148 | + return (dummyOperation) | |
112 | 149 | } |
113 | 150 | |
114 | - override fun getConnectionInterface(): ICameraConnection { | |
115 | - TODO("Not yet implemented") | |
151 | + override fun getConnectionInterface(): ICameraConnection | |
152 | + { | |
153 | + return (cameraConnection) | |
116 | 154 | } |
117 | 155 | |
118 | - override fun getZoomLensHolder(): IZoomLensHolder { | |
119 | - TODO("Not yet implemented") | |
156 | + override fun getZoomLensHolder(): IZoomLensHolder | |
157 | + { | |
158 | + return (dummyOperation) | |
120 | 159 | } |
121 | 160 | |
122 | - override fun getLevelGauge(): ILevelGauge { | |
123 | - TODO("Not yet implemented") | |
161 | + override fun getLevelGauge(): ILevelGauge | |
162 | + { | |
163 | + return (dummyOperation) | |
124 | 164 | } |
125 | 165 | |
166 | + override fun onAfLockUpdate(isAfLocked: Boolean) | |
167 | + { | |
168 | + //TODO("Not yet implemented") | |
169 | + } | |
126 | 170 | |
127 | -} | |
\ No newline at end of file | ||
171 | + override fun onShootingStatusUpdate(status: IIndicatorControl.shootingStatus?) | |
172 | + { | |
173 | + //TODO("Not yet implemented") | |
174 | + } | |
175 | +} |
@@ -0,0 +1,24 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper | |
2 | + | |
3 | +class ThetaSessionHolder : IThetaSessionIdProvider, IThetaSessionIdNotifier | |
4 | +{ | |
5 | + private var sessionId : String = "" | |
6 | + | |
7 | + override fun getSessionId(): String | |
8 | + { | |
9 | + return (sessionId) | |
10 | + } | |
11 | + | |
12 | + override fun receivedSessionId(sessionId: String?) | |
13 | + { | |
14 | + if (sessionId != null) | |
15 | + { | |
16 | + this.sessionId = sessionId | |
17 | + } | |
18 | + } | |
19 | + | |
20 | + fun isApiLevelV21() : Boolean | |
21 | + { | |
22 | + return (!(sessionId.isNotEmpty())) | |
23 | + } | |
24 | +} |
@@ -0,0 +1,189 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper.connection | |
2 | + | |
3 | +import android.util.Log | |
4 | +import androidx.appcompat.app.AppCompatActivity | |
5 | +import jp.sfjp.gokigen.a01c.R | |
6 | +import jp.sfjp.gokigen.a01c.liveview.ICameraStatusReceiver | |
7 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdNotifier | |
8 | +import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient | |
9 | +import org.json.JSONObject | |
10 | + | |
11 | +class ThetaCameraConnectSequence(private val context: AppCompatActivity, private val cameraStatusReceiver: ICameraStatusReceiver, private val sessionIdNotifier: IThetaSessionIdNotifier) : Runnable | |
12 | +{ | |
13 | + private var useThetaV21 : Boolean = false | |
14 | + private val httpClient = SimpleHttpClient() | |
15 | + | |
16 | + override fun run() | |
17 | + { | |
18 | + // 使用する API Levelを決める | |
19 | + useThetaV21 = decideApiLevel() | |
20 | + try | |
21 | + { | |
22 | + Log.v(TAG,"Theta API v2.1 : $useThetaV21") | |
23 | + if (useThetaV21) | |
24 | + { | |
25 | + // API Level V2.1を使用して通信する | |
26 | + connectApiV21() | |
27 | + } | |
28 | + else | |
29 | + { | |
30 | + // API Level V2 を使用して通信する | |
31 | + connectApiV2() | |
32 | + } | |
33 | + } | |
34 | + catch (e : Exception) | |
35 | + { | |
36 | + e.printStackTrace() | |
37 | + } | |
38 | + } | |
39 | + | |
40 | + /** | |
41 | + * | |
42 | + */ | |
43 | + private fun decideApiLevel() : Boolean | |
44 | + { | |
45 | + var apiLevelIsV21 = false | |
46 | + try | |
47 | + { | |
48 | + val oscInfoUrl = "http://192.168.1.1/osc/info" | |
49 | + val timeoutMs = 5000 | |
50 | + val response: String = httpClient.httpGet(oscInfoUrl, timeoutMs) | |
51 | + Log.v(TAG, " $oscInfoUrl $response") | |
52 | + if (response.isNotEmpty()) | |
53 | + { | |
54 | + val apiLevelArray = JSONObject(response).getJSONArray("apiLevel") | |
55 | + val size = apiLevelArray.length() | |
56 | + for (index in 0 until size) | |
57 | + { | |
58 | + val api = apiLevelArray.getInt(index) | |
59 | + if (api == 2) //if (api == 2 && useThetaV21) | |
60 | + { | |
61 | + apiLevelIsV21 = true | |
62 | + } | |
63 | + } | |
64 | + } | |
65 | + } | |
66 | + catch (e: Exception) | |
67 | + { | |
68 | + e.printStackTrace() | |
69 | + } | |
70 | + return (apiLevelIsV21) | |
71 | + } | |
72 | + | |
73 | + /** | |
74 | + * | |
75 | + */ | |
76 | + private fun connectApiV2() | |
77 | + { | |
78 | + val commandsExecuteUrl = "http://192.168.1.1/osc/commands/execute" | |
79 | + val startSessionData = "{\"name\":\"camera.startSession\",\"parameters\":{\"timeout\":0}}" | |
80 | + val getStateUrl = "http://192.168.1.1/osc/state" | |
81 | + val timeoutMs = 5000 | |
82 | + try | |
83 | + { | |
84 | + val response: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, startSessionData, null, "application/json;charset=utf-8", timeoutMs) | |
85 | + Log.v(TAG, " $commandsExecuteUrl $startSessionData $response") | |
86 | + | |
87 | + val response2: String? = httpClient.httpPostWithHeader(getStateUrl, "", null, "application/json;charset=utf-8", timeoutMs) | |
88 | + Log.v(TAG, " $getStateUrl $response2") | |
89 | + if ((response2 != null)&&(response2.isNotEmpty())) | |
90 | + { | |
91 | + try | |
92 | + { | |
93 | + val jsonObject = JSONObject(response2) | |
94 | + val sessionId = jsonObject.getJSONObject("state").getString("sessionId") | |
95 | + sessionIdNotifier.receivedSessionId(sessionId) | |
96 | + onConnectNotify() | |
97 | + return | |
98 | + } | |
99 | + catch (e: Exception) | |
100 | + { | |
101 | + e.printStackTrace() | |
102 | + } | |
103 | + } | |
104 | + // 応答なし、を応答する。 | |
105 | + onConnectError(context.getString(R.string.theta_connect_response_ng)) | |
106 | + } | |
107 | + catch (e: Exception) | |
108 | + { | |
109 | + e.printStackTrace() | |
110 | + onConnectError(e.localizedMessage) | |
111 | + } | |
112 | + } | |
113 | + | |
114 | + private fun connectApiV21() { | |
115 | + val commandsExecuteUrl = "http://192.168.1.1/osc/commands/execute" | |
116 | + val startSessionData = "{\"name\":\"camera.startSession\",\"parameters\":{\"timeout\":0}}" | |
117 | + val getStateUrl = "http://192.168.1.1/osc/state" | |
118 | + val timeoutMs = 5000 | |
119 | + try { | |
120 | + val responseS: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, startSessionData, null, "application/json;charset=utf-8", timeoutMs) | |
121 | + Log.v(TAG, " [ $httpClient ] $startSessionData ::: $responseS") | |
122 | + val response: String? = httpClient.httpPostWithHeader(getStateUrl, "", null, null, timeoutMs) | |
123 | + Log.v(TAG, " ($getStateUrl) $response") | |
124 | + if ((response != null)&&(response.isNotEmpty())) | |
125 | + { | |
126 | + var apiLevel = 1 | |
127 | + var sessionId: String? = null | |
128 | + val `object` = JSONObject(response) | |
129 | + try { | |
130 | + apiLevel = `object`.getJSONObject("state").getInt("_apiVersion") | |
131 | + } catch (e: Exception) { | |
132 | + e.printStackTrace() | |
133 | + } | |
134 | + try { | |
135 | + sessionId = `object`.getJSONObject("state").getString("sessionId") | |
136 | + sessionIdNotifier.receivedSessionId(sessionId) | |
137 | + } catch (e: Exception) { | |
138 | + e.printStackTrace() | |
139 | + } | |
140 | + if (apiLevel != 2) { | |
141 | + val setApiLevelData = "{\"name\":\"camera.setOptions\",\"parameters\":{\"sessionId\" : \"$sessionId\", \"options\":{ \"clientVersion\":2}}}" | |
142 | + val response3: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, setApiLevelData, null, "application/json;charset=utf-8", timeoutMs) | |
143 | + Log.v(TAG, " $commandsExecuteUrl $setApiLevelData $response3") | |
144 | + } | |
145 | + onConnectNotify() | |
146 | + } else { | |
147 | + onConnectError(context.getString(R.string.camera_not_found)) | |
148 | + } | |
149 | + } catch (e: Exception) | |
150 | + { | |
151 | + e.printStackTrace() | |
152 | + onConnectError(e.localizedMessage) | |
153 | + } | |
154 | + } | |
155 | + | |
156 | + private fun onConnectNotify() | |
157 | + { | |
158 | + try | |
159 | + { | |
160 | + val thread = Thread { // カメラとの接続確立を通知する | |
161 | + cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_connected)) | |
162 | + cameraStatusReceiver.onCameraConnected() | |
163 | + Log.v(TAG, "onConnectNotify()") | |
164 | + } | |
165 | + thread.start() | |
166 | + } | |
167 | + catch (e: Exception) | |
168 | + { | |
169 | + e.printStackTrace() | |
170 | + } | |
171 | + } | |
172 | + | |
173 | + private fun onConnectError(reason: String?) | |
174 | + { | |
175 | + if (reason != null) | |
176 | + { | |
177 | + cameraStatusReceiver.onCameraConnectError(reason) | |
178 | + } | |
179 | + else | |
180 | + { | |
181 | + cameraStatusReceiver.onCameraConnectError("") | |
182 | + } | |
183 | + } | |
184 | + | |
185 | + companion object | |
186 | + { | |
187 | + private val TAG = ThetaCameraConnectSequence::class.java.simpleName | |
188 | + } | |
189 | +} | |
\ No newline at end of file |
@@ -0,0 +1,166 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper.connection | |
2 | + | |
3 | +import android.content.* | |
4 | +import android.net.ConnectivityManager | |
5 | +import android.net.wifi.WifiManager | |
6 | +import android.util.Log | |
7 | +import androidx.appcompat.app.AppCompatActivity | |
8 | +import jp.sfjp.gokigen.a01c.ICameraConnection | |
9 | +import jp.sfjp.gokigen.a01c.R | |
10 | +import jp.sfjp.gokigen.a01c.liveview.ICameraStatusReceiver | |
11 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdNotifier | |
12 | +import java.util.concurrent.Executor | |
13 | +import java.util.concurrent.Executors | |
14 | + | |
15 | +/** | |
16 | + * | |
17 | + */ | |
18 | +class ThetaCameraConnection(private val context: AppCompatActivity, private val statusReceiver: ICameraStatusReceiver, private val sessionIdNotifier: IThetaSessionIdNotifier) : ICameraConnection | |
19 | +{ | |
20 | + private val cameraExecutor: Executor = Executors.newFixedThreadPool(1) | |
21 | + private var connectionStatus: ICameraConnection.CameraConnectionStatus = ICameraConnection.CameraConnectionStatus.UNKNOWN | |
22 | + private var isNetworkStatusWatching : Boolean = false | |
23 | + private var connectionReceiver = object : BroadcastReceiver() | |
24 | + { | |
25 | + override fun onReceive(context: Context, intent: Intent) | |
26 | + { | |
27 | + onReceiveBroadcastOfConnection(context, intent) | |
28 | + } | |
29 | + } | |
30 | + | |
31 | + /** | |
32 | + * | |
33 | + */ | |
34 | + private fun onReceiveBroadcastOfConnection(context: Context, intent: Intent) | |
35 | + { | |
36 | + statusReceiver.onStatusNotify(context.getString(R.string.connect_check_wifi)) | |
37 | + Log.v(TAG, context.getString(R.string.connect_check_wifi)) | |
38 | + val action = intent.action | |
39 | + if (action == null) | |
40 | + { | |
41 | + Log.v(TAG, "intent.getAction() : null") | |
42 | + return | |
43 | + } | |
44 | + try | |
45 | + { | |
46 | + if (action == ConnectivityManager.CONNECTIVITY_ACTION) | |
47 | + { | |
48 | + Log.v(TAG, "onReceiveBroadcastOfConnection() : CONNECTIVITY_ACTION") | |
49 | + val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager | |
50 | + val info = wifiManager.connectionInfo | |
51 | + if ((wifiManager.isWifiEnabled)&&(info != null)) | |
52 | + { | |
53 | + connectToCamera() | |
54 | + } | |
55 | + else | |
56 | + { | |
57 | + if (info == null) | |
58 | + { | |
59 | + Log.v(TAG, "NETWORK INFO IS NULL.") | |
60 | + } | |
61 | + else | |
62 | + { | |
63 | + Log.v(TAG, "isWifiEnabled : " + wifiManager.isWifiEnabled + " NetworkId : " + info.networkId) | |
64 | + } | |
65 | + } | |
66 | + } | |
67 | + } | |
68 | + catch (e: Exception) | |
69 | + { | |
70 | + Log.w(TAG, "onReceiveBroadcastOfConnection() EXCEPTION" + e.message) | |
71 | + e.printStackTrace() | |
72 | + } | |
73 | + } | |
74 | + | |
75 | + /** | |
76 | + * | |
77 | + */ | |
78 | + override fun startWatchWifiStatus(context: Context) | |
79 | + { | |
80 | + Log.v(TAG, "startWatchWifiStatus()") | |
81 | + statusReceiver.onStatusNotify("prepare") | |
82 | + isNetworkStatusWatching = true | |
83 | + val filter = IntentFilter() | |
84 | + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) | |
85 | + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION) | |
86 | + context.registerReceiver(connectionReceiver, filter) | |
87 | + } | |
88 | + | |
89 | + /** | |
90 | + * | |
91 | + */ | |
92 | + override fun stopWatchWifiStatus(context: Context) | |
93 | + { | |
94 | + Log.v(TAG, "stopWatchWifiStatus()") | |
95 | + isNetworkStatusWatching = false | |
96 | + context.unregisterReceiver(connectionReceiver) | |
97 | + disconnect(false) | |
98 | + } | |
99 | + | |
100 | + /** | |
101 | + * | |
102 | + */ | |
103 | + override fun isWatchWifiStatus(): Boolean | |
104 | + { | |
105 | + return (isNetworkStatusWatching) | |
106 | + } | |
107 | + | |
108 | + /** | |
109 | + * | |
110 | + */ | |
111 | + override fun disconnect(powerOff: Boolean) | |
112 | + { | |
113 | + Log.v(TAG, "disconnect()") | |
114 | + disconnectFromCamera(powerOff) | |
115 | + connectionStatus = ICameraConnection.CameraConnectionStatus.DISCONNECTED | |
116 | + statusReceiver.onCameraDisconnected() | |
117 | + } | |
118 | + | |
119 | + /** | |
120 | + * | |
121 | + */ | |
122 | + override fun connect() | |
123 | + { | |
124 | + Log.v(TAG, "connect()") | |
125 | + connectToCamera() | |
126 | + } | |
127 | + | |
128 | + /** | |
129 | + * カメラとの切断処理 | |
130 | + */ | |
131 | + private fun disconnectFromCamera(powerOff: Boolean) | |
132 | + { | |
133 | + Log.v(TAG, "disconnectFromCamera() : $powerOff") | |
134 | + try | |
135 | + { | |
136 | + cameraExecutor.execute(ThetaCameraDisconnectSequence()) | |
137 | + } | |
138 | + catch (e: Exception) | |
139 | + { | |
140 | + e.printStackTrace() | |
141 | + } | |
142 | + } | |
143 | + | |
144 | + /** | |
145 | + * カメラとの接続処理 | |
146 | + */ | |
147 | + private fun connectToCamera() | |
148 | + { | |
149 | + Log.v(TAG, "connectToCamera()") | |
150 | + connectionStatus = ICameraConnection.CameraConnectionStatus.CONNECTING | |
151 | + try | |
152 | + { | |
153 | + cameraExecutor.execute(ThetaCameraConnectSequence(context, statusReceiver, sessionIdNotifier)) | |
154 | + } | |
155 | + catch (e: Exception) | |
156 | + { | |
157 | + Log.v(TAG, "connectToCamera() EXCEPTION : " + e.message) | |
158 | + e.printStackTrace() | |
159 | + } | |
160 | + } | |
161 | + | |
162 | + companion object | |
163 | + { | |
164 | + private val TAG = ThetaCameraConnection::class.java.simpleName | |
165 | + } | |
166 | +} | |
\ No newline at end of file |
@@ -0,0 +1,12 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper.connection | |
2 | + | |
3 | +import android.util.Log | |
4 | + | |
5 | +class ThetaCameraDisconnectSequence : Runnable | |
6 | +{ | |
7 | + override fun run() | |
8 | + { | |
9 | + // カメラをPowerOffして接続を切る | |
10 | + Log.v(ThetaCameraDisconnectSequence::class.java.simpleName, " Disconnect from THETA.") | |
11 | + } | |
12 | +} |
@@ -0,0 +1,110 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper.liveview | |
2 | + | |
3 | +import android.util.Log | |
4 | +import jp.sfjp.gokigen.a01c.liveview.CameraLiveViewListenerImpl | |
5 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider | |
6 | +import jp.sfjp.gokigen.a01c.utils.SimpleLiveviewSlicer | |
7 | + | |
8 | +class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvider, private val liveViewListener: CameraLiveViewListenerImpl) | |
9 | +{ | |
10 | + private var whileFetching = false | |
11 | + | |
12 | + fun startLiveView() | |
13 | + { | |
14 | + Log.v(TAG, " startLiveView()") | |
15 | + try | |
16 | + { | |
17 | + val thread = Thread { | |
18 | + try | |
19 | + { | |
20 | + | |
21 | + start(!(sessionIdProvider.sessionId.isEmpty())) | |
22 | + } | |
23 | + catch (e: Exception) | |
24 | + { | |
25 | + e.printStackTrace() | |
26 | + } | |
27 | + } | |
28 | + thread.start() | |
29 | + } | |
30 | + catch (e: Exception) | |
31 | + { | |
32 | + e.printStackTrace() | |
33 | + } | |
34 | + } | |
35 | + | |
36 | + fun stopLiveView() | |
37 | + { | |
38 | + Log.v(TAG, " stopLiveView()") | |
39 | + whileFetching = false | |
40 | + } | |
41 | + | |
42 | + private fun start(useOscV2 : Boolean) | |
43 | + { | |
44 | + if (whileFetching) | |
45 | + { | |
46 | + Log.v(TAG, "start() already starting.") | |
47 | + return | |
48 | + } | |
49 | + whileFetching = true | |
50 | + | |
51 | + try | |
52 | + { | |
53 | + val thread = Thread { | |
54 | + Log.d(TAG, "Starting retrieving streaming data from server.") | |
55 | + val slicer = SimpleLiveviewSlicer() | |
56 | + var continuousNullDataReceived = 0 | |
57 | + try | |
58 | + { | |
59 | + val streamUrl = "http://192.168.1.1/osc/commands/execute" | |
60 | + val paramData = if (useOscV2) "{\"name\":\"camera.getLivePreview\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera._getLivePreview\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.getSessionId().toString() + "\"}}" | |
61 | + Log.v(TAG, " >>>>> START THETA PREVIEW : $streamUrl $paramData") | |
62 | + | |
63 | + // Create Slicer to open the stream and parse it. | |
64 | + slicer.open(streamUrl, paramData, "application/json;charset=utf-8") | |
65 | + while (whileFetching) { | |
66 | + val payload: SimpleLiveviewSlicer.Payload? = slicer.nextPayloadForMotionJpeg() | |
67 | + if (payload == null) | |
68 | + { | |
69 | + //Log.v(TAG, "Liveview Payload is null."); | |
70 | + continuousNullDataReceived++ | |
71 | + if (continuousNullDataReceived > FETCH_ERROR_MAX) | |
72 | + { | |
73 | + Log.d(TAG, " FETCH ERROR MAX OVER ") | |
74 | + break | |
75 | + } | |
76 | + continue | |
77 | + } | |
78 | + liveViewListener.setImageData(payload.jpegData, null) | |
79 | + continuousNullDataReceived = 0 | |
80 | + } | |
81 | + } | |
82 | + catch (e: Exception) | |
83 | + { | |
84 | + e.printStackTrace() | |
85 | + } | |
86 | + finally | |
87 | + { | |
88 | + slicer.close() | |
89 | + if (whileFetching && continuousNullDataReceived > FETCH_ERROR_MAX) | |
90 | + { | |
91 | + // 再度ライブビューのスタートをやってみる。 | |
92 | + whileFetching = false | |
93 | + start(useOscV2) | |
94 | + } | |
95 | + } | |
96 | + } | |
97 | + thread.start() | |
98 | + } | |
99 | + catch (e: Exception) | |
100 | + { | |
101 | + e.printStackTrace() | |
102 | + } | |
103 | + } | |
104 | + | |
105 | + companion object | |
106 | + { | |
107 | + private val TAG = ThetaLiveViewControl::class.java.simpleName | |
108 | + private const val FETCH_ERROR_MAX = 30 | |
109 | + } | |
110 | +} |
@@ -0,0 +1,182 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation | |
2 | + | |
3 | +import android.graphics.Color | |
4 | +import jp.co.olympus.camerakit.OLYCamera | |
5 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.ICameraHardwareStatus | |
6 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.ICameraRunMode | |
7 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.ILevelGauge | |
8 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.IZoomLensHolder | |
9 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ICameraPropertyLoadSaveOperations | |
10 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ILoadSaveCameraProperties | |
11 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.property.IOlyCameraPropertyProvider | |
12 | + | |
13 | +class ThetaDummyOperation : ILevelGauge, IZoomLensHolder, ICameraRunMode, ILoadSaveCameraProperties, ICameraPropertyLoadSaveOperations, IOlyCameraPropertyProvider, ICameraHardwareStatus | |
14 | +{ | |
15 | + override fun getLevel(area: ILevelGauge.LevelArea?): Float { | |
16 | + // TODO("Not yet implemented") | |
17 | + return (1.0f) | |
18 | + } | |
19 | + | |
20 | + override fun getLevelColor(value: Float): Int { | |
21 | + // TODO("Not yet implemented") | |
22 | + return (Color.WHITE) | |
23 | + } | |
24 | + | |
25 | + override fun checkLevelGauge(camera: OLYCamera?) { | |
26 | + // TODO("Not yet implemented") | |
27 | + } | |
28 | + | |
29 | + override fun updateLevelGaugeChecking(isWatch: Boolean) { | |
30 | + //TODO("Not yet implemented") | |
31 | + } | |
32 | + | |
33 | + override fun canZoom(): Boolean { | |
34 | + //TODO("Not yet implemented") | |
35 | + return (false) | |
36 | + } | |
37 | + | |
38 | + override fun updateStatus() { | |
39 | + //TODO("Not yet implemented") | |
40 | + } | |
41 | + | |
42 | + override fun getMaximumFocalLength(): Float { | |
43 | + // TODO("Not yet implemented") | |
44 | + return (16.0f) | |
45 | + } | |
46 | + | |
47 | + override fun getActualFocalLength(): Float { | |
48 | + TODO("Not yet implemented") | |
49 | + } | |
50 | + | |
51 | + override fun inquireHardwareInformation(): MutableMap<String, Any> { | |
52 | + TODO("Not yet implemented") | |
53 | + } | |
54 | + | |
55 | + override fun getLensMountStatus(): String { | |
56 | + TODO("Not yet implemented") | |
57 | + } | |
58 | + | |
59 | + override fun getMediaMountStatus(): String { | |
60 | + TODO("Not yet implemented") | |
61 | + } | |
62 | + | |
63 | + override fun getMinimumFocalLength(): Float { | |
64 | + //TODO("Not yet implemented") | |
65 | + return (16.0f) | |
66 | + } | |
67 | + | |
68 | + override fun getCurrentFocalLength(): Float { | |
69 | + //TODO("Not yet implemented") | |
70 | + return (16.0f) | |
71 | + } | |
72 | + | |
73 | + override fun driveZoomLens(targetLength: Float) { | |
74 | + //TODO("Not yet implemented") | |
75 | + } | |
76 | + | |
77 | + override fun driveZoomLens(direction: Int) { | |
78 | + //TODO("Not yet implemented") | |
79 | + } | |
80 | + | |
81 | + override fun isDrivingZoomLens(): Boolean { | |
82 | + //TODO("Not yet implemented") | |
83 | + return (false) | |
84 | + } | |
85 | + | |
86 | + override fun getCurrentDigitalZoomScale(): Float { | |
87 | + //TODO("Not yet implemented") | |
88 | + return (1.0f) | |
89 | + } | |
90 | + | |
91 | + override fun magnifyLiveView(scale: Int): Boolean { | |
92 | + //TODO("Not yet implemented") | |
93 | + return (false) | |
94 | + } | |
95 | + | |
96 | + override fun changeDigitalZoomScale(scale: Float, isCyclic: Boolean) { | |
97 | + // TODO("Not yet implemented") | |
98 | + } | |
99 | + | |
100 | + override fun changeRunMode(isRecording: Boolean) { | |
101 | + //TODO("Not yet implemented") | |
102 | + } | |
103 | + | |
104 | + override fun isRecordingMode(): Boolean { | |
105 | + //TODO("Not yet implemented") | |
106 | + return (true) | |
107 | + } | |
108 | + | |
109 | + override fun loadCameraSettings(id: String?) { | |
110 | + //TODO("Not yet implemented") | |
111 | + } | |
112 | + | |
113 | + override fun saveCameraSettings(id: String?, name: String?) { | |
114 | + //TODO("Not yet implemented") | |
115 | + } | |
116 | + | |
117 | + override fun saveProperties(idHeader: String?, dataName: String?) { | |
118 | + //TODO("Not yet implemented") | |
119 | + } | |
120 | + | |
121 | + override fun loadProperties(idHeader: String?, dataName: String?) { | |
122 | + //TODO("Not yet implemented") | |
123 | + } | |
124 | + | |
125 | + override fun getCameraPropertyNames(): MutableSet<String> { | |
126 | + TODO("Not yet implemented") | |
127 | + } | |
128 | + | |
129 | + override fun getCameraPropertyValue(name: String?): String { | |
130 | + //TODO("Not yet implemented") | |
131 | + return ("") | |
132 | + } | |
133 | + | |
134 | + override fun getCameraPropertyValues(names: MutableSet<String>?): MutableMap<String, String> { | |
135 | + TODO("Not yet implemented") | |
136 | + } | |
137 | + | |
138 | + override fun getCameraPropertyTitle(name: String?): String { | |
139 | + TODO("Not yet implemented") | |
140 | + } | |
141 | + | |
142 | + override fun getCameraPropertyValueList(name: String?): MutableList<String> { | |
143 | + TODO("Not yet implemented") | |
144 | + } | |
145 | + | |
146 | + override fun getCameraPropertyValueTitle(propertyValue: String?): String { | |
147 | + TODO("Not yet implemented") | |
148 | + } | |
149 | + | |
150 | + override fun setCameraPropertyValue(name: String?, value: String?) { | |
151 | + TODO("Not yet implemented") | |
152 | + } | |
153 | + | |
154 | + override fun setCameraPropertyValues(values: MutableMap<String, String>?) { | |
155 | + TODO("Not yet implemented") | |
156 | + } | |
157 | + | |
158 | + override fun updateCameraPropertyUp(name: String?) { | |
159 | + TODO("Not yet implemented") | |
160 | + } | |
161 | + | |
162 | + override fun updateCameraPropertyDown(name: String?) { | |
163 | + TODO("Not yet implemented") | |
164 | + } | |
165 | + | |
166 | + override fun changeCameraProperty(name: String?, direction: Int) { | |
167 | + TODO("Not yet implemented") | |
168 | + } | |
169 | + | |
170 | + override fun canSetCameraProperty(name: String?): Boolean { | |
171 | + TODO("Not yet implemented") | |
172 | + } | |
173 | + | |
174 | + override fun isConnected(): Boolean { | |
175 | + TODO("Not yet implemented") | |
176 | + } | |
177 | + | |
178 | + override fun getHardwareStatus(): ICameraHardwareStatus { | |
179 | + TODO("Not yet implemented") | |
180 | + } | |
181 | + | |
182 | +} | |
\ No newline at end of file |
@@ -0,0 +1,141 @@ | ||
1 | +package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation | |
2 | + | |
3 | +import android.util.Log | |
4 | +import jp.sfjp.gokigen.a01c.ICameraController | |
5 | +import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl | |
6 | +import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider | |
7 | +import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient | |
8 | +import org.json.JSONObject | |
9 | + | |
10 | + | |
11 | +class ThetaSingleShotControl(private val sessionIdProvider: IThetaSessionIdProvider, private val indicator: IIndicatorControl, private val liveViewControl: ICameraController) | |
12 | +{ | |
13 | + private val httpClient = SimpleHttpClient() | |
14 | + | |
15 | + /** | |
16 | + * | |
17 | + * | |
18 | + */ | |
19 | + fun singleShot(useThetaV21 : Boolean) | |
20 | + { | |
21 | + Log.v(TAG, "singleShot()") | |
22 | + try | |
23 | + { | |
24 | + val thread = Thread { | |
25 | + try | |
26 | + { | |
27 | + val shootUrl = "http://192.168.1.1/osc/commands/execute" | |
28 | + val postData = if (useThetaV21) "{\"name\":\"camera.takePicture\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera.takePicture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\"}}" | |
29 | + val result: String? = httpClient.httpPostWithHeader(shootUrl, postData, null, "application/json;charset=utf-8", timeoutMs) | |
30 | + if ((result != null)&&(result.isNotEmpty())) | |
31 | + { | |
32 | + Log.v(TAG, " singleShot() : $result") | |
33 | + indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Starting) | |
34 | + | |
35 | + // 画像処理が終わるまで待つ | |
36 | + waitChangeStatus() | |
37 | + | |
38 | + // ライブビューのの再実行を指示する | |
39 | + indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Stopping) | |
40 | + liveViewControl.stopLiveView() | |
41 | + waitMs(300) // ちょっと待つ... | |
42 | + liveViewControl.startLiveView() | |
43 | + } | |
44 | + else | |
45 | + { | |
46 | + Log.v(TAG, "singleShot() reply is null.") | |
47 | + } | |
48 | + } | |
49 | + catch (e: Exception) | |
50 | + { | |
51 | + e.printStackTrace() | |
52 | + } | |
53 | + } | |
54 | + thread.start() | |
55 | + } | |
56 | + catch (e: Exception) | |
57 | + { | |
58 | + e.printStackTrace() | |
59 | + } | |
60 | + } | |
61 | + | |
62 | + /** | |
63 | + * 撮影状態が変わるまで待つ。 | |
64 | + * (ただし、タイムアウト時間を超えたらライブビューを再開させる) | |
65 | + */ | |
66 | + private fun waitChangeStatus() | |
67 | + { | |
68 | + val getStateUrl = "http://192.168.1.1/osc/state" | |
69 | + val maxWaitTimeoutMs = 9000 // 最大待ち時間 (単位: ms) | |
70 | + var fingerprint = "" | |
71 | + try | |
72 | + { | |
73 | + val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs) | |
74 | + if ((result != null)&&(result.isNotEmpty())) | |
75 | + { | |
76 | + val jsonObject = JSONObject(result) | |
77 | + fingerprint = jsonObject.getString("fingerprint") | |
78 | + | |
79 | + // 現在の状態(ログを出す) | |
80 | + Log.v(TAG, " $getStateUrl $result ($fingerprint)") | |
81 | + } | |
82 | + } | |
83 | + catch (e: Exception) | |
84 | + { | |
85 | + e.printStackTrace() | |
86 | + } | |
87 | + | |
88 | + try | |
89 | + { | |
90 | + val firstTime = System.currentTimeMillis() | |
91 | + var currentTime = firstTime | |
92 | + while (currentTime - firstTime < maxWaitTimeoutMs) | |
93 | + { | |
94 | + // ... 状態を見て次に進める | |
95 | + val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs) | |
96 | + if ((result != null)&&(result.isNotEmpty())) | |
97 | + { | |
98 | + val jsonObject = JSONObject(result) | |
99 | + val currentFingerprint = jsonObject.getString("fingerprint") | |
100 | + | |
101 | + // ログを出してみる | |
102 | + // Log.v(TAG, " " + getStateUrl + " ( " + result + " ) " + "(" + fingerprint + " " + current_fingerprint + ")"); | |
103 | + if (fingerprint != currentFingerprint) | |
104 | + { | |
105 | + // fingerprintが更新された! | |
106 | + break | |
107 | + } | |
108 | + Log.v(TAG, " ----- NOW PROCESSING ----- : $fingerprint") | |
109 | + } | |
110 | + waitMs(1000) | |
111 | + currentTime = System.currentTimeMillis() | |
112 | + } | |
113 | + } | |
114 | + catch (e: Exception) | |
115 | + { | |
116 | + e.printStackTrace() | |
117 | + } | |
118 | + } | |
119 | + | |
120 | + /** | |
121 | + * | |
122 | + * | |
123 | + */ | |
124 | + private fun waitMs(waitMs: Int) | |
125 | + { | |
126 | + try | |
127 | + { | |
128 | + Thread.sleep(waitMs.toLong()) | |
129 | + } | |
130 | + catch (e: Exception) | |
131 | + { | |
132 | + e.printStackTrace() | |
133 | + } | |
134 | + } | |
135 | + | |
136 | + companion object | |
137 | + { | |
138 | + private val TAG = ThetaSingleShotControl::class.java.simpleName | |
139 | + private const val timeoutMs = 6000 | |
140 | + } | |
141 | +} |
@@ -7,7 +7,7 @@ import java.io.* | ||
7 | 7 | import java.net.HttpURLConnection |
8 | 8 | import java.net.URL |
9 | 9 | |
10 | -class SimpleHttpClient() | |
10 | +class SimpleHttpClient | |
11 | 11 | { |
12 | 12 | /** |
13 | 13 | * |
@@ -4,6 +4,7 @@ package jp.sfjp.gokigen.a01c.utils; | ||
4 | 4 | import android.util.Log; |
5 | 5 | |
6 | 6 | import androidx.annotation.NonNull; |
7 | +import androidx.annotation.Nullable; | |
7 | 8 | |
8 | 9 | import java.io.ByteArrayOutputStream; |
9 | 10 | import java.io.EOFException; |
@@ -351,7 +352,7 @@ public class SimpleLiveviewSlicer | ||
351 | 352 | * |
352 | 353 | * |
353 | 354 | */ |
354 | - public Payload nextPayloadForMotionJpeg() | |
355 | + public @Nullable Payload nextPayloadForMotionJpeg() | |
355 | 356 | { |
356 | 357 | int searchIndex = 0; |
357 | 358 | int[] endmarker = { 0xff, 0xd9 }; |
@@ -74,4 +74,7 @@ | ||
74 | 74 | <string name="connection_method_opc">OPC</string> |
75 | 75 | <string name="connection_method_theta">THETA</string> |
76 | 76 | |
77 | + <string name="theta_connect_response_ng">応答がありません。</string> | |
78 | + <string name="camera_not_found">カメラ未発見</string> | |
79 | + | |
77 | 80 | </resources> |
@@ -72,4 +72,7 @@ | ||
72 | 72 | <string name="connection_method_opc">OPC</string> |
73 | 73 | <string name="connection_method_theta">THETA</string> |
74 | 74 | |
75 | + <string name="theta_connect_response_ng">Did not receive any response.</string> | |
76 | + <string name="camera_not_found">Not Found…</string> | |
77 | + | |
75 | 78 | </resources> |