Skip to content

Commit

Permalink
Improve self-timer in VideoCapture
Browse files Browse the repository at this point in the history
  • Loading branch information
osakila authored Jul 12, 2024
1 parent 9622273 commit 371844e
Show file tree
Hide file tree
Showing 25 changed files with 503 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,11 @@ fun setTimeShiftCaptureBuilderParams(call: MethodCall, builder: TimeShiftCapture
}

fun setVideoCaptureBuilderParams(call: MethodCall, builder: VideoCapture.Builder) {
call.argument<Int>("_capture_interval")?.let {
if (it >= 0) {
builder.setCheckStatusCommandInterval(it.toLong())
}
}
call.argument<String>(OptionNameEnum.MaxRecordableTime.name)?.let { enumName ->
MaxRecordableTimeEnum.values().find { it.name == enumName }?.let {
builder.setMaxRecordableTime(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler {
const val notifyIdTimeShiftProgress = 10011
const val notifyIdTimeShiftStopError = 10012
const val notifyIdTimeShiftCapturing = 10013
const val notifyIdVideoCaptureStopError = 10003
const val notifyIdLimitlessIntervalCaptureStopError = 10004
const val notifyIdLimitlessIntervalCaptureCapturing = 10005
const val notifyIdShotCountSpecifiedIntervalCaptureProgress = 10021
Expand All @@ -84,6 +83,8 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler {
const val notifyIdContinuousCaptureProgress = 10061;
const val notifyIdContinuousCaptureCapturing = 10062;
const val notifyIdPhotoCapturing = 10071
const val notifyIdVideoCaptureStopError = 10081
const val notifyIdVideoCaptureCapturing = 10082
}

fun sendNotifyEvent(id: Int, params: Map<String, Any?>) {
Expand Down Expand Up @@ -811,6 +812,13 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler {
toMessageNotifyParam(exception.message ?: exception.toString())
)
}

override fun onCapturing(status: CapturingStatusEnum) {
sendNotifyEvent(
notifyIdVideoCaptureCapturing,
toCapturingNotifyParam(status)
)
}
})
}

Expand Down
5 changes: 5 additions & 0 deletions flutter/ios/Classes/ConvertUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ func setTimeShiftCaptureBuilderParams(params: [String: Any], builder: TimeShiftC
}

func setVideoCaptureBuilderParams(params: [String: Any], builder: VideoCapture.Builder) {
if let interval = params["_capture_interval"] as? Int,
interval >= 0
{
builder.setCheckStatusCommandInterval(timeMillis: Int64(interval))
}
if let value = params[ThetaRepository.OptionNameEnum.maxrecordabletime.name] as? String {
if let enumValue = getEnumValue(values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value) {
builder.setMaxRecordableTime(time: enumValue)
Expand Down
11 changes: 9 additions & 2 deletions flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ let NOTIFY_LIVE_PREVIEW = 10001
let NOTIFY_TIME_SHIFT_PROGRESS = 10011
let NOTIFY_TIME_SHIFT_STOP_ERROR = 10012
let NOTIFY_TIME_SHIFT_CAPTURING = 10013
let NOTIFY_VIDEO_CAPTURE_STOP_ERROR = 10003
let NOTIFY_LIMITLESS_INTERVAL_CAPTURE_STOP_ERROR = 10004
let NOTIFY_LIMITLESS_INTERVAL_CAPTURE_CAPTURING = 10005
let NOTIFY_SHOT_COUNT_SPECIFIED_INTERVAL_CAPTURE_PROGRESS = 10021
Expand All @@ -25,6 +24,8 @@ let NOTIFY_BURST_CAPTURING = 10053
let NOTIFY_CONTINUOUS_PROGRESS = 10061
let NOTIFY_CONTINUOUS_CAPTURING = 10062
let NOTIFY_PHOTO_CAPTURING = 10071
let NOTIFY_VIDEO_CAPTURE_STOP_ERROR = 10081
let NOTIFY_VIDEO_CAPTURE_CAPTURING = 10082

public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
public func onListen(withArguments _: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
Expand Down Expand Up @@ -83,7 +84,9 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre

func sendNotifyEvent(id: Int, params: [String: Any]?) {
if let eventSink = eventSink {
eventSink(toNotify(id: id, params: params))
DispatchQueue.main.async {
eventSink(toNotify(id: id, params: params))
}
}
}

Expand Down Expand Up @@ -731,6 +734,10 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre
let error = exception.asError()
plugin?.sendNotifyEvent(id: NOTIFY_VIDEO_CAPTURE_STOP_ERROR, params: toMessageNotifyParam(message: error.localizedDescription))
}

func onCapturing(status: CapturingStatusEnum) {
plugin?.sendNotifyEvent(id: NOTIFY_VIDEO_CAPTURE_CAPTURING, params: toCapturingNotifyParam(value: status))
}
}
videoCapturing = videoCapture!.startCapture(
callback: Callback({ fileUrl, error in
Expand Down
13 changes: 10 additions & 3 deletions flutter/lib/capture/capture.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,13 @@ class TimeShiftCapture extends Capture {

/// Capture of Video
class VideoCapture extends Capture {
VideoCapture(super.options);
final int _interval;

VideoCapture(super.options, this._interval);

int getCheckStatusCommandInterval() {
return _interval;
}

/// Get maximum recordable time (in seconds) of the camera.
MaxRecordableTimeEnum? getMaxRecordableTime() {
Expand All @@ -164,9 +170,10 @@ class VideoCapture extends Capture {
/// Starts video capture.
VideoCapturing startCapture(void Function(String? fileUrl) onCaptureCompleted,
void Function(Exception exception) onCaptureFailed,
{void Function(Exception exception)? onStopFailed}) {
{void Function(Exception exception)? onStopFailed,
void Function(CapturingStatusEnum status)? onCapturing}) {
ThetaClientFlutterPlatform.instance
.startVideoCapture(onStopFailed)
.startVideoCapture(onStopFailed, onCapturing)
.then((value) => onCaptureCompleted(value))
.onError((error, stackTrace) => onCaptureFailed(error as Exception));
return VideoCapturing();
Expand Down
12 changes: 10 additions & 2 deletions flutter/lib/capture/capture_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ class TimeShiftCaptureBuilder extends CaptureBuilder<TimeShiftCaptureBuilder> {

/// Builder of VideoCapture
class VideoCaptureBuilder extends CaptureBuilder<VideoCaptureBuilder> {
int _interval = -1;

VideoCaptureBuilder setCheckStatusCommandInterval(int timeMillis) {
_interval = timeMillis;
return this;
}

/// Set video file format.
VideoCaptureBuilder setFileFormat(VideoFileFormatEnum fileFormat) {
_options[TagNameEnum.videoFileFormat.rawValue] = fileFormat;
Expand All @@ -196,8 +203,9 @@ class VideoCaptureBuilder extends CaptureBuilder<VideoCaptureBuilder> {
Future<VideoCapture> build() async {
var completer = Completer<VideoCapture>();
try {
await ThetaClientFlutterPlatform.instance.buildVideoCapture(_options);
completer.complete(VideoCapture(_options));
await ThetaClientFlutterPlatform.instance
.buildVideoCapture(_options, _interval);
completer.complete(VideoCapture(_options, _interval));
} catch (e) {
completer.completeError(e);
}
Expand Down
27 changes: 22 additions & 5 deletions flutter/lib/theta_client_flutter_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const notifyIdLivePreview = 10001;
const notifyIdTimeShiftProgress = 10011;
const notifyIdTimeShiftStopError = 10012;
const notifyIdTimeShiftCapturing = 10013;
const notifyIdVideoCaptureStopError = 10003;
const notifyIdLimitlessIntervalCaptureStopError = 10004;
const notifyIdLimitlessIntervalCaptureCapturing = 10005;
const notifyIdShotCountSpecifiedIntervalCaptureProgress = 10021;
Expand All @@ -29,6 +28,8 @@ const notifyIdBurstCaptureCapturing = 10053;
const notifyIdContinuousCaptureProgress = 10061;
const notifyIdContinuousCaptureCapturing = 10062;
const notifyIdPhotoCapturing = 10071;
const notifyIdVideoCaptureStopError = 10081;
const notifyIdVideoCaptureCapturing = 10082;

/// An implementation of [ThetaClientFlutterPlatform] that uses method channels.
class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform {
Expand Down Expand Up @@ -349,14 +350,17 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform {
}

@override
Future<void> buildVideoCapture(Map<String, dynamic> options) async {
return methodChannel.invokeMethod<void>(
'buildVideoCapture', ConvertUtils.convertCaptureParams(options));
Future<void> buildVideoCapture(
Map<String, dynamic> options, int interval) async {
final params = ConvertUtils.convertCaptureParams(options);
params['_capture_interval'] = interval;
return methodChannel.invokeMethod<void>('buildVideoCapture', params);
}

@override
Future<String?> startVideoCapture(
void Function(Exception exception)? onStopFailed) async {
void Function(Exception exception)? onStopFailed,
void Function(CapturingStatusEnum status)? onCapturing) async {
var completer = Completer<String?>();
try {
enableNotifyEventReceiver();
Expand All @@ -368,12 +372,25 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform {
}
});
}
if (onCapturing != null) {
addNotify(notifyIdVideoCaptureCapturing, (params) {
final strStatus = params?['status'] as String?;
if (strStatus != null) {
final status = CapturingStatusEnum.getValue(strStatus);
if (status != null) {
onCapturing(status);
}
}
});
}
final fileUrl =
await methodChannel.invokeMethod<String>('startVideoCapture');
removeNotify(notifyIdVideoCaptureStopError);
removeNotify(notifyIdVideoCaptureCapturing);
completer.complete(fileUrl);
} catch (e) {
removeNotify(notifyIdVideoCaptureStopError);
removeNotify(notifyIdVideoCaptureCapturing);
completer.completeError(e);
}
return completer.future;
Expand Down
5 changes: 3 additions & 2 deletions flutter/lib/theta_client_flutter_platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,13 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface {
'getVideoCaptureBuilder() has not been implemented.');
}

Future<void> buildVideoCapture(Map<String, dynamic> options) {
Future<void> buildVideoCapture(Map<String, dynamic> options, int interval) {
throw UnimplementedError('buildVideoCapture() has not been implemented.');
}

Future<String?> startVideoCapture(
void Function(Exception exception)? onStopFailed) {
void Function(Exception exception)? onStopFailed,
void Function(CapturingStatusEnum status)? onCapturing) {
throw UnimplementedError('startVideoCapture() has not been implemented.');
}

Expand Down
64 changes: 62 additions & 2 deletions flutter/test/capture/video_capture_method_channel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void main() {

return Future.value();
});
await platform.buildVideoCapture(options);
await platform.buildVideoCapture(options, 1);
});

test('startVideoCapture', () async {
Expand All @@ -77,6 +77,66 @@ void main() {
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
return fileUrl;
});
expect(await platform.startVideoCapture(null), fileUrl);
expect(await platform.startVideoCapture(null, null), fileUrl);
});

test('call onStopFailed', () async {
const fileUrl =
'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4';
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
expect(platform.notifyList.containsKey(10081), true,
reason: 'add notify stop error');

// native event
platform.onNotify({
'id': 10081,
'params': {
'message': "stop error",
},
});

return fileUrl;
});

var isOnStopFailed = false;
expect(
await platform.startVideoCapture((exception) {
isOnStopFailed = true;
}, null),
fileUrl);
expect(platform.notifyList.containsKey(10081), false,
reason: 'remove notify stop error');
expect(isOnStopFailed, true);
});

test('call onCapturing', () async {
const fileUrl =
'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4';
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
expect(platform.notifyList.containsKey(10082), true,
reason: 'add notify capturing status');

// native event
platform.onNotify({
'id': 10082,
'params': {
'status': 'SELF_TIMER_COUNTDOWN',
},
});

return fileUrl;
});

CapturingStatusEnum? lastStatus;
expect(
await platform.startVideoCapture(null, (status) {
lastStatus = status;
}),
fileUrl);
expect(platform.notifyList.containsKey(10082), false,
reason: 'remove notify capturing status');
expect(lastStatus, CapturingStatusEnum.selfTimerCountdown);
});
}
Loading

0 comments on commit 371844e

Please sign in to comment.