From 2552bdcedb0c0b06630bc9f746ae75fe7fb870ee Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Mon, 1 Apr 2024 00:16:10 +0530 Subject: [PATCH 01/68] [camera/camera_web] Supporting camera stream on web --- packages/camera/camera_web/CHANGELOG.md | 81 ++++++++++--------- .../example/integration_test/camera_test.dart | 30 +++++++ .../camera/camera_web/lib/src/camera.dart | 26 ++++++ .../camera_web/lib/src/camera_service.dart | 75 +++++++++++++++++ .../camera/camera_web/lib/src/camera_web.dart | 15 ++++ 5 files changed, 187 insertions(+), 40 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index fe5ec3a9d1d..ccbc9f8a9cf 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,113 +1,114 @@ -## NEXT +## 0.3.3 -* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +- Supporting camera stream on web. +- Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. ## 0.3.5 -* Migrates to package:web to support WASM -* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +- Migrates to package:web to support WASM +- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.3.4 -* Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at +- Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at the app-facing package level, and is deprecated at the platform interface level. -* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. +- Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 0.3.3 -* Adds support to control video FPS and bitrate. See `CameraController.withSettings`. -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +- Adds support to control video FPS and bitrate. See `CameraController.withSettings`. +- Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.3.2+4 -* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. -* Fixes new lint warnings. +- Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. +- Fixes new lint warnings. ## 0.3.2+3 -* Migrates to `dart:ui_web` APIs. -* Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. +- Migrates to `dart:ui_web` APIs. +- Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. ## 0.3.2+2 -* Adds pub topics to package metadata. -* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. +- Adds pub topics to package metadata. +- Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. ## 0.3.2+1 -* Updates README to improve example of `Image` creation. +- Updates README to improve example of `Image` creation. ## 0.3.2 -* Changes `availableCameras` to not ask for the microphone permission. +- Changes `availableCameras` to not ask for the microphone permission. ## 0.3.1+4 -* Removes obsolete null checks on non-nullable values. -* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +- Removes obsolete null checks on non-nullable values. +- Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 0.3.1+3 -* Clarifies explanation of endorsement in README. -* Aligns Dart and Flutter SDK constraints. +- Clarifies explanation of endorsement in README. +- Aligns Dart and Flutter SDK constraints. ## 0.3.1+2 -* Updates links for the merge of flutter/plugins into flutter/packages. -* Updates minimum Flutter version to 3.0. +- Updates links for the merge of flutter/plugins into flutter/packages. +- Updates minimum Flutter version to 3.0. ## 0.3.1+1 -* Updates code for stricter lint checks. +- Updates code for stricter lint checks. ## 0.3.1 -* Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). +- Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). ## 0.3.0+1 -* Updates imports for `prefer_relative_imports`. -* Updates minimum Flutter version to 2.10. -* Fixes avoid_redundant_argument_values lint warnings and minor typos. -* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +- Updates imports for `prefer_relative_imports`. +- Updates minimum Flutter version to 2.10. +- Fixes avoid_redundant_argument_values lint warnings and minor typos. +- Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.3.0 -* **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. +- **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. ## 0.2.1+6 -* Minor fixes for new analysis options. +- Minor fixes for new analysis options. ## 0.2.1+5 -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors +- Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors lint warnings. ## 0.2.1+4 -* Migrates from `ui.hash*` to `Object.hash*`. -* Updates minimum Flutter version for changes in 0.2.1+3. +- Migrates from `ui.hash*` to `Object.hash*`. +- Updates minimum Flutter version for changes in 0.2.1+3. ## 0.2.1+3 -* Internal code cleanup for stricter analysis options. +- Internal code cleanup for stricter analysis options. ## 0.2.1+2 -* Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. -* Implemented support for new Dart SDKs with an async requestFullscreen API. +- Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. +- Implemented support for new Dart SDKs with an async requestFullscreen API. ## 0.2.1+1 -* Update usage documentation. +- Update usage documentation. ## 0.2.1 -* Add video recording functionality. -* Fix cameraNotReadable error that prevented access to the camera on some Android devices. +- Add video recording functionality. +- Fix cameraNotReadable error that prevented access to the camera on some Android devices. ## 0.2.0 -* Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. +- Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index e953a06b0e8..f0ceb392a90 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1704,5 +1704,35 @@ void main() { }); }); }); + group('cameraFrameStream', () { + testWidgets( + 'bytes is a multiple of 4', + (WidgetTester tester) async { + final MockVideoElement videoElement = MockVideoElement(); + final Camera camera = Camera( + textureId: textureId, + cameraService: cameraService, + )..videoElement = videoElement; + + when(() => videoElement.srcObject).thenAnswer( + (_) => mediaStream, + ); + + final CameraImageData cameraImageData = + await camera.cameraFrameStream().first; + expect( + cameraImageData, + equals( + isA().having( + (CameraImageData e) => e.planes.first.bytes.length % 4, + 'bytes', + equals(0), + ), + ), + ); + }, + timeout: const Timeout(Duration(seconds: 1)), + ); + }); }); } diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 124f595fecf..22fc490edf5 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -638,4 +638,30 @@ class Camera { ..height = '100%' ..objectFit = 'cover'; } + + late final StreamController _cameraFrameStreamController = + StreamController.broadcast(); + + /// Returns a stream of camera frames. + /// + /// To stop listening to new animation frames close all listening streams. + Stream cameraFrameStream({ + CameraImageStreamOptions? options, + }) { + final Stream stream = _cameraFrameStreamController.stream; + window!.requestAnimationFrame(_onAnimationFrame); + return stream; + } + + /// Called when a new animation frame is available. + Future _onAnimationFrame([num? _]) async { + final CameraImageData? image = await _cameraService.takeFrame(videoElement); + if (image != null) { + _cameraFrameStreamController.add(image); + } + + if (_cameraFrameStreamController.hasListener) { + window!.requestAnimationFrame(_onAnimationFrame); + } + } } diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 072fe06859f..1ee90aa9491 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:js_interop'; +import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; @@ -363,4 +364,78 @@ class CameraService { return DeviceOrientation.portraitUp; } } + + /// Maps a [Uint] to a [CameraImageData]. + CameraImageData getCameraImageDataFromBytes( + Uint8List bytes, { + required int height, + required int width, + }) { + return CameraImageData( + format: const CameraImageFormat( + ImageFormatGroup.jpeg, + raw: '', + ), + planes: [ + CameraImagePlane( + bytes: bytes, + bytesPerRow: 0, + ), + ], + height: height, + width: width, + ); + } + + ///Returns frame at a specific time using video element + Future takeFrame(html.VideoElement videoElement) async { + if (!(videoElement.isConnected ?? false)) { + return null; + } + + final int videoWidth = videoElement.videoWidth; + final int videoHeight = videoElement.videoHeight; + final List widthPx = videoElement.style.width.split('px'); + final List heightPx = videoElement.style.height.split('px'); + final String widthString = + widthPx.isNotEmpty ? widthPx.first : '$videoWidth'; + final String heightString = + heightPx.isNotEmpty ? heightPx.first : '$videoHeight'; + final int width = int.tryParse(widthString) ?? videoWidth; + final int height = int.tryParse(heightString) ?? videoHeight; + + final bool canUseOffscreenCanvas = + JsUtil().hasProperty(html.window, 'OffscreenCanvas'); + late html.ImageData imageData; + if (canUseOffscreenCanvas) { + final html.OffscreenCanvas canvas = html.OffscreenCanvas(width, height); + final html.OffscreenCanvasRenderingContext2D context = + canvas.getContext('2d')! as html.OffscreenCanvasRenderingContext2D; + context.drawImage(videoElement, 0, 0, width, height); + imageData = context.getImageData(0, 0, width, height); + } else { + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D context = canvas.context2D; + context.drawImageScaled(videoElement, 0, 0, width, height); + imageData = context.getImageData(0, 0, width, height); + canvas.remove(); + } + final ByteBuffer byteBuffer = imageData.data.buffer; + + return CameraImageData( + format: const CameraImageFormat( + ImageFormatGroup.jpeg, + raw: '', + ), + planes: [ + CameraImagePlane( + bytes: byteBuffer.asUint8List(), + bytesPerRow: 0, + ), + ], + height: imageData.height, + width: imageData.width, + ); + } } diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 11d14316971..88f75284ed3 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -633,6 +633,21 @@ class CameraPlugin extends CameraPlatform { } } + @override + Stream onStreamedFrameAvailable( + int cameraId, { + CameraImageStreamOptions? options, + }) { + try { + return getCamera(cameraId).cameraFrameStream(options: options); + } on html.DomException catch (e) { + throw PlatformException(code: e.name, message: e.message); + } on CameraWebException catch (e) { + _addCameraErrorEvent(e); + throw PlatformException(code: e.code.toString(), message: e.description); + } + } + @override Future pausePreview(int cameraId) async { try { From 72a7127a3e9906d5c4eca9ac60456dc77d3580ea Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Mon, 22 Apr 2024 20:19:53 +0530 Subject: [PATCH 02/68] [camera_web] Version and Changelog updated --- packages/camera/camera_web/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index ccbc9f8a9cf..8262407ddbb 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.3+1 + +- Supporting camera stream on web. + ## 0.3.3 - Supporting camera stream on web. From d366566f43710dbed74c4b5eb2bf78e779766544 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 23 Apr 2024 16:41:12 +0530 Subject: [PATCH 03/68] [camera_web] - integration test updated: mock videoElement initialized before expecting mock response --- .../camera_web/example/integration_test/camera_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index f0ceb392a90..c330e898d51 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1713,11 +1713,10 @@ void main() { textureId: textureId, cameraService: cameraService, )..videoElement = videoElement; - when(() => videoElement.srcObject).thenAnswer( (_) => mediaStream, ); - + await camera.initialize(); final CameraImageData cameraImageData = await camera.cameraFrameStream().first; expect( From 4a18e226b995c39bec83ca1043457269318b8cde Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 23 Apr 2024 16:41:12 +0530 Subject: [PATCH 04/68] [camera_web] - integration test updated: mock videoElement initialized before expecting mock response From b12f7e7d95a905d2914b5d93a8b3da71a6ea222e Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:28:37 +0530 Subject: [PATCH 05/68] [camera_web] camera service takeFrame with better exception handelling --- .../camera/camera_web/lib/src/camera.dart | 7 ++-- .../camera_web/lib/src/camera_service.dart | 36 +++++++------------ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 22fc490edf5..0d19599fb99 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -655,11 +655,8 @@ class Camera { /// Called when a new animation frame is available. Future _onAnimationFrame([num? _]) async { - final CameraImageData? image = await _cameraService.takeFrame(videoElement); - if (image != null) { - _cameraFrameStreamController.add(image); - } - + final CameraImageData image = _cameraService.takeFrame(videoElement); + _cameraFrameStreamController.add(image); if (_cameraFrameStreamController.hasListener) { window!.requestAnimationFrame(_onAnimationFrame); } diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 1ee90aa9491..01930225d65 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -388,24 +388,23 @@ class CameraService { } ///Returns frame at a specific time using video element - Future takeFrame(html.VideoElement videoElement) async { - if (!(videoElement.isConnected ?? false)) { - return null; - } - - final int videoWidth = videoElement.videoWidth; - final int videoHeight = videoElement.videoHeight; + CameraImageData takeFrame(html.VideoElement videoElement) { final List widthPx = videoElement.style.width.split('px'); final List heightPx = videoElement.style.height.split('px'); final String widthString = - widthPx.isNotEmpty ? widthPx.first : '$videoWidth'; + widthPx.isNotEmpty ? widthPx.first : '${videoElement.videoWidth}'; final String heightString = - heightPx.isNotEmpty ? heightPx.first : '$videoHeight'; - final int width = int.tryParse(widthString) ?? videoWidth; - final int height = int.tryParse(heightString) ?? videoHeight; + heightPx.isNotEmpty ? heightPx.first : '${videoElement.videoHeight}'; + final int width = int.tryParse(widthString) ?? videoElement.videoWidth; + final int height = int.tryParse(heightString) ?? videoElement.videoHeight; + if (width == 0 || height == 0) { + throw Exception( + 'Computed dimensions are zero: width=$width, height=$height', + ); + } final bool canUseOffscreenCanvas = - JsUtil().hasProperty(html.window, 'OffscreenCanvas'); + jsUtil.hasProperty(window!, 'OffscreenCanvas'); late html.ImageData imageData; if (canUseOffscreenCanvas) { final html.OffscreenCanvas canvas = html.OffscreenCanvas(width, height); @@ -423,17 +422,8 @@ class CameraService { } final ByteBuffer byteBuffer = imageData.data.buffer; - return CameraImageData( - format: const CameraImageFormat( - ImageFormatGroup.jpeg, - raw: '', - ), - planes: [ - CameraImagePlane( - bytes: byteBuffer.asUint8List(), - bytesPerRow: 0, - ), - ], + return getCameraImageDataFromBytes( + byteBuffer.asUint8List(), height: imageData.height, width: imageData.width, ); From 8697046382b27531c7e5abc7b096dd11d093fea8 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 23 Apr 2024 16:41:12 +0530 Subject: [PATCH 06/68] [camera_web] - integration test updated: mock videoElement initialized before expecting mock response From b42fbb9485474bd85638d2f492aecf6fc919a77d Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:28:37 +0530 Subject: [PATCH 07/68] [camera_web] camera service takeFrame with better exception handelling From b7b9342db2edc0f1a64eeb355dace2f1000ae678 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:29:34 +0530 Subject: [PATCH 08/68] [camera_web] tested takeFrame camera service function --- .../integration_test/camera_service_test.dart | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 2ed0c54e633..1a86d9d5a9c 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -4,6 +4,7 @@ // ignore_for_file: only_throw_errors +import 'dart:async'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; @@ -903,5 +904,31 @@ void main() { ); }); }); + + group('takeFrame', () { + testWidgets( + 'returns Camera Image of Size ' + 'when videoElement is of Size', + (WidgetTester widgetTester) async { + const Size size = Size(10, 10); + final Completer completer = Completer(); + final VideoElement videoElement = getVideoElementWithBlankStream(size) + ..onLoadedMetadata.listen((_) { + completer.complete(); + }) + ..load(); + await completer.future; + final CameraImageData cameraImageData = + cameraService.takeFrame(videoElement); + expect( + size, + Size( + cameraImageData.width.toDouble(), + cameraImageData.height.toDouble(), + ), + ); + }, + ); + }); }); } From 8ec3a0476f13eef4cd1ed2f4a3332fbb3518f6c9 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 23 Apr 2024 16:41:12 +0530 Subject: [PATCH 09/68] [camera_web] - integration test updated: mock videoElement initialized before expecting mock response From 622841c606851e48f76e909ad287d156ec065d0c Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:28:37 +0530 Subject: [PATCH 10/68] [camera_web] camera service takeFrame with better exception handelling From 096ac4df74757dd81c9949ef43360d37b48bede4 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:29:34 +0530 Subject: [PATCH 11/68] [camera_web] tested takeFrame camera service function --- .../example/integration_test/camera_service_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 1a86d9d5a9c..ef57e0ff58b 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; +import 'dart:html'; +import 'dart:js_util' as js_util; // ignore_for_file: only_throw_errors import 'dart:async'; From ed6f0865af3d8fec4840bdfbd56d87c4c7ca6f1b Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:30:06 +0530 Subject: [PATCH 12/68] [camera_web] tested cameraFrameStream --- .../example/integration_test/camera_test.dart | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index c330e898d51..8c69111abc6 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; import 'dart:ui'; @@ -1708,15 +1709,33 @@ void main() { testWidgets( 'bytes is a multiple of 4', (WidgetTester tester) async { - final MockVideoElement videoElement = MockVideoElement(); + final VideoElement videoElement = getVideoElementWithBlankStream( + const Size(10, 10), + ); + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, )..videoElement = videoElement; - when(() => videoElement.srcObject).thenAnswer( - (_) => mediaStream, + + when( + () => cameraService.takeFrame(videoElement), + ).thenAnswer( + (_) => CameraImageData( + format: const CameraImageFormat( + ImageFormatGroup.jpeg, + raw: '', + ), + planes: [ + CameraImagePlane( + bytes: Uint8List(32), + bytesPerRow: 0, + ), + ], + height: 10, + width: 10, + ), ); - await camera.initialize(); final CameraImageData cameraImageData = await camera.cameraFrameStream().first; expect( @@ -1730,7 +1749,7 @@ void main() { ), ); }, - timeout: const Timeout(Duration(seconds: 1)), + timeout: const Timeout(Duration(seconds: 2)), ); }); }); From d1244a044aeca263ce213f5d65da05efbb6f7d43 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 23 Apr 2024 16:41:12 +0530 Subject: [PATCH 13/68] [camera_web] - integration test updated: mock videoElement initialized before expecting mock response From d891a36a1d32091502ed572139dc963d6d6a8507 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:28:37 +0530 Subject: [PATCH 14/68] [camera_web] camera service takeFrame with better exception handelling From 7dfb194bb66d9528afb00831862084bc7d5f3cd0 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:29:34 +0530 Subject: [PATCH 15/68] [camera_web] tested takeFrame camera service function From b9111cbaa33185e51e61d7c3a18386a95d0e9132 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:30:06 +0530 Subject: [PATCH 16/68] [camera_web] tested cameraFrameStream From f80cb033f23c33507896d17d386084fdfb8e5bf2 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:55:32 +0530 Subject: [PATCH 17/68] [camera_web] platform version updated according to guidelines --- packages/camera/camera_web/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 8262407ddbb..1aa7202aefe 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,6 +1,6 @@ -## 0.3.3+1 +## 0.3.4 -- Supporting camera stream on web. +- Supporting camera image stream on web. ## 0.3.3 From 41ac24267ae0a189801ddeb66d614afe9cd6a132 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 23 Apr 2024 16:41:12 +0530 Subject: [PATCH 18/68] [camera_web] - integration test updated: mock videoElement initialized before expecting mock response From 3edea8c57fc830ccd6125ff82db8a855f65fed5d Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:28:37 +0530 Subject: [PATCH 19/68] [camera_web] camera service takeFrame with better exception handelling From d1e4d952e2769d809216093af370b969b22f41fd Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:29:34 +0530 Subject: [PATCH 20/68] [camera_web] tested takeFrame camera service function From 86da719ee9b69fd0d462ee5f9bbb245fe5eafa16 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:30:06 +0530 Subject: [PATCH 21/68] [camera_web] tested cameraFrameStream From dcf48f9fc174399ef225013bfca893558ef5df05 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 May 2024 12:55:32 +0530 Subject: [PATCH 22/68] [camera_web] platform version updated according to guidelines --- packages/camera/camera_web/CHANGELOG.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 1aa7202aefe..a63d80460b0 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -4,23 +4,6 @@ ## 0.3.3 -- Supporting camera stream on web. -- Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. - -## 0.3.5 - -- Migrates to package:web to support WASM -- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. - -## 0.3.4 - -- Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at - the app-facing package level, and is deprecated at the platform interface - level. -- Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. - -## 0.3.3 - - Adds support to control video FPS and bitrate. See `CameraController.withSettings`. - Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. From 7e012440ef5e5135f4c6c3cb3d6ae8aa7aa8606a Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Thu, 23 May 2024 13:32:04 +0530 Subject: [PATCH 23/68] style: removed late from late final_cameraFrameStreamController --- packages/camera/camera_web/lib/src/camera.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 0d19599fb99..d254081e938 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -639,7 +639,7 @@ class Camera { ..objectFit = 'cover'; } - late final StreamController _cameraFrameStreamController = + final StreamController _cameraFrameStreamController = StreamController.broadcast(); /// Returns a stream of camera frames. From 6d20e3825b13f7721817c16b9dc97a65f4dd4109 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Thu, 23 May 2024 18:06:05 +0530 Subject: [PATCH 24/68] refactor: cameraFrameStream handles looping of animation frames --- .../camera/camera_web/lib/src/camera.dart | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index d254081e938..98aa7b1b14b 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -648,17 +648,50 @@ class Camera { Stream cameraFrameStream({ CameraImageStreamOptions? options, }) { - final Stream stream = _cameraFrameStreamController.stream; - window!.requestAnimationFrame(_onAnimationFrame); - return stream; + ///Used to cancel the frame animationFrame + int? animationFrameId; + + bool isRequestingFrames = true; + + /// Requests animation frames in a loop as long as + /// the [isRequestingFrames] is set. + void requestFramesLoop() { + Future requestNextFrame() async { + while (isRequestingFrames) { + await Future(() { + final Completer completer = Completer(); + window!.requestAnimationFrame((num frameId) { + animationFrameId = frameId.toInt(); + if (isRequestingFrames) { + _addCameraImageDataEvent(); + } + completer.complete(); + }); + return completer.future; + }); + } + } + + requestNextFrame(); + } + + _cameraFrameStreamController + ..onListen = () { + requestFramesLoop(); + } + ..onCancel = () { + isRequestingFrames = false; + if (animationFrameId != null) { + window!.cancelAnimationFrame(animationFrameId!); + } + }; + + return _cameraFrameStreamController.stream; } - /// Called when a new animation frame is available. - Future _onAnimationFrame([num? _]) async { + /// Used to trigger add event of camera image data in camera frame stream + void _addCameraImageDataEvent() { final CameraImageData image = _cameraService.takeFrame(videoElement); _cameraFrameStreamController.add(image); - if (_cameraFrameStreamController.hasListener) { - window!.requestAnimationFrame(_onAnimationFrame); - } } } From f56879e3edd270fe1f7f9de987ee60790df2e131 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Thu, 23 May 2024 18:18:37 +0530 Subject: [PATCH 25/68] refactor: saperated _triggerAnimationFramesLoop from cameraFrameStream, and used window.animationFrames --- .../camera/camera_web/lib/src/camera.dart | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 98aa7b1b14b..3b37dd2ee3e 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -648,45 +648,24 @@ class Camera { Stream cameraFrameStream({ CameraImageStreamOptions? options, }) { - ///Used to cancel the frame animationFrame - int? animationFrameId; - - bool isRequestingFrames = true; - - /// Requests animation frames in a loop as long as - /// the [isRequestingFrames] is set. - void requestFramesLoop() { - Future requestNextFrame() async { - while (isRequestingFrames) { - await Future(() { - final Completer completer = Completer(); - window!.requestAnimationFrame((num frameId) { - animationFrameId = frameId.toInt(); - if (isRequestingFrames) { - _addCameraImageDataEvent(); - } - completer.complete(); - }); - return completer.future; - }); - } - } + _cameraFrameStreamController.onListen = () { + _triggerAnimationFramesLoop(_addCameraImageDataEvent); + }; - requestNextFrame(); - } + return _cameraFrameStreamController.stream; + } - _cameraFrameStreamController - ..onListen = () { - requestFramesLoop(); + /// Triggers animation frames in a loop as long as + /// [_cameraFrameStreamController.hasListener] and executes the callback + void _triggerAnimationFramesLoop(VoidCallback callback) { + Future triggerNextAnimationFrame() async { + while (_cameraFrameStreamController.hasListener) { + await window!.animationFrame; + callback(); } - ..onCancel = () { - isRequestingFrames = false; - if (animationFrameId != null) { - window!.cancelAnimationFrame(animationFrameId!); - } - }; + } - return _cameraFrameStreamController.stream; + triggerNextAnimationFrame(); } /// Used to trigger add event of camera image data in camera frame stream From 1e7c869221b5ff4617d3de74c548ed8a15b3f8eb Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 26 May 2024 13:43:02 +0530 Subject: [PATCH 26/68] chore: can use off screen canvas constant --- .../integration_test/camera_service_test.dart | 37 +++++++++++++++++-- .../example/integration_test/camera_test.dart | 10 ++++- .../camera/camera_web/lib/src/camera.dart | 11 +++++- .../camera_web/lib/src/camera_service.dart | 12 ++++-- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index ef57e0ff58b..c85809f56b1 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -908,7 +908,36 @@ void main() { }); }); - group('takeFrame', () { + group('camera image stream', () { + setUp( + () { + cameraService.jsUtil = jsUtil; + }, + ); + testWidgets( + 'returns true if broswer has OffscreenCanvas ' + 'otherwise false', + (WidgetTester widgetTester) async { + when( + () => jsUtil.hasProperty(window, 'OffscreenCanvas'), + ).thenReturn(true); + final bool hasOffScreenCanvas = + cameraService.hasPropertyOffScreenCanvas(); + expect( + hasOffScreenCanvas, + true, + ); + when( + () => jsUtil.hasProperty(window, 'OffscreenCanvas'), + ).thenReturn(false); + final bool hasNotOffScreenCanvas = + cameraService.hasPropertyOffScreenCanvas(); + expect( + hasNotOffScreenCanvas, + false, + ); + }, + ); testWidgets( 'returns Camera Image of Size ' 'when videoElement is of Size', @@ -921,8 +950,10 @@ void main() { }) ..load(); await completer.future; - final CameraImageData cameraImageData = - cameraService.takeFrame(videoElement); + final CameraImageData cameraImageData = cameraService.takeFrame( + videoElement, + canUseOffscreenCanvas: true, + ); expect( size, Size( diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 8c69111abc6..bdf87901b51 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -62,6 +62,10 @@ void main() { cameraId: any(named: 'cameraId'), ), ).thenAnswer((_) => Future.value(mediaStream)); + + when( + () => cameraService.hasPropertyOffScreenCanvas(), + ).thenAnswer((_) => true); }); setUpAll(() { @@ -1719,7 +1723,10 @@ void main() { )..videoElement = videoElement; when( - () => cameraService.takeFrame(videoElement), + () => cameraService.takeFrame( + videoElement, + canUseOffscreenCanvas: camera.canUseOffscreenCanvas, + ), ).thenAnswer( (_) => CameraImageData( format: const CameraImageFormat( @@ -1736,6 +1743,7 @@ void main() { width: 10, ), ); + final CameraImageData cameraImageData = await camera.cameraFrameStream().first; expect( diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 3b37dd2ee3e..e4bc4a752aa 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -159,6 +159,10 @@ class Camera { final StreamController videoRecorderController = StreamController.broadcast(); + /// Used to check if allowed to paint canvas off screen + @visibleForTesting + bool canUseOffscreenCanvas = false; + /// Initializes the camera stream displayed in the [videoElement]. /// Registers the camera view with [textureId] under [_getViewType] type. /// Emits the camera default video track on the [onEnded] stream when it ends. @@ -200,6 +204,8 @@ class Camera { onEndedController.add(defaultVideoTrack); }); } + + canUseOffscreenCanvas = _cameraService.hasPropertyOffScreenCanvas(); } /// Starts the camera stream. @@ -670,7 +676,10 @@ class Camera { /// Used to trigger add event of camera image data in camera frame stream void _addCameraImageDataEvent() { - final CameraImageData image = _cameraService.takeFrame(videoElement); + final CameraImageData image = _cameraService.takeFrame( + videoElement, + canUseOffscreenCanvas: canUseOffscreenCanvas, + ); _cameraFrameStreamController.add(image); } } diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 01930225d65..c7e760b5cf3 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -387,8 +387,16 @@ class CameraService { ); } + ///Used to check if browser has OffscreenCanvas capability + bool hasPropertyOffScreenCanvas() { + return jsUtil.hasProperty(window!, 'OffscreenCanvas'); + } + ///Returns frame at a specific time using video element - CameraImageData takeFrame(html.VideoElement videoElement) { + CameraImageData takeFrame( + html.VideoElement videoElement, { + bool canUseOffscreenCanvas = false, + }) { final List widthPx = videoElement.style.width.split('px'); final List heightPx = videoElement.style.height.split('px'); final String widthString = @@ -403,8 +411,6 @@ class CameraService { 'Computed dimensions are zero: width=$width, height=$height', ); } - final bool canUseOffscreenCanvas = - jsUtil.hasProperty(window!, 'OffscreenCanvas'); late html.ImageData imageData; if (canUseOffscreenCanvas) { final html.OffscreenCanvas canvas = html.OffscreenCanvas(width, height); From 5b10bcefb5bb3f92f18210e3120ce1bd505fd9b3 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 26 May 2024 17:26:26 +0530 Subject: [PATCH 27/68] chore: removed unnecessary string interpolation from videoElement style height and width in takeFrame --- .../camera/camera_web/lib/src/camera_service.dart | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index c7e760b5cf3..19aa310c9fb 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -397,15 +397,8 @@ class CameraService { html.VideoElement videoElement, { bool canUseOffscreenCanvas = false, }) { - final List widthPx = videoElement.style.width.split('px'); - final List heightPx = videoElement.style.height.split('px'); - final String widthString = - widthPx.isNotEmpty ? widthPx.first : '${videoElement.videoWidth}'; - final String heightString = - heightPx.isNotEmpty ? heightPx.first : '${videoElement.videoHeight}'; - final int width = int.tryParse(widthString) ?? videoElement.videoWidth; - final int height = int.tryParse(heightString) ?? videoElement.videoHeight; - + final int width = videoElement.videoWidth; + final int height = videoElement.videoHeight; if (width == 0 || height == 0) { throw Exception( 'Computed dimensions are zero: width=$width, height=$height', From 90bb72edd399ba77112dc1c312a983a8a28aa6a0 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 26 May 2024 18:38:49 +0530 Subject: [PATCH 28/68] chore: reusing the OffscreenCanvas or CanvasElement depending on canUseOffScreenCanvas --- .../camera_web/lib/src/camera_service.dart | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 19aa310c9fb..d77ab2624b4 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -392,6 +392,12 @@ class CameraService { return jsUtil.hasProperty(window!, 'OffscreenCanvas'); } + ///Used in [takeFrame] if [canUseOffscreenCanvas] is false + html.CanvasElement? _canvasElement; + + ///Used in [takeFrame] if [canUseOffscreenCanvas] is false + html.OffscreenCanvas? _offscreenCanvas; + ///Returns frame at a specific time using video element CameraImageData takeFrame( html.VideoElement videoElement, { @@ -406,18 +412,24 @@ class CameraService { } late html.ImageData imageData; if (canUseOffscreenCanvas) { - final html.OffscreenCanvas canvas = html.OffscreenCanvas(width, height); - final html.OffscreenCanvasRenderingContext2D context = - canvas.getContext('2d')! as html.OffscreenCanvasRenderingContext2D; - context.drawImage(videoElement, 0, 0, width, height); + if (_offscreenCanvas == null || + _offscreenCanvas!.width != width || + _offscreenCanvas!.height != height) { + _offscreenCanvas = html.OffscreenCanvas(width, height); + } + final html.OffscreenCanvasRenderingContext2D context = _offscreenCanvas! + .getContext('2d')! as html.OffscreenCanvasRenderingContext2D; + context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); } else { - final html.CanvasElement canvas = - html.CanvasElement(width: width, height: height); - final html.CanvasRenderingContext2D context = canvas.context2D; + if (_canvasElement == null || + _canvasElement!.width != width || + _canvasElement!.height != height) { + _canvasElement = html.CanvasElement(width: width, height: height); + } + final html.CanvasRenderingContext2D context = _canvasElement!.context2D; context.drawImageScaled(videoElement, 0, 0, width, height); imageData = context.getImageData(0, 0, width, height); - canvas.remove(); } final ByteBuffer byteBuffer = imageData.data.buffer; From b196180812797acf707dc133ba903a57b615041b Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 26 May 2024 18:40:19 +0530 Subject: [PATCH 29/68] refactor: removed getCameraImageDataFromBytes to be used in-line --- .../camera_web/lib/src/camera_service.dart | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index d77ab2624b4..71b405f84fd 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -365,28 +365,6 @@ class CameraService { } } - /// Maps a [Uint] to a [CameraImageData]. - CameraImageData getCameraImageDataFromBytes( - Uint8List bytes, { - required int height, - required int width, - }) { - return CameraImageData( - format: const CameraImageFormat( - ImageFormatGroup.jpeg, - raw: '', - ), - planes: [ - CameraImagePlane( - bytes: bytes, - bytesPerRow: 0, - ), - ], - height: height, - width: width, - ); - } - ///Used to check if browser has OffscreenCanvas capability bool hasPropertyOffScreenCanvas() { return jsUtil.hasProperty(window!, 'OffscreenCanvas'); @@ -433,10 +411,19 @@ class CameraService { } final ByteBuffer byteBuffer = imageData.data.buffer; - return getCameraImageDataFromBytes( - byteBuffer.asUint8List(), - height: imageData.height, - width: imageData.width, + return CameraImageData( + format: const CameraImageFormat( + ImageFormatGroup.jpeg, + raw: '', + ), + planes: [ + CameraImagePlane( + bytes: byteBuffer.asUint8List(), + bytesPerRow: 0, + ), + ], + height: height, + width: width, ); } } From 5c1c6626117f24f317b5e2dd260820533b2160b3 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 26 May 2024 19:20:19 +0530 Subject: [PATCH 30/68] fix: setup hasPropertyOffScreenCanvas in tests --- .../example/integration_test/camera_bitrate_test.dart | 4 ++++ .../camera_web/example/integration_test/camera_web_test.dart | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart b/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart index 3e2c9bd40c9..616a5adb286 100644 --- a/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart @@ -106,6 +106,10 @@ void main() { ).thenAnswer( (_) => Future.value(canvasElement.captureStream())); + when( + () => cameraService.hasPropertyOffScreenCanvas(), + ).thenAnswer((_) => true); + final Camera camera = Camera( textureId: cameraId, cameraService: cameraService, diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index ac31979eb17..cbc015b947a 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -100,6 +100,10 @@ void main() { (_) async => videoElement.captureStream(), ); + when( + () => cameraService.hasPropertyOffScreenCanvas(), + ).thenAnswer((_) => true); + CameraPlatform.instance = CameraPlugin( cameraService: cameraService, )..window = window; From f484f0284c187e79b6a00ea1c1887699cade00f8 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 14 Jul 2024 20:07:12 +0530 Subject: [PATCH 31/68] chore: version and changelog updated --- packages/camera/camera_web/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index a63d80460b0..8feaa3cb970 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.5 + +- Supporting camera image stream on web. + ## 0.3.4 - Supporting camera image stream on web. From 98bceb7e063cf04340cde126797a3a66e009b11e Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 14 Jul 2024 20:08:52 +0530 Subject: [PATCH 32/68] docs: removed "Streaming of frames" from Missing implementation --- packages/camera/camera_web/README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_web/README.md b/packages/camera/camera_web/README.md index 1d20e9a88fc..9995d134a18 100644 --- a/packages/camera/camera_web/README.md +++ b/packages/camera/camera_web/README.md @@ -2,7 +2,7 @@ The web implementation of [`camera`][camera]. -*Note*: This plugin is under development. See [missing implementation](#missing-implementation). +_Note_: This plugin is under development. See [missing implementation](#missing-implementation). ## Usage @@ -46,7 +46,7 @@ For the browsers that do not support the device orientation: - `CameraPlatform.onDeviceOrientationChanged` returns an empty stream. - `CameraPlatform.lockCaptureOrientation` and `CameraPlatform.unlockCaptureOrientation` -throw a `PlatformException` with the `orientationNotSupported` error code. + throw a `PlatformException` with the `orientationNotSupported` error code. ### Flash mode and zoom level @@ -58,13 +58,13 @@ with the following [browser support](https://caniuse.com/mdn-api_imagecapture): For the browsers that do not support the flash mode: - `CameraPlatform.setFlashMode` throws a `PlatformException` with the -`torchModeNotSupported` error code. + `torchModeNotSupported` error code. For the browsers that do not support the zoom level: - `CameraPlatform.getMaxZoomLevel`, `CameraPlatform.getMinZoomLevel` and -`CameraPlatform.setZoomLevel` throw a `PlatformException` with the -`zoomLevelNotSupported` error code. + `CameraPlatform.setZoomLevel` throw a `PlatformException` with the + `zoomLevelNotSupported` error code. ### Taking a picture @@ -81,6 +81,7 @@ URL pointing to a location within the browser (blob) and can be displayed using See the example below: + ```dart final Image image; if (kIsWeb) { @@ -97,22 +98,25 @@ The video recording implementation is backed by [MediaRecorder Web API](https:// ![Data on support for the MediaRecorder feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/mediarecorder.png). A video is recorded in one of the following video MIME types: + - video/webm (e.g. on Chrome or Firefox) - video/mp4 (e.g. on Safari) Pausing, resuming or stopping the video recording throws a `PlatformException` with the `videoRecordingNotStarted` error code if the video recording was not started. For the browsers that do not support the video recording: + - `CameraPlatform.startVideoRecording` throws a `PlatformException` with the `notSupported` error code. ## Missing implementation The web implementation of [`camera`][camera] is missing the following features: + - Exposure mode, point and offset - Focus mode and point - Sensor orientation - Image format group -- Streaming of frames + [camera]: https://pub.dev/packages/camera From bf9c3fe68273f0ac028c4a1d4a93a43007fae0e0 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 14 Jul 2024 20:58:31 +0530 Subject: [PATCH 33/68] Revert "docs: removed "Streaming of frames" from Missing implementation" This reverts commit 4db1863e6b8560afd0950a634f0fc5a9091e33f9. --- packages/camera/camera_web/README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera_web/README.md b/packages/camera/camera_web/README.md index 9995d134a18..1d20e9a88fc 100644 --- a/packages/camera/camera_web/README.md +++ b/packages/camera/camera_web/README.md @@ -2,7 +2,7 @@ The web implementation of [`camera`][camera]. -_Note_: This plugin is under development. See [missing implementation](#missing-implementation). +*Note*: This plugin is under development. See [missing implementation](#missing-implementation). ## Usage @@ -46,7 +46,7 @@ For the browsers that do not support the device orientation: - `CameraPlatform.onDeviceOrientationChanged` returns an empty stream. - `CameraPlatform.lockCaptureOrientation` and `CameraPlatform.unlockCaptureOrientation` - throw a `PlatformException` with the `orientationNotSupported` error code. +throw a `PlatformException` with the `orientationNotSupported` error code. ### Flash mode and zoom level @@ -58,13 +58,13 @@ with the following [browser support](https://caniuse.com/mdn-api_imagecapture): For the browsers that do not support the flash mode: - `CameraPlatform.setFlashMode` throws a `PlatformException` with the - `torchModeNotSupported` error code. +`torchModeNotSupported` error code. For the browsers that do not support the zoom level: - `CameraPlatform.getMaxZoomLevel`, `CameraPlatform.getMinZoomLevel` and - `CameraPlatform.setZoomLevel` throw a `PlatformException` with the - `zoomLevelNotSupported` error code. +`CameraPlatform.setZoomLevel` throw a `PlatformException` with the +`zoomLevelNotSupported` error code. ### Taking a picture @@ -81,7 +81,6 @@ URL pointing to a location within the browser (blob) and can be displayed using See the example below: - ```dart final Image image; if (kIsWeb) { @@ -98,25 +97,22 @@ The video recording implementation is backed by [MediaRecorder Web API](https:// ![Data on support for the MediaRecorder feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/mediarecorder.png). A video is recorded in one of the following video MIME types: - - video/webm (e.g. on Chrome or Firefox) - video/mp4 (e.g. on Safari) Pausing, resuming or stopping the video recording throws a `PlatformException` with the `videoRecordingNotStarted` error code if the video recording was not started. For the browsers that do not support the video recording: - - `CameraPlatform.startVideoRecording` throws a `PlatformException` with the `notSupported` error code. ## Missing implementation The web implementation of [`camera`][camera] is missing the following features: - - Exposure mode, point and offset - Focus mode and point - Sensor orientation - Image format group +- Streaming of frames - [camera]: https://pub.dev/packages/camera From f183b04d8c9cd2eace2a37adc9fc23d131a5811c Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Mon, 28 Oct 2024 15:43:30 +0530 Subject: [PATCH 34/68] merge: branch 'main' flutter/packages + replaced dart:html import with package:web --- packages/camera/camera_web/CHANGELOG.md | 8 +++- .../integration_test/camera_service_test.dart | 24 ++++++---- .../camera/camera_web/lib/src/camera.dart | 47 +++++++++++++++---- .../camera_web/lib/src/camera_service.dart | 36 +++++++++----- .../camera/camera_web/lib/src/camera_web.dart | 2 +- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 8feaa3cb970..bf4d8e6c4b9 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,6 +1,12 @@ -## 0.3.5 +## 0.3.6 - Supporting camera image stream on web. +- Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. + +## 0.3.5 + +- Migrates to package:web to support WASM +- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.3.4 diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index c85809f56b1..69b7cdfb3a3 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; -import 'dart:html'; -import 'dart:js_util' as js_util; // ignore_for_file: only_throw_errors import 'dart:async'; @@ -919,7 +916,10 @@ void main() { 'otherwise false', (WidgetTester widgetTester) async { when( - () => jsUtil.hasProperty(window, 'OffscreenCanvas'), + () => jsUtil.hasProperty( + window, + 'OffscreenCanvas'.toJS, + ), ).thenReturn(true); final bool hasOffScreenCanvas = cameraService.hasPropertyOffScreenCanvas(); @@ -928,7 +928,10 @@ void main() { true, ); when( - () => jsUtil.hasProperty(window, 'OffscreenCanvas'), + () => jsUtil.hasProperty( + window, + 'OffscreenCanvas'.toJS, + ), ).thenReturn(false); final bool hasNotOffScreenCanvas = cameraService.hasPropertyOffScreenCanvas(); @@ -944,11 +947,12 @@ void main() { (WidgetTester widgetTester) async { const Size size = Size(10, 10); final Completer completer = Completer(); - final VideoElement videoElement = getVideoElementWithBlankStream(size) - ..onLoadedMetadata.listen((_) { - completer.complete(); - }) - ..load(); + final web.VideoElement videoElement = + getVideoElementWithBlankStream(size) + ..onLoadedMetadata.listen((_) { + completer.complete(); + }) + ..load(); await completer.future; final CameraImageData cameraImageData = cameraService.takeFrame( videoElement, diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index e4bc4a752aa..3f460a99f8e 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:js_interop'; import 'dart:ui'; import 'dart:ui_web' as ui_web; - import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:web/web.dart' as web; @@ -648,6 +647,10 @@ class Camera { final StreamController _cameraFrameStreamController = StreamController.broadcast(); + // TODO(replace): introduced fps in + /// [CameraImageStreamOptions] + final int cameraStreamFPS = 30; + /// Returns a stream of camera frames. /// /// To stop listening to new animation frames close all listening streams. @@ -655,7 +658,10 @@ class Camera { CameraImageStreamOptions? options, }) { _cameraFrameStreamController.onListen = () { - _triggerAnimationFramesLoop(_addCameraImageDataEvent); + _triggerAnimationFramesLoop( + _addCameraImageDataEvent, + fps: cameraStreamFPS, + ); }; return _cameraFrameStreamController.stream; @@ -663,15 +669,40 @@ class Camera { /// Triggers animation frames in a loop as long as /// [_cameraFrameStreamController.hasListener] and executes the callback - void _triggerAnimationFramesLoop(VoidCallback callback) { - Future triggerNextAnimationFrame() async { - while (_cameraFrameStreamController.hasListener) { - await window!.animationFrame; - callback(); + void _triggerAnimationFramesLoop( + VoidCallback action, { + required int fps, + }) { + Completer completer = Completer(); + completer.complete(); + + void onAnimate(num _) { + if (!_cameraFrameStreamController.hasListener) { + return; } + + if (!completer.isCompleted) { + // Schedule the next frame + window.requestAnimationFrame(onAnimate.toJS); + return; + } + + // Perform the action task + action(); + + // Reset the completer and set up a delay + completer = Completer(); + Future.delayed( + Duration(milliseconds: 1000 ~/ fps), + ).then((_) { + completer.complete(); + // Schedule the next frame after the delay + window.requestAnimationFrame(onAnimate.toJS); + }); } - triggerNextAnimationFrame(); + // Start the animation loop + window.requestAnimationFrame(onAnimate.toJS); } /// Used to trigger add event of camera image data in camera frame stream diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 71b405f84fd..28fd3840858 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -367,18 +367,18 @@ class CameraService { ///Used to check if browser has OffscreenCanvas capability bool hasPropertyOffScreenCanvas() { - return jsUtil.hasProperty(window!, 'OffscreenCanvas'); + return jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS); } ///Used in [takeFrame] if [canUseOffscreenCanvas] is false - html.CanvasElement? _canvasElement; + web.CanvasElement? _canvasElement; ///Used in [takeFrame] if [canUseOffscreenCanvas] is false - html.OffscreenCanvas? _offscreenCanvas; + web.OffscreenCanvas? _offscreenCanvas; ///Returns frame at a specific time using video element CameraImageData takeFrame( - html.VideoElement videoElement, { + web.VideoElement videoElement, { bool canUseOffscreenCanvas = false, }) { final int width = videoElement.videoWidth; @@ -388,28 +388,40 @@ class CameraService { 'Computed dimensions are zero: width=$width, height=$height', ); } - late html.ImageData imageData; + late web.ImageData imageData; if (canUseOffscreenCanvas) { if (_offscreenCanvas == null || _offscreenCanvas!.width != width || _offscreenCanvas!.height != height) { - _offscreenCanvas = html.OffscreenCanvas(width, height); + _offscreenCanvas = web.OffscreenCanvas(width, height); } - final html.OffscreenCanvasRenderingContext2D context = _offscreenCanvas! - .getContext('2d')! as html.OffscreenCanvasRenderingContext2D; + final web.OffscreenCanvasRenderingContext2D context = + _offscreenCanvas!.getContext( + '2d', + {'willReadFrequently': true}.toJSBox, + )! as web.OffscreenCanvasRenderingContext2D; context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); } else { if (_canvasElement == null || _canvasElement!.width != width || _canvasElement!.height != height) { - _canvasElement = html.CanvasElement(width: width, height: height); + _canvasElement = web.CanvasElement() + ..height = height + ..width = width; } - final html.CanvasRenderingContext2D context = _canvasElement!.context2D; - context.drawImageScaled(videoElement, 0, 0, width, height); + final web.CanvasRenderingContext2D context = _canvasElement!.context2D; + + context.drawImageScaled( + videoElement, + 0, + 0, + width.toDouble(), + height.toDouble(), + ); imageData = context.getImageData(0, 0, width, height); } - final ByteBuffer byteBuffer = imageData.data.buffer; + final ByteBuffer byteBuffer = imageData.data.toDart.buffer; return CameraImageData( format: const CameraImageFormat( diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 88f75284ed3..f8d232a1c0a 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -640,7 +640,7 @@ class CameraPlugin extends CameraPlatform { }) { try { return getCamera(cameraId).cameraFrameStream(options: options); - } on html.DomException catch (e) { + } on web.DOMException catch (e) { throw PlatformException(code: e.name, message: e.message); } on CameraWebException catch (e) { _addCameraErrorEvent(e); From 22cad657d43b3118d7058770b8920ba53c7e6fdc Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 29 Oct 2024 02:07:55 +0530 Subject: [PATCH 35/68] fix: version bump --- packages/camera/camera_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 4052a701b86..abf5de713f4 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.3.5 +version: 0.3.6 environment: sdk: ^3.4.0 From 380c35bd93075b29cf35d281d5d823955f65d17f Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 29 Oct 2024 02:18:50 +0530 Subject: [PATCH 36/68] fix: import organized in camera_test --- .../camera/camera_web/example/integration_test/camera_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index bdf87901b51..dc7df7f2de6 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:typed_data'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; +import 'dart:typed_data'; import 'dart:ui'; import 'package:async/async.dart'; From cc818ba083d4e0a0ef9b5149463dea213e64b30b Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 1 Nov 2024 13:19:28 +0530 Subject: [PATCH 37/68] [camera_web] fix: Changelog.md --- packages/camera/camera_web/CHANGELOG.md | 81 +++++++++++++------------ 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index bf4d8e6c4b9..a0887a42c99 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,111 +1,114 @@ ## 0.3.6 -- Supporting camera image stream on web. -- Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Supporting camera image stream on web. +* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. ## 0.3.5 -- Migrates to package:web to support WASM -- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Migrates to package:web to support WASM +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.3.4 -- Supporting camera image stream on web. +* Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at + the app-facing package level, and is deprecated at the platform interface + level. +* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 0.3.3 -- Adds support to control video FPS and bitrate. See `CameraController.withSettings`. -- Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Adds support to control video FPS and bitrate. See `CameraController.withSettings`. +* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.3.2+4 -- Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. -- Fixes new lint warnings. +* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. +* Fixes new lint warnings. ## 0.3.2+3 -- Migrates to `dart:ui_web` APIs. -- Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. +* Migrates to `dart:ui_web` APIs. +* Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. ## 0.3.2+2 -- Adds pub topics to package metadata. -- Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. ## 0.3.2+1 -- Updates README to improve example of `Image` creation. +* Updates README to improve example of `Image` creation. ## 0.3.2 -- Changes `availableCameras` to not ask for the microphone permission. +* Changes `availableCameras` to not ask for the microphone permission. ## 0.3.1+4 -- Removes obsolete null checks on non-nullable values. -- Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +* Removes obsolete null checks on non-nullable values. +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 0.3.1+3 -- Clarifies explanation of endorsement in README. -- Aligns Dart and Flutter SDK constraints. +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. ## 0.3.1+2 -- Updates links for the merge of flutter/plugins into flutter/packages. -- Updates minimum Flutter version to 3.0. +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates minimum Flutter version to 3.0. ## 0.3.1+1 -- Updates code for stricter lint checks. +* Updates code for stricter lint checks. ## 0.3.1 -- Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). +* Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). ## 0.3.0+1 -- Updates imports for `prefer_relative_imports`. -- Updates minimum Flutter version to 2.10. -- Fixes avoid_redundant_argument_values lint warnings and minor typos. -- Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Updates imports for `prefer_relative_imports`. +* Updates minimum Flutter version to 2.10. +* Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.3.0 -- **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. +* **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. ## 0.2.1+6 -- Minor fixes for new analysis options. +* Minor fixes for new analysis options. ## 0.2.1+5 -- Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors lint warnings. ## 0.2.1+4 -- Migrates from `ui.hash*` to `Object.hash*`. -- Updates minimum Flutter version for changes in 0.2.1+3. +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version for changes in 0.2.1+3. ## 0.2.1+3 -- Internal code cleanup for stricter analysis options. +* Internal code cleanup for stricter analysis options. ## 0.2.1+2 -- Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. -- Implemented support for new Dart SDKs with an async requestFullscreen API. +* Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. +* Implemented support for new Dart SDKs with an async requestFullscreen API. ## 0.2.1+1 -- Update usage documentation. +* Update usage documentation. ## 0.2.1 -- Add video recording functionality. -- Fix cameraNotReadable error that prevented access to the camera on some Android devices. +* Add video recording functionality. +* Fix cameraNotReadable error that prevented access to the camera on some Android devices. ## 0.2.0 -- Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. +* Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. From a04a4728ad0991c6222cdd7fb078626b7f2b6ec5 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 1 Nov 2024 13:22:52 +0530 Subject: [PATCH 38/68] [camera_web] chore: imports organized for camera.dart --- packages/camera/camera_web/lib/src/camera.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 3f460a99f8e..50d7f46b518 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:js_interop'; import 'dart:ui'; import 'dart:ui_web' as ui_web; + import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:web/web.dart' as web; From aa9a81906319f65d76a538708ca5509a080a0fa3 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 00:13:28 +0530 Subject: [PATCH 39/68] [camera_web] fix: CameraImageData constructed with unkown ImageFormatGroup, 0 as raw value and fixed the bytes per row value --- packages/camera/camera_web/lib/src/camera_service.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 28fd3840858..72157679091 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -425,13 +425,13 @@ class CameraService { return CameraImageData( format: const CameraImageFormat( - ImageFormatGroup.jpeg, - raw: '', + ImageFormatGroup.unknown, + raw: 0, ), planes: [ CameraImagePlane( bytes: byteBuffer.asUint8List(), - bytesPerRow: 0, + bytesPerRow: width * 4, ), ], height: height, From 594111911dd2036df6624ed5dcc29038db93641c Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 00:32:34 +0530 Subject: [PATCH 40/68] [camera_web]: chore: preserving animationFrameId from requestAnimationFrame for cancelAnimationFrame --- .../camera/camera_web/lib/src/camera.dart | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 50d7f46b518..c2e3ee4e797 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -677,14 +677,16 @@ class Camera { Completer completer = Completer(); completer.complete(); + int? animationFrameId; + void onAnimate(num _) { if (!_cameraFrameStreamController.hasListener) { return; } if (!completer.isCompleted) { - // Schedule the next frame - window.requestAnimationFrame(onAnimate.toJS); + // Schedule the next frame and capture its ID + animationFrameId = window.requestAnimationFrame(onAnimate.toJS); return; } @@ -697,13 +699,21 @@ class Camera { Duration(milliseconds: 1000 ~/ fps), ).then((_) { completer.complete(); - // Schedule the next frame after the delay - window.requestAnimationFrame(onAnimate.toJS); + // Schedule the next frame after the delay and capture its ID + animationFrameId = window.requestAnimationFrame(onAnimate.toJS); }); } - // Start the animation loop - window.requestAnimationFrame(onAnimate.toJS); + // Listen for the stream controller cancellation to stop the animation + _cameraFrameStreamController.onCancel = () { + if (animationFrameId != null) { + window.cancelAnimationFrame(animationFrameId!); + animationFrameId = null; + } + }; + + // Start the animation loop and capture the initial frame ID + animationFrameId = window.requestAnimationFrame(onAnimate.toJS); } /// Used to trigger add event of camera image data in camera frame stream From 37569491f9bda80300c12670b356fea0ae19ae03 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 00:44:34 +0530 Subject: [PATCH 41/68] [camera_web] chore: removed completer from _triggerAnimationFramesLoop and improved readability --- .../camera/camera_web/lib/src/camera.dart | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index c2e3ee4e797..4d7354b0b3e 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -674,46 +674,33 @@ class Camera { VoidCallback action, { required int fps, }) { - Completer completer = Completer(); - completer.complete(); - int? animationFrameId; + final Duration frameDuration = Duration(milliseconds: 1000 ~/ fps); void onAnimate(num _) { if (!_cameraFrameStreamController.hasListener) { return; } - if (!completer.isCompleted) { - // Schedule the next frame and capture its ID - animationFrameId = window.requestAnimationFrame(onAnimate.toJS); - return; - } - // Perform the action task action(); - // Reset the completer and set up a delay - completer = Completer(); - Future.delayed( - Duration(milliseconds: 1000 ~/ fps), - ).then((_) { - completer.complete(); - // Schedule the next frame after the delay and capture its ID + // Schedule the next frame after the delay + Future.delayed(frameDuration).then((_) { animationFrameId = window.requestAnimationFrame(onAnimate.toJS); }); } - // Listen for the stream controller cancellation to stop the animation + // Start the animation loop + animationFrameId = window.requestAnimationFrame(onAnimate.toJS); + + // Stop the animation when the stream controller has no more listeners _cameraFrameStreamController.onCancel = () { if (animationFrameId != null) { window.cancelAnimationFrame(animationFrameId!); animationFrameId = null; } }; - - // Start the animation loop and capture the initial frame ID - animationFrameId = window.requestAnimationFrame(onAnimate.toJS); } /// Used to trigger add event of camera image data in camera frame stream From 74bb74e6344161c315e7eb66c2e1944e7812bb2f Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 00:50:31 +0530 Subject: [PATCH 42/68] [camera_web] fix: jsified willReadFrequently in getContext of OffscreenCanvasRenderingContext2D --- packages/camera/camera_web/lib/src/camera_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 72157679091..54afb4f216e 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -398,7 +398,7 @@ class CameraService { final web.OffscreenCanvasRenderingContext2D context = _offscreenCanvas!.getContext( '2d', - {'willReadFrequently': true}.toJSBox, + {'willReadFrequently': true}.jsify(), )! as web.OffscreenCanvasRenderingContext2D; context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); From 54aa92e6bd9327cd9bc2a993891d19902cd86829 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 02:33:31 +0530 Subject: [PATCH 43/68] [camera_web] chore: optimized frame rate by using simple arithmatic calculation on high resolution timestamp --- .../camera/camera_web/lib/src/camera.dart | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 4d7354b0b3e..8fc3584cc4c 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -668,33 +668,37 @@ class Camera { return _cameraFrameStreamController.stream; } - /// Triggers animation frames in a loop as long as - /// [_cameraFrameStreamController.hasListener] and executes the callback + /// Triggers animation frames in a loop at a specified FPS + /// as long as [animationFrameId] is not cancelled void _triggerAnimationFramesLoop( VoidCallback action, { required int fps, }) { int? animationFrameId; - final Duration frameDuration = Duration(milliseconds: 1000 ~/ fps); + num now, then = window.performance.now(), elapsed, fpsInterval = 1000 / fps; - void onAnimate(num _) { - if (!_cameraFrameStreamController.hasListener) { - return; - } + void onAnimate(num timestamp) { + // Schedule the next frame + animationFrameId = window.requestAnimationFrame(onAnimate.toJS); - // Perform the action task - action(); + // Calculate the elapsed time since the last frame + now = timestamp; + elapsed = now - then; - // Schedule the next frame after the delay - Future.delayed(frameDuration).then((_) { - animationFrameId = window.requestAnimationFrame(onAnimate.toJS); - }); + // If enough time has passed since the last frame + if (elapsed > fpsInterval) { + // Get ready for next frame + then = now - (elapsed % fpsInterval); + + // Perform the action task + action(); + } } - // Start the animation loop + // Initialize the animation loop animationFrameId = window.requestAnimationFrame(onAnimate.toJS); - // Stop the animation when the stream controller has no more listeners + // Listen for the stream controller cancellation to stop the animation _cameraFrameStreamController.onCancel = () { if (animationFrameId != null) { window.cancelAnimationFrame(animationFrameId!); From e46ef6f7547ec50ee5fbbe961fce2af74d01efb0 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 02:45:09 +0530 Subject: [PATCH 44/68] [camera_web] chore: removed canUseOffScreenCanvas named paramater from takeFrame function of camera service, using the function directly --- .../example/integration_test/camera_service_test.dart | 1 - .../camera_web/example/integration_test/camera_test.dart | 5 +---- packages/camera/camera_web/lib/src/camera.dart | 5 +---- packages/camera/camera_web/lib/src/camera_service.dart | 7 +++---- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 69b7cdfb3a3..66d1ff5e953 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -956,7 +956,6 @@ void main() { await completer.future; final CameraImageData cameraImageData = cameraService.takeFrame( videoElement, - canUseOffscreenCanvas: true, ); expect( size, diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index dc7df7f2de6..958d7039ade 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1723,10 +1723,7 @@ void main() { )..videoElement = videoElement; when( - () => cameraService.takeFrame( - videoElement, - canUseOffscreenCanvas: camera.canUseOffscreenCanvas, - ), + () => cameraService.takeFrame(videoElement), ).thenAnswer( (_) => CameraImageData( format: const CameraImageFormat( diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 8fc3584cc4c..a3b1552ee50 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -709,10 +709,7 @@ class Camera { /// Used to trigger add event of camera image data in camera frame stream void _addCameraImageDataEvent() { - final CameraImageData image = _cameraService.takeFrame( - videoElement, - canUseOffscreenCanvas: canUseOffscreenCanvas, - ); + final CameraImageData image = _cameraService.takeFrame(videoElement); _cameraFrameStreamController.add(image); } } diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 54afb4f216e..d66bcf06f08 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -378,9 +378,8 @@ class CameraService { ///Returns frame at a specific time using video element CameraImageData takeFrame( - web.VideoElement videoElement, { - bool canUseOffscreenCanvas = false, - }) { + web.VideoElement videoElement, + ) { final int width = videoElement.videoWidth; final int height = videoElement.videoHeight; if (width == 0 || height == 0) { @@ -389,7 +388,7 @@ class CameraService { ); } late web.ImageData imageData; - if (canUseOffscreenCanvas) { + if (hasPropertyOffScreenCanvas()) { if (_offscreenCanvas == null || _offscreenCanvas!.width != width || _offscreenCanvas!.height != height) { From 5e7bb5e00533b23121bb28fd6ea6b59a2d3c0b8d Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 5 Nov 2024 02:46:54 +0530 Subject: [PATCH 45/68] [camera_web] chore: initializing canUseOffscreenCanvas from constructor instead of intialize() --- packages/camera/camera_web/lib/src/camera.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index a3b1552ee50..c9d6a825060 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -49,7 +49,8 @@ class Camera { required CameraService cameraService, this.options = const CameraOptions(), this.recorderOptions = const (audioBitrate: null, videoBitrate: null), - }) : _cameraService = cameraService; + }) : _cameraService = cameraService, + canUseOffscreenCanvas = cameraService.hasPropertyOffScreenCanvas(); /// The texture id used to register the camera view. final int textureId; @@ -204,8 +205,6 @@ class Camera { onEndedController.add(defaultVideoTrack); }); } - - canUseOffscreenCanvas = _cameraService.hasPropertyOffScreenCanvas(); } /// Starts the camera stream. From c48418ba2bf39769cb539bd39d3979f43b49c9d7 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 6 Nov 2024 18:42:50 +0530 Subject: [PATCH 46/68] [camera_web]: replaced dynamic with Object? in map value for willReadFrequently --- packages/camera/camera_web/lib/src/camera_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index d66bcf06f08..80fc5f45143 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -397,7 +397,7 @@ class CameraService { final web.OffscreenCanvasRenderingContext2D context = _offscreenCanvas!.getContext( '2d', - {'willReadFrequently': true}.jsify(), + {'willReadFrequently': true}.jsify(), )! as web.OffscreenCanvasRenderingContext2D; context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); From a0cd373db5aeb9392d814116349edb9516f2d537 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 6 Nov 2024 19:05:45 +0530 Subject: [PATCH 47/68] [camera_web] test: camera image stream test cases for with and without OffscreenCanvas in camera service --- .../integration_test/camera_service_test.dart | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 66d1ff5e953..6ae503da38e 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -908,7 +908,7 @@ void main() { group('camera image stream', () { setUp( () { - cameraService.jsUtil = jsUtil; + cameraService = cameraService..jsUtil = jsUtil; }, ); testWidgets( @@ -943,7 +943,8 @@ void main() { ); testWidgets( 'returns Camera Image of Size ' - 'when videoElement is of Size', + 'when videoElement is of Size ' + 'when browser supports OffscreenCanvas', (WidgetTester widgetTester) async { const Size size = Size(10, 10); final Completer completer = Completer(); @@ -954,6 +955,39 @@ void main() { }) ..load(); await completer.future; + when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( + true, + ); + final CameraImageData cameraImageData = cameraService.takeFrame( + videoElement, + ); + expect( + size, + Size( + cameraImageData.width.toDouble(), + cameraImageData.height.toDouble(), + ), + ); + verify(cameraService.hasPropertyOffScreenCanvas).called(1); + }, + ); + testWidgets( + 'returns Camera Image of Size ' + 'when videoElement is of Size ' + 'when browser does not supports OffscreenCanvas', + (WidgetTester widgetTester) async { + const Size size = Size(10, 10); + final Completer loadVideo = Completer(); + final web.VideoElement videoElement = + getVideoElementWithBlankStream(size) + ..onLoadedMetadata.listen((_) { + loadVideo.complete(); + }) + ..load(); + await loadVideo.future; + when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( + false, + ); final CameraImageData cameraImageData = cameraService.takeFrame( videoElement, ); @@ -964,6 +998,7 @@ void main() { cameraImageData.height.toDouble(), ), ); + verify(cameraService.hasPropertyOffScreenCanvas).called(1); }, ); }); From d7b7d5cac99f80c61de50fd36885af23b1547ef1 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 6 Nov 2024 19:06:25 +0530 Subject: [PATCH 48/68] [camera_web] test: camera frame stream test cases for with and without OffscreenCanvas in camera test --- .../example/integration_test/camera_test.dart | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 958d7039ade..e11d4045752 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1711,7 +1711,8 @@ void main() { }); group('cameraFrameStream', () { testWidgets( - 'bytes is a multiple of 4', + 'CameraImageData bytes is a multiple of 4 ' + 'when browser supports OffscreenCanvas', (WidgetTester tester) async { final VideoElement videoElement = getVideoElementWithBlankStream( const Size(10, 10), @@ -1722,18 +1723,72 @@ void main() { cameraService: cameraService, )..videoElement = videoElement; + when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( + true, + ); + + when( + () => cameraService.takeFrame(videoElement), + ).thenAnswer( + (_) => CameraImageData( + format: const CameraImageFormat( + ImageFormatGroup.unknown, + raw: 0, + ), + planes: [ + CameraImagePlane( + bytes: Uint8List(32), + bytesPerRow: videoElement.width * 4, + ), + ], + height: 10, + width: 10, + ), + ); + + final CameraImageData cameraImageData = + await camera.cameraFrameStream().first; + expect( + cameraImageData, + equals( + isA().having( + (CameraImageData e) => e.planes.first.bytes.length % 4, + 'bytes', + equals(0), + ), + ), + ); + }, + ); + testWidgets( + 'CameraImageData bytes is a multiple of 4 ' + 'when browser does not supports OffscreenCanvas', + (WidgetTester tester) async { + final VideoElement videoElement = getVideoElementWithBlankStream( + const Size(10, 10), + ); + + final Camera camera = Camera( + textureId: textureId, + cameraService: cameraService, + )..videoElement = videoElement; + + when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( + false, + ); + when( () => cameraService.takeFrame(videoElement), ).thenAnswer( (_) => CameraImageData( format: const CameraImageFormat( - ImageFormatGroup.jpeg, - raw: '', + ImageFormatGroup.unknown, + raw: 0, ), planes: [ CameraImagePlane( bytes: Uint8List(32), - bytesPerRow: 0, + bytesPerRow: videoElement.width * 4, ), ], height: 10, @@ -1754,7 +1809,6 @@ void main() { ), ); }, - timeout: const Timeout(Duration(seconds: 2)), ); }); }); From a959df5165e46716ce5c6c3839540436510348e5 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Tue, 12 Nov 2024 14:17:35 +0530 Subject: [PATCH 49/68] chore: updated animate function in _triggerAnimationFramesLoop to get accurate fps --- .../camera/camera_web/lib/src/camera.dart | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index c9d6a825060..9364895a16f 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -649,7 +649,7 @@ class Camera { // TODO(replace): introduced fps in /// [CameraImageStreamOptions] - final int cameraStreamFPS = 30; + final int cameraStreamFPS = 60; /// Returns a stream of camera frames. /// @@ -674,28 +674,26 @@ class Camera { required int fps, }) { int? animationFrameId; - num now, then = window.performance.now(), elapsed, fpsInterval = 1000 / fps; + num then = 0, fpsInterval = 1000 / fps; - void onAnimate(num timestamp) { + int? animate(num timestamp) { // Schedule the next frame - animationFrameId = window.requestAnimationFrame(onAnimate.toJS); - + animationFrameId = window.requestAnimationFrame(animate.toJS); // Calculate the elapsed time since the last frame - now = timestamp; - elapsed = now - then; + final num elapsed = timestamp - then; - // If enough time has passed since the last frame - if (elapsed > fpsInterval) { + // if we're close to the next frame (by ~8ms), do it. + if (fpsInterval - elapsed <= 8) { // Get ready for next frame - then = now - (elapsed % fpsInterval); - + then = timestamp; // Perform the action task action(); } + return animationFrameId; } // Initialize the animation loop - animationFrameId = window.requestAnimationFrame(onAnimate.toJS); + animationFrameId = animate(window.performance.now()); // Listen for the stream controller cancellation to stop the animation _cameraFrameStreamController.onCancel = () { From 006de9c97553f5d9233dc6cd614fd623d289eb73 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sat, 27 Sep 2025 21:15:01 +0530 Subject: [PATCH 50/68] [camera_web] chore: addressed review comments --- packages/camera/camera_web/CHANGELOG.md | 2 +- .../integration_test/camera_bitrate_test.dart | 4 +- .../integration_test/camera_service_test.dart | 108 +- .../example/integration_test/camera_test.dart | 94 +- .../integration_test/camera_web_test.dart | 4 +- .../integration_test/helpers/mocks.mocks.dart | 1255 ++++++++++------- .../camera/camera_web/lib/src/camera.dart | 29 +- .../camera_web/lib/src/camera_service.dart | 27 +- 8 files changed, 812 insertions(+), 711 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 70ceadcad49..fb025bbe8af 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.3.6 -- Supporting camera image stream on web. +- Adds support for camera image stream on web. - Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. ## 0.3.5 diff --git a/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart b/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart index 0973f5734c7..f604eb7382a 100644 --- a/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart @@ -93,9 +93,7 @@ void main() { cameraService.getMediaStreamForOptions(options, cameraId: cameraId), ).thenAnswer((_) async => canvasElement.captureStream()); - when( - () => cameraService.hasPropertyOffScreenCanvas(), - ).thenAnswer((_) => true); + when(cameraService.hasPropertyOffScreenCanvas()).thenAnswer((_) => true); final Camera camera = Camera( textureId: cameraId, diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index ea1332cf052..d47896e4909 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -985,58 +985,41 @@ void main() { }); group('camera image stream', () { - setUp( - () { - cameraService = cameraService..jsUtil = jsUtil; - }, - ); - testWidgets( - 'returns true if broswer has OffscreenCanvas ' - 'otherwise false', - (WidgetTester widgetTester) async { + setUp(() { + cameraService = cameraService..jsUtil = jsUtil; + }); + testWidgets('returns true if broswer has OffscreenCanvas ' + 'otherwise false', (WidgetTester widgetTester) async { + for (final bool supportsOffscreenCanvas in [true, false]) { when( - () => jsUtil.hasProperty( - window, - 'OffscreenCanvas'.toJS, - ), - ).thenReturn(true); + jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS), + ).thenReturn(supportsOffscreenCanvas); final bool hasOffScreenCanvas = cameraService.hasPropertyOffScreenCanvas(); expect( hasOffScreenCanvas, - true, + supportsOffscreenCanvas ? isTrue : isFalse, ); + } + }); + testWidgets('returns Camera Image of Size ' + 'when videoElement is of Size ' + 'regardless of OffscreenCanvas support', ( + WidgetTester widgetTester, + ) async { + const Size size = Size(10, 10); + final Completer completer = Completer(); + final web.VideoElement videoElement = + getVideoElementWithBlankStream(size) + ..onLoadedMetadata.listen((_) { + completer.complete(); + }) + ..load(); + await completer.future; + for (final bool supportsOffscreenCanvas in [true, false]) { when( - () => jsUtil.hasProperty( - window, - 'OffscreenCanvas'.toJS, - ), - ).thenReturn(false); - final bool hasNotOffScreenCanvas = - cameraService.hasPropertyOffScreenCanvas(); - expect( - hasNotOffScreenCanvas, - false, - ); - }, - ); - testWidgets( - 'returns Camera Image of Size ' - 'when videoElement is of Size ' - 'when browser supports OffscreenCanvas', - (WidgetTester widgetTester) async { - const Size size = Size(10, 10); - final Completer completer = Completer(); - final web.VideoElement videoElement = - getVideoElementWithBlankStream(size) - ..onLoadedMetadata.listen((_) { - completer.complete(); - }) - ..load(); - await completer.future; - when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( - true, - ); + jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS), + ).thenReturn(supportsOffscreenCanvas); final CameraImageData cameraImageData = cameraService.takeFrame( videoElement, ); @@ -1047,39 +1030,8 @@ void main() { cameraImageData.height.toDouble(), ), ); - verify(cameraService.hasPropertyOffScreenCanvas).called(1); - }, - ); - testWidgets( - 'returns Camera Image of Size ' - 'when videoElement is of Size ' - 'when browser does not supports OffscreenCanvas', - (WidgetTester widgetTester) async { - const Size size = Size(10, 10); - final Completer loadVideo = Completer(); - final web.VideoElement videoElement = - getVideoElementWithBlankStream(size) - ..onLoadedMetadata.listen((_) { - loadVideo.complete(); - }) - ..load(); - await loadVideo.future; - when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( - false, - ); - final CameraImageData cameraImageData = cameraService.takeFrame( - videoElement, - ); - expect( - size, - Size( - cameraImageData.width.toDouble(), - cameraImageData.height.toDouble(), - ), - ); - verify(cameraService.hasPropertyOffScreenCanvas).called(1); - }, - ); + } + }); }); }); } diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 4247759d4e5..495a1938266 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -63,9 +63,7 @@ void main() { ), ).thenAnswer((_) => Future.value(mediaStream)); - when( - () => cameraService.hasPropertyOffScreenCanvas(), - ).thenAnswer((_) => true); + when(cameraService.hasPropertyOffScreenCanvas()).thenAnswer((_) => true); }); group('initialize', () { @@ -1608,81 +1606,24 @@ void main() { }); }); group('cameraFrameStream', () { - testWidgets( - 'CameraImageData bytes is a multiple of 4 ' - 'when browser supports OffscreenCanvas', - (WidgetTester tester) async { - final VideoElement videoElement = getVideoElementWithBlankStream( - const Size(10, 10), - ); - - final Camera camera = Camera( - textureId: textureId, - cameraService: cameraService, - )..videoElement = videoElement; - - when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( - true, - ); + testWidgets('CameraImageData bytes is a multiple of 4 ' + 'regardless of OffscreenCanvas support', (WidgetTester tester) async { + final VideoElement videoElement = getVideoElementWithBlankStream( + const Size(10, 10), + ); + final Camera camera = Camera( + textureId: textureId, + cameraService: cameraService, + )..videoElement = videoElement; + for (final bool supportsOffscreenCanvas in [true, false]) { when( - () => cameraService.takeFrame(videoElement), - ).thenAnswer( - (_) => CameraImageData( - format: const CameraImageFormat( - ImageFormatGroup.unknown, - raw: 0, - ), - planes: [ - CameraImagePlane( - bytes: Uint8List(32), - bytesPerRow: videoElement.width * 4, - ), - ], - height: 10, - width: 10, - ), - ); + cameraService.hasPropertyOffScreenCanvas(), + ).thenReturn(supportsOffscreenCanvas); - final CameraImageData cameraImageData = - await camera.cameraFrameStream().first; - expect( - cameraImageData, - equals( - isA().having( - (CameraImageData e) => e.planes.first.bytes.length % 4, - 'bytes', - equals(0), - ), - ), - ); - }, - ); - testWidgets( - 'CameraImageData bytes is a multiple of 4 ' - 'when browser does not supports OffscreenCanvas', - (WidgetTester tester) async { - final VideoElement videoElement = getVideoElementWithBlankStream( - const Size(10, 10), - ); - - final Camera camera = Camera( - textureId: textureId, - cameraService: cameraService, - )..videoElement = videoElement; - - when(() => cameraService.hasPropertyOffScreenCanvas()).thenReturn( - false, - ); - - when( - () => cameraService.takeFrame(videoElement), - ).thenAnswer( + when(cameraService.takeFrame(videoElement)).thenAnswer( (_) => CameraImageData( - format: const CameraImageFormat( - ImageFormatGroup.unknown, - raw: 0, - ), + format: const CameraImageFormat(ImageFormatGroup.unknown, raw: 0), planes: [ CameraImagePlane( bytes: Uint8List(32), @@ -1696,6 +1637,7 @@ void main() { final CameraImageData cameraImageData = await camera.cameraFrameStream().first; + expect( cameraImageData, equals( @@ -1706,8 +1648,8 @@ void main() { ), ), ); - }, - ); + } + }); }); }); } diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 0781ef4b670..7125dfcc3d5 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -95,9 +95,7 @@ void main() { ), ).thenAnswer((_) async => videoElement.captureStream()); - when( - () => cameraService.hasPropertyOffScreenCanvas(), - ).thenAnswer((_) => true); + when(cameraService.hasPropertyOffScreenCanvas()).thenAnswer((_) => true); CameraPlatform.instance = CameraPlugin(cameraService: cameraService) ..window = window; diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart index 321507d7717..4061d5b494c 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart @@ -3,12 +3,12 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'dart:js_interop' as _i13; import 'dart:ui' as _i4; import 'package:camera_platform_interface/camera_platform_interface.dart' - as _i7; + as _i5; import 'package:camera_web/src/camera.dart' as _i10; import 'package:camera_web/src/camera_service.dart' as _i8; import 'package:camera_web/src/shims/dart_js_util.dart' as _i2; @@ -16,7 +16,7 @@ import 'package:camera_web/src/types/types.dart' as _i3; import 'package:flutter/services.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; -import 'package:web/web.dart' as _i6; +import 'package:web/web.dart' as _i7; import 'mocks.dart' as _i9; @@ -35,53 +35,109 @@ import 'mocks.dart' as _i9; // ignore_for_file: subtype_of_sealed_class class _FakeJsUtil_0 extends _i1.SmartFake implements _i2.JsUtil { - _FakeJsUtil_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeJsUtil_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } class _FakeZoomLevelCapability_1 extends _i1.SmartFake implements _i3.ZoomLevelCapability { - _FakeZoomLevelCapability_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeZoomLevelCapability_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } class _FakeSize_2 extends _i1.SmartFake implements _i4.Size { - _FakeSize_2(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeSize_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } -class _FakeCameraOptions_3 extends _i1.SmartFake implements _i3.CameraOptions { - _FakeCameraOptions_3(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeCameraImageData_3 extends _i1.SmartFake + implements _i5.CameraImageData { + _FakeCameraImageData_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } -class _FakeStreamController_4 extends _i1.SmartFake - implements _i5.StreamController { - _FakeStreamController_4(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeCameraOptions_4 extends _i1.SmartFake implements _i3.CameraOptions { + _FakeCameraOptions_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } -class _FakeEventStreamProvider_5 extends _i1.SmartFake - implements _i6.EventStreamProvider { - _FakeEventStreamProvider_5(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeStreamController_5 extends _i1.SmartFake + implements _i6.StreamController { + _FakeStreamController_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } -class _FakeXFile_6 extends _i1.SmartFake implements _i7.XFile { - _FakeXFile_6(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); +class _FakeEventStreamProvider_6 extends _i1.SmartFake + implements _i7.EventStreamProvider { + _FakeEventStreamProvider_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } -class _FakeAudioConstraints_7 extends _i1.SmartFake +class _FakeXFile_7 extends _i1.SmartFake implements _i5.XFile { + _FakeXFile_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAudioConstraints_8 extends _i1.SmartFake implements _i3.AudioConstraints { - _FakeAudioConstraints_7(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeAudioConstraints_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } -class _FakeVideoConstraints_8 extends _i1.SmartFake +class _FakeVideoConstraints_9 extends _i1.SmartFake implements _i3.VideoConstraints { - _FakeVideoConstraints_8(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeVideoConstraints_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } /// A class which mocks [CameraService]. @@ -89,179 +145,235 @@ class _FakeVideoConstraints_8 extends _i1.SmartFake /// See the documentation for Mockito's code generation for more information. class MockCameraService extends _i1.Mock implements _i8.CameraService { @override - _i6.Window get window => - (super.noSuchMethod( - Invocation.getter(#window), - returnValue: _i9.windowShim(), - returnValueForMissingStub: _i9.windowShim(), - ) - as _i6.Window); + _i7.Window get window => (super.noSuchMethod( + Invocation.getter(#window), + returnValue: _i9.windowShim(), + returnValueForMissingStub: _i9.windowShim(), + ) as _i7.Window); @override - set window(_i6.Window? _window) => super.noSuchMethod( - Invocation.setter(#window, _window), - returnValueForMissingStub: null, - ); + set window(_i7.Window? _window) => super.noSuchMethod( + Invocation.setter( + #window, + _window, + ), + returnValueForMissingStub: null, + ); @override - _i2.JsUtil get jsUtil => - (super.noSuchMethod( - Invocation.getter(#jsUtil), - returnValue: _FakeJsUtil_0(this, Invocation.getter(#jsUtil)), - returnValueForMissingStub: _FakeJsUtil_0( - this, - Invocation.getter(#jsUtil), - ), - ) - as _i2.JsUtil); + _i2.JsUtil get jsUtil => (super.noSuchMethod( + Invocation.getter(#jsUtil), + returnValue: _FakeJsUtil_0( + this, + Invocation.getter(#jsUtil), + ), + returnValueForMissingStub: _FakeJsUtil_0( + this, + Invocation.getter(#jsUtil), + ), + ) as _i2.JsUtil); @override set jsUtil(_i2.JsUtil? _jsUtil) => super.noSuchMethod( - Invocation.setter(#jsUtil, _jsUtil), - returnValueForMissingStub: null, - ); + Invocation.setter( + #jsUtil, + _jsUtil, + ), + returnValueForMissingStub: null, + ); @override - _i5.Future<_i6.MediaStream> getMediaStreamForOptions( + _i6.Future<_i7.MediaStream> getMediaStreamForOptions( _i3.CameraOptions? options, { int? cameraId = 0, }) => (super.noSuchMethod( - Invocation.method( - #getMediaStreamForOptions, - [options], - {#cameraId: cameraId}, - ), - returnValue: _i9.getMediaStreamForOptionsShim( - options, - cameraId: cameraId, - ), - returnValueForMissingStub: _i9.getMediaStreamForOptionsShim( - options, - cameraId: cameraId, - ), - ) - as _i5.Future<_i6.MediaStream>); + Invocation.method( + #getMediaStreamForOptions, + [options], + {#cameraId: cameraId}, + ), + returnValue: _i9.getMediaStreamForOptionsShim( + options, + cameraId: cameraId, + ), + returnValueForMissingStub: _i9.getMediaStreamForOptionsShim( + options, + cameraId: cameraId, + ), + ) as _i6.Future<_i7.MediaStream>); @override _i3.ZoomLevelCapability getZoomLevelCapabilityForCamera( - _i10.Camera? camera, - ) => - (super.noSuchMethod( - Invocation.method(#getZoomLevelCapabilityForCamera, [camera]), - returnValue: _FakeZoomLevelCapability_1( - this, - Invocation.method(#getZoomLevelCapabilityForCamera, [camera]), - ), - returnValueForMissingStub: _FakeZoomLevelCapability_1( - this, - Invocation.method(#getZoomLevelCapabilityForCamera, [camera]), - ), - ) - as _i3.ZoomLevelCapability); - - @override - String? getFacingModeForVideoTrack(_i6.MediaStreamTrack? videoTrack) => - (super.noSuchMethod( - Invocation.method(#getFacingModeForVideoTrack, [videoTrack]), - returnValueForMissingStub: null, - ) - as String?); + _i10.Camera? camera) => + (super.noSuchMethod( + Invocation.method( + #getZoomLevelCapabilityForCamera, + [camera], + ), + returnValue: _FakeZoomLevelCapability_1( + this, + Invocation.method( + #getZoomLevelCapabilityForCamera, + [camera], + ), + ), + returnValueForMissingStub: _FakeZoomLevelCapability_1( + this, + Invocation.method( + #getZoomLevelCapabilityForCamera, + [camera], + ), + ), + ) as _i3.ZoomLevelCapability); + + @override + String? getFacingModeForVideoTrack(_i7.MediaStreamTrack? videoTrack) => + (super.noSuchMethod( + Invocation.method( + #getFacingModeForVideoTrack, + [videoTrack], + ), + returnValueForMissingStub: null, + ) as String?); @override - _i7.CameraLensDirection mapFacingModeToLensDirection(String? facingMode) => + _i5.CameraLensDirection mapFacingModeToLensDirection(String? facingMode) => (super.noSuchMethod( - Invocation.method(#mapFacingModeToLensDirection, [facingMode]), - returnValue: _i7.CameraLensDirection.front, - returnValueForMissingStub: _i7.CameraLensDirection.front, - ) - as _i7.CameraLensDirection); + Invocation.method( + #mapFacingModeToLensDirection, + [facingMode], + ), + returnValue: _i5.CameraLensDirection.front, + returnValueForMissingStub: _i5.CameraLensDirection.front, + ) as _i5.CameraLensDirection); @override _i3.CameraType mapFacingModeToCameraType(String? facingMode) => (super.noSuchMethod( - Invocation.method(#mapFacingModeToCameraType, [facingMode]), - returnValue: _i3.CameraType.environment, - returnValueForMissingStub: _i3.CameraType.environment, - ) - as _i3.CameraType); - - @override - _i4.Size mapResolutionPresetToSize(_i7.ResolutionPreset? resolutionPreset) => - (super.noSuchMethod( - Invocation.method(#mapResolutionPresetToSize, [resolutionPreset]), - returnValue: _FakeSize_2( - this, - Invocation.method(#mapResolutionPresetToSize, [resolutionPreset]), - ), - returnValueForMissingStub: _FakeSize_2( - this, - Invocation.method(#mapResolutionPresetToSize, [resolutionPreset]), - ), - ) - as _i4.Size); + Invocation.method( + #mapFacingModeToCameraType, + [facingMode], + ), + returnValue: _i3.CameraType.environment, + returnValueForMissingStub: _i3.CameraType.environment, + ) as _i3.CameraType); + + @override + _i4.Size mapResolutionPresetToSize(_i5.ResolutionPreset? resolutionPreset) => + (super.noSuchMethod( + Invocation.method( + #mapResolutionPresetToSize, + [resolutionPreset], + ), + returnValue: _FakeSize_2( + this, + Invocation.method( + #mapResolutionPresetToSize, + [resolutionPreset], + ), + ), + returnValueForMissingStub: _FakeSize_2( + this, + Invocation.method( + #mapResolutionPresetToSize, + [resolutionPreset], + ), + ), + ) as _i4.Size); @override int mapResolutionPresetToVideoBitrate( - _i7.ResolutionPreset? resolutionPreset, - ) => + _i5.ResolutionPreset? resolutionPreset) => (super.noSuchMethod( - Invocation.method(#mapResolutionPresetToVideoBitrate, [ - resolutionPreset, - ]), - returnValue: 0, - returnValueForMissingStub: 0, - ) - as int); + Invocation.method( + #mapResolutionPresetToVideoBitrate, + [resolutionPreset], + ), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); @override int mapResolutionPresetToAudioBitrate( - _i7.ResolutionPreset? resolutionPreset, - ) => + _i5.ResolutionPreset? resolutionPreset) => (super.noSuchMethod( - Invocation.method(#mapResolutionPresetToAudioBitrate, [ - resolutionPreset, - ]), - returnValue: 0, - returnValueForMissingStub: 0, - ) - as int); + Invocation.method( + #mapResolutionPresetToAudioBitrate, + [resolutionPreset], + ), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); @override String mapDeviceOrientationToOrientationType( - _i11.DeviceOrientation? deviceOrientation, - ) => - (super.noSuchMethod( - Invocation.method(#mapDeviceOrientationToOrientationType, [ - deviceOrientation, - ]), - returnValue: _i12.dummyValue( - this, - Invocation.method(#mapDeviceOrientationToOrientationType, [ - deviceOrientation, - ]), - ), - returnValueForMissingStub: _i12.dummyValue( - this, - Invocation.method(#mapDeviceOrientationToOrientationType, [ - deviceOrientation, - ]), - ), - ) - as String); + _i11.DeviceOrientation? deviceOrientation) => + (super.noSuchMethod( + Invocation.method( + #mapDeviceOrientationToOrientationType, + [deviceOrientation], + ), + returnValue: _i12.dummyValue( + this, + Invocation.method( + #mapDeviceOrientationToOrientationType, + [deviceOrientation], + ), + ), + returnValueForMissingStub: _i12.dummyValue( + this, + Invocation.method( + #mapDeviceOrientationToOrientationType, + [deviceOrientation], + ), + ), + ) as String); @override _i11.DeviceOrientation mapOrientationTypeToDeviceOrientation( - String? orientationType, - ) => - (super.noSuchMethod( - Invocation.method(#mapOrientationTypeToDeviceOrientation, [ - orientationType, - ]), - returnValue: _i11.DeviceOrientation.portraitUp, - returnValueForMissingStub: _i11.DeviceOrientation.portraitUp, - ) - as _i11.DeviceOrientation); + String? orientationType) => + (super.noSuchMethod( + Invocation.method( + #mapOrientationTypeToDeviceOrientation, + [orientationType], + ), + returnValue: _i11.DeviceOrientation.portraitUp, + returnValueForMissingStub: _i11.DeviceOrientation.portraitUp, + ) as _i11.DeviceOrientation); + + @override + bool hasPropertyOffScreenCanvas() => (super.noSuchMethod( + Invocation.method( + #hasPropertyOffScreenCanvas, + [], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i5.CameraImageData takeFrame(_i7.HTMLVideoElement? videoElement) => + (super.noSuchMethod( + Invocation.method( + #takeFrame, + [videoElement], + ), + returnValue: _FakeCameraImageData_3( + this, + Invocation.method( + #takeFrame, + [videoElement], + ), + ), + returnValueForMissingStub: _FakeCameraImageData_3( + this, + Invocation.method( + #takeFrame, + [videoElement], + ), + ), + ) as _i5.CameraImageData); } /// A class which mocks [JsUtil]. @@ -269,21 +381,37 @@ class MockCameraService extends _i1.Mock implements _i8.CameraService { /// See the documentation for Mockito's code generation for more information. class MockJsUtil extends _i1.Mock implements _i2.JsUtil { @override - bool hasProperty(_i13.JSObject? o, _i13.JSAny? name) => + bool hasProperty( + _i13.JSObject? o, + _i13.JSAny? name, + ) => (super.noSuchMethod( - Invocation.method(#hasProperty, [o, name]), - returnValue: false, - returnValueForMissingStub: false, - ) - as bool); - - @override - _i13.JSAny? getProperty(_i13.JSObject? o, _i13.JSAny? name) => + Invocation.method( + #hasProperty, + [ + o, + name, + ], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i13.JSAny? getProperty( + _i13.JSObject? o, + _i13.JSAny? name, + ) => (super.noSuchMethod( - Invocation.method(#getProperty, [o, name]), - returnValueForMissingStub: null, - ) - as _i13.JSAny?); + Invocation.method( + #getProperty, + [ + o, + name, + ], + ), + returnValueForMissingStub: null, + ) as _i13.JSAny?); } /// A class which mocks [Camera]. @@ -291,385 +419,473 @@ class MockJsUtil extends _i1.Mock implements _i2.JsUtil { /// See the documentation for Mockito's code generation for more information. class MockCamera extends _i1.Mock implements _i10.Camera { @override - int get textureId => - (super.noSuchMethod( - Invocation.getter(#textureId), - returnValue: 0, - returnValueForMissingStub: 0, - ) - as int); + int get textureId => (super.noSuchMethod( + Invocation.getter(#textureId), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); @override - _i3.CameraOptions get options => - (super.noSuchMethod( - Invocation.getter(#options), - returnValue: _FakeCameraOptions_3( - this, - Invocation.getter(#options), - ), - returnValueForMissingStub: _FakeCameraOptions_3( - this, - Invocation.getter(#options), - ), - ) - as _i3.CameraOptions); + _i3.CameraOptions get options => (super.noSuchMethod( + Invocation.getter(#options), + returnValue: _FakeCameraOptions_4( + this, + Invocation.getter(#options), + ), + returnValueForMissingStub: _FakeCameraOptions_4( + this, + Invocation.getter(#options), + ), + ) as _i3.CameraOptions); @override ({int? audioBitrate, int? videoBitrate}) get recorderOptions => (super.noSuchMethod( - Invocation.getter(#recorderOptions), - returnValue: (audioBitrate: null, videoBitrate: null), - returnValueForMissingStub: (audioBitrate: null, videoBitrate: null), - ) - as ({int? audioBitrate, int? videoBitrate})); + Invocation.getter(#recorderOptions), + returnValue: (audioBitrate: null, videoBitrate: null), + returnValueForMissingStub: (audioBitrate: null, videoBitrate: null), + ) as ({int? audioBitrate, int? videoBitrate})); @override - _i6.HTMLVideoElement get videoElement => - (super.noSuchMethod( - Invocation.getter(#videoElement), - returnValue: _i9.videoElementShim(), - returnValueForMissingStub: _i9.videoElementShim(), - ) - as _i6.HTMLVideoElement); + _i7.HTMLVideoElement get videoElement => (super.noSuchMethod( + Invocation.getter(#videoElement), + returnValue: _i9.videoElementShim(), + returnValueForMissingStub: _i9.videoElementShim(), + ) as _i7.HTMLVideoElement); @override - set videoElement(_i6.HTMLVideoElement? _videoElement) => super.noSuchMethod( - Invocation.setter(#videoElement, _videoElement), - returnValueForMissingStub: null, - ); + set videoElement(_i7.HTMLVideoElement? _videoElement) => super.noSuchMethod( + Invocation.setter( + #videoElement, + _videoElement, + ), + returnValueForMissingStub: null, + ); @override - _i6.HTMLDivElement get divElement => - (super.noSuchMethod( - Invocation.getter(#divElement), - returnValue: _i9.divElementShim(), - returnValueForMissingStub: _i9.divElementShim(), - ) - as _i6.HTMLDivElement); + _i7.HTMLDivElement get divElement => (super.noSuchMethod( + Invocation.getter(#divElement), + returnValue: _i9.divElementShim(), + returnValueForMissingStub: _i9.divElementShim(), + ) as _i7.HTMLDivElement); @override - set divElement(_i6.HTMLDivElement? _divElement) => super.noSuchMethod( - Invocation.setter(#divElement, _divElement), - returnValueForMissingStub: null, - ); + set divElement(_i7.HTMLDivElement? _divElement) => super.noSuchMethod( + Invocation.setter( + #divElement, + _divElement, + ), + returnValueForMissingStub: null, + ); @override - set stream(_i6.MediaStream? _stream) => super.noSuchMethod( - Invocation.setter(#stream, _stream), - returnValueForMissingStub: null, - ); + set stream(_i7.MediaStream? _stream) => super.noSuchMethod( + Invocation.setter( + #stream, + _stream, + ), + returnValueForMissingStub: null, + ); @override - _i5.StreamController<_i6.MediaStreamTrack> get onEndedController => + _i6.StreamController<_i7.MediaStreamTrack> get onEndedController => (super.noSuchMethod( - Invocation.getter(#onEndedController), - returnValue: _FakeStreamController_4<_i6.MediaStreamTrack>( - this, - Invocation.getter(#onEndedController), - ), - returnValueForMissingStub: - _FakeStreamController_4<_i6.MediaStreamTrack>( - this, - Invocation.getter(#onEndedController), - ), - ) - as _i5.StreamController<_i6.MediaStreamTrack>); - - @override - _i6.EventStreamProvider<_i6.Event> get mediaRecorderOnErrorProvider => + Invocation.getter(#onEndedController), + returnValue: _FakeStreamController_5<_i7.MediaStreamTrack>( + this, + Invocation.getter(#onEndedController), + ), + returnValueForMissingStub: + _FakeStreamController_5<_i7.MediaStreamTrack>( + this, + Invocation.getter(#onEndedController), + ), + ) as _i6.StreamController<_i7.MediaStreamTrack>); + + @override + _i7.EventStreamProvider<_i7.Event> get mediaRecorderOnErrorProvider => (super.noSuchMethod( - Invocation.getter(#mediaRecorderOnErrorProvider), - returnValue: _FakeEventStreamProvider_5<_i6.Event>( - this, - Invocation.getter(#mediaRecorderOnErrorProvider), - ), - returnValueForMissingStub: _FakeEventStreamProvider_5<_i6.Event>( - this, - Invocation.getter(#mediaRecorderOnErrorProvider), - ), - ) - as _i6.EventStreamProvider<_i6.Event>); + Invocation.getter(#mediaRecorderOnErrorProvider), + returnValue: _FakeEventStreamProvider_6<_i7.Event>( + this, + Invocation.getter(#mediaRecorderOnErrorProvider), + ), + returnValueForMissingStub: _FakeEventStreamProvider_6<_i7.Event>( + this, + Invocation.getter(#mediaRecorderOnErrorProvider), + ), + ) as _i7.EventStreamProvider<_i7.Event>); @override set mediaRecorderOnErrorProvider( - _i6.EventStreamProvider<_i6.Event>? _mediaRecorderOnErrorProvider, - ) => super.noSuchMethod( - Invocation.setter( - #mediaRecorderOnErrorProvider, - _mediaRecorderOnErrorProvider, - ), - returnValueForMissingStub: null, - ); + _i7.EventStreamProvider<_i7.Event>? _mediaRecorderOnErrorProvider) => + super.noSuchMethod( + Invocation.setter( + #mediaRecorderOnErrorProvider, + _mediaRecorderOnErrorProvider, + ), + returnValueForMissingStub: null, + ); @override - _i5.StreamController<_i6.ErrorEvent> get videoRecordingErrorController => - (super.noSuchMethod( - Invocation.getter(#videoRecordingErrorController), - returnValue: _FakeStreamController_4<_i6.ErrorEvent>( - this, - Invocation.getter(#videoRecordingErrorController), - ), - returnValueForMissingStub: _FakeStreamController_4<_i6.ErrorEvent>( - this, - Invocation.getter(#videoRecordingErrorController), - ), - ) - as _i5.StreamController<_i6.ErrorEvent>); - - @override - set flashMode(_i7.FlashMode? _flashMode) => super.noSuchMethod( - Invocation.setter(#flashMode, _flashMode), - returnValueForMissingStub: null, - ); - - @override - _i6.Window get window => + _i6.StreamController<_i7.ErrorEvent> get videoRecordingErrorController => (super.noSuchMethod( - Invocation.getter(#window), - returnValue: _i9.windowShim(), - returnValueForMissingStub: _i9.windowShim(), - ) - as _i6.Window); + Invocation.getter(#videoRecordingErrorController), + returnValue: _FakeStreamController_5<_i7.ErrorEvent>( + this, + Invocation.getter(#videoRecordingErrorController), + ), + returnValueForMissingStub: _FakeStreamController_5<_i7.ErrorEvent>( + this, + Invocation.getter(#videoRecordingErrorController), + ), + ) as _i6.StreamController<_i7.ErrorEvent>); @override - set window(_i6.Window? _window) => super.noSuchMethod( - Invocation.setter(#window, _window), - returnValueForMissingStub: null, - ); + set flashMode(_i5.FlashMode? _flashMode) => super.noSuchMethod( + Invocation.setter( + #flashMode, + _flashMode, + ), + returnValueForMissingStub: null, + ); @override - set mediaRecorder(_i6.MediaRecorder? _mediaRecorder) => super.noSuchMethod( - Invocation.setter(#mediaRecorder, _mediaRecorder), - returnValueForMissingStub: null, - ); + _i7.Window get window => (super.noSuchMethod( + Invocation.getter(#window), + returnValue: _i9.windowShim(), + returnValueForMissingStub: _i9.windowShim(), + ) as _i7.Window); @override - bool Function(String) get isVideoTypeSupported => - (super.noSuchMethod( - Invocation.getter(#isVideoTypeSupported), - returnValue: (String __p0) => false, - returnValueForMissingStub: (String __p0) => false, - ) - as bool Function(String)); + set window(_i7.Window? _window) => super.noSuchMethod( + Invocation.setter( + #window, + _window, + ), + returnValueForMissingStub: null, + ); @override - set isVideoTypeSupported(bool Function(String)? _isVideoTypeSupported) => - super.noSuchMethod( - Invocation.setter(#isVideoTypeSupported, _isVideoTypeSupported), + set mediaRecorder(_i7.MediaRecorder? _mediaRecorder) => super.noSuchMethod( + Invocation.setter( + #mediaRecorder, + _mediaRecorder, + ), returnValueForMissingStub: null, ); @override - _i6.Blob Function(List<_i6.Blob>, String) get blobBuilder => - (super.noSuchMethod( - Invocation.getter(#blobBuilder), - returnValue: _i9.blobBuilderShim(), - returnValueForMissingStub: _i9.blobBuilderShim(), - ) - as _i6.Blob Function(List<_i6.Blob>, String)); + bool Function(String) get isVideoTypeSupported => (super.noSuchMethod( + Invocation.getter(#isVideoTypeSupported), + returnValue: (String __p0) => false, + returnValueForMissingStub: (String __p0) => false, + ) as bool Function(String)); @override - set blobBuilder(_i6.Blob Function(List<_i6.Blob>, String)? _blobBuilder) => + set isVideoTypeSupported(bool Function(String)? _isVideoTypeSupported) => super.noSuchMethod( - Invocation.setter(#blobBuilder, _blobBuilder), + Invocation.setter( + #isVideoTypeSupported, + _isVideoTypeSupported, + ), returnValueForMissingStub: null, ); @override - _i5.StreamController<_i7.VideoRecordedEvent> get videoRecorderController => - (super.noSuchMethod( - Invocation.getter(#videoRecorderController), - returnValue: _FakeStreamController_4<_i7.VideoRecordedEvent>( - this, - Invocation.getter(#videoRecorderController), - ), - returnValueForMissingStub: - _FakeStreamController_4<_i7.VideoRecordedEvent>( - this, - Invocation.getter(#videoRecorderController), - ), - ) - as _i5.StreamController<_i7.VideoRecordedEvent>); - - @override - _i5.Stream<_i6.MediaStreamTrack> get onEnded => - (super.noSuchMethod( - Invocation.getter(#onEnded), - returnValue: _i5.Stream<_i6.MediaStreamTrack>.empty(), - returnValueForMissingStub: _i5.Stream<_i6.MediaStreamTrack>.empty(), - ) - as _i5.Stream<_i6.MediaStreamTrack>); + _i7.Blob Function( + List<_i7.Blob>, + String, + ) get blobBuilder => (super.noSuchMethod( + Invocation.getter(#blobBuilder), + returnValue: _i9.blobBuilderShim(), + returnValueForMissingStub: _i9.blobBuilderShim(), + ) as _i7.Blob Function( + List<_i7.Blob>, + String, + )); + + @override + set blobBuilder( + _i7.Blob Function( + List<_i7.Blob>, + String, + )? _blobBuilder) => + super.noSuchMethod( + Invocation.setter( + #blobBuilder, + _blobBuilder, + ), + returnValueForMissingStub: null, + ); @override - _i5.Stream<_i6.ErrorEvent> get onVideoRecordingError => + _i6.StreamController<_i5.VideoRecordedEvent> get videoRecorderController => (super.noSuchMethod( - Invocation.getter(#onVideoRecordingError), - returnValue: _i5.Stream<_i6.ErrorEvent>.empty(), - returnValueForMissingStub: _i5.Stream<_i6.ErrorEvent>.empty(), - ) - as _i5.Stream<_i6.ErrorEvent>); + Invocation.getter(#videoRecorderController), + returnValue: _FakeStreamController_5<_i5.VideoRecordedEvent>( + this, + Invocation.getter(#videoRecorderController), + ), + returnValueForMissingStub: + _FakeStreamController_5<_i5.VideoRecordedEvent>( + this, + Invocation.getter(#videoRecorderController), + ), + ) as _i6.StreamController<_i5.VideoRecordedEvent>); @override - _i5.Stream<_i7.VideoRecordedEvent> get onVideoRecordedEvent => - (super.noSuchMethod( - Invocation.getter(#onVideoRecordedEvent), - returnValue: _i5.Stream<_i7.VideoRecordedEvent>.empty(), - returnValueForMissingStub: - _i5.Stream<_i7.VideoRecordedEvent>.empty(), - ) - as _i5.Stream<_i7.VideoRecordedEvent>); + bool get canUseOffscreenCanvas => (super.noSuchMethod( + Invocation.getter(#canUseOffscreenCanvas), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); @override - _i5.Future initialize() => - (super.noSuchMethod( - Invocation.method(#initialize, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); + int get cameraStreamFPS => (super.noSuchMethod( + Invocation.getter(#cameraStreamFPS), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + + @override + _i6.Stream<_i7.MediaStreamTrack> get onEnded => (super.noSuchMethod( + Invocation.getter(#onEnded), + returnValue: _i6.Stream<_i7.MediaStreamTrack>.empty(), + returnValueForMissingStub: _i6.Stream<_i7.MediaStreamTrack>.empty(), + ) as _i6.Stream<_i7.MediaStreamTrack>); + + @override + _i6.Stream<_i7.ErrorEvent> get onVideoRecordingError => (super.noSuchMethod( + Invocation.getter(#onVideoRecordingError), + returnValue: _i6.Stream<_i7.ErrorEvent>.empty(), + returnValueForMissingStub: _i6.Stream<_i7.ErrorEvent>.empty(), + ) as _i6.Stream<_i7.ErrorEvent>); @override - _i5.Future play() => + _i6.Stream<_i5.VideoRecordedEvent> get onVideoRecordedEvent => (super.noSuchMethod( - Invocation.method(#play, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); + Invocation.getter(#onVideoRecordedEvent), + returnValue: _i6.Stream<_i5.VideoRecordedEvent>.empty(), + returnValueForMissingStub: _i6.Stream<_i5.VideoRecordedEvent>.empty(), + ) as _i6.Stream<_i5.VideoRecordedEvent>); @override - void pause() => super.noSuchMethod( - Invocation.method(#pause, []), - returnValueForMissingStub: null, - ); + _i6.Future initialize() => (super.noSuchMethod( + Invocation.method( + #initialize, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - void stop() => super.noSuchMethod( - Invocation.method(#stop, []), - returnValueForMissingStub: null, - ); + _i6.Future play() => (super.noSuchMethod( + Invocation.method( + #play, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future<_i7.XFile> takePicture() => - (super.noSuchMethod( - Invocation.method(#takePicture, []), - returnValue: _i5.Future<_i7.XFile>.value( - _FakeXFile_6(this, Invocation.method(#takePicture, [])), - ), - returnValueForMissingStub: _i5.Future<_i7.XFile>.value( - _FakeXFile_6(this, Invocation.method(#takePicture, [])), - ), - ) - as _i5.Future<_i7.XFile>); - - @override - _i4.Size getVideoSize() => - (super.noSuchMethod( - Invocation.method(#getVideoSize, []), - returnValue: _FakeSize_2( - this, - Invocation.method(#getVideoSize, []), - ), - returnValueForMissingStub: _FakeSize_2( - this, - Invocation.method(#getVideoSize, []), - ), - ) - as _i4.Size); - - @override - void setFlashMode(_i7.FlashMode? mode) => super.noSuchMethod( - Invocation.method(#setFlashMode, [mode]), - returnValueForMissingStub: null, - ); - - @override - double getMaxZoomLevel() => - (super.noSuchMethod( - Invocation.method(#getMaxZoomLevel, []), - returnValue: 0.0, - returnValueForMissingStub: 0.0, - ) - as double); + void pause() => super.noSuchMethod( + Invocation.method( + #pause, + [], + ), + returnValueForMissingStub: null, + ); @override - double getMinZoomLevel() => - (super.noSuchMethod( - Invocation.method(#getMinZoomLevel, []), - returnValue: 0.0, - returnValueForMissingStub: 0.0, - ) - as double); + void stop() => super.noSuchMethod( + Invocation.method( + #stop, + [], + ), + returnValueForMissingStub: null, + ); @override - void setZoomLevel(double? zoom) => super.noSuchMethod( - Invocation.method(#setZoomLevel, [zoom]), - returnValueForMissingStub: null, - ); + _i6.Future<_i5.XFile> takePicture() => (super.noSuchMethod( + Invocation.method( + #takePicture, + [], + ), + returnValue: _i6.Future<_i5.XFile>.value(_FakeXFile_7( + this, + Invocation.method( + #takePicture, + [], + ), + )), + returnValueForMissingStub: _i6.Future<_i5.XFile>.value(_FakeXFile_7( + this, + Invocation.method( + #takePicture, + [], + ), + )), + ) as _i6.Future<_i5.XFile>); + + @override + _i4.Size getVideoSize() => (super.noSuchMethod( + Invocation.method( + #getVideoSize, + [], + ), + returnValue: _FakeSize_2( + this, + Invocation.method( + #getVideoSize, + [], + ), + ), + returnValueForMissingStub: _FakeSize_2( + this, + Invocation.method( + #getVideoSize, + [], + ), + ), + ) as _i4.Size); + + @override + void setFlashMode(_i5.FlashMode? mode) => super.noSuchMethod( + Invocation.method( + #setFlashMode, + [mode], + ), + returnValueForMissingStub: null, + ); @override - String getViewType() => - (super.noSuchMethod( - Invocation.method(#getViewType, []), - returnValue: _i12.dummyValue( - this, - Invocation.method(#getViewType, []), - ), - returnValueForMissingStub: _i12.dummyValue( - this, - Invocation.method(#getViewType, []), - ), - ) - as String); - - @override - _i5.Future startVideoRecording() => - (super.noSuchMethod( - Invocation.method(#startVideoRecording, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); + double getMaxZoomLevel() => (super.noSuchMethod( + Invocation.method( + #getMaxZoomLevel, + [], + ), + returnValue: 0.0, + returnValueForMissingStub: 0.0, + ) as double); @override - _i5.Future pauseVideoRecording() => - (super.noSuchMethod( - Invocation.method(#pauseVideoRecording, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); + double getMinZoomLevel() => (super.noSuchMethod( + Invocation.method( + #getMinZoomLevel, + [], + ), + returnValue: 0.0, + returnValueForMissingStub: 0.0, + ) as double); @override - _i5.Future resumeVideoRecording() => - (super.noSuchMethod( - Invocation.method(#resumeVideoRecording, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); + void setZoomLevel(double? zoom) => super.noSuchMethod( + Invocation.method( + #setZoomLevel, + [zoom], + ), + returnValueForMissingStub: null, + ); @override - _i5.Future<_i7.XFile> stopVideoRecording() => - (super.noSuchMethod( - Invocation.method(#stopVideoRecording, []), - returnValue: _i5.Future<_i7.XFile>.value( - _FakeXFile_6(this, Invocation.method(#stopVideoRecording, [])), - ), - returnValueForMissingStub: _i5.Future<_i7.XFile>.value( - _FakeXFile_6(this, Invocation.method(#stopVideoRecording, [])), - ), - ) - as _i5.Future<_i7.XFile>); - - @override - _i5.Future dispose() => - (super.noSuchMethod( - Invocation.method(#dispose, []), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) - as _i5.Future); + String getViewType() => (super.noSuchMethod( + Invocation.method( + #getViewType, + [], + ), + returnValue: _i12.dummyValue( + this, + Invocation.method( + #getViewType, + [], + ), + ), + returnValueForMissingStub: _i12.dummyValue( + this, + Invocation.method( + #getViewType, + [], + ), + ), + ) as String); + + @override + _i6.Future startVideoRecording() => (super.noSuchMethod( + Invocation.method( + #startVideoRecording, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future pauseVideoRecording() => (super.noSuchMethod( + Invocation.method( + #pauseVideoRecording, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future resumeVideoRecording() => (super.noSuchMethod( + Invocation.method( + #resumeVideoRecording, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future<_i5.XFile> stopVideoRecording() => (super.noSuchMethod( + Invocation.method( + #stopVideoRecording, + [], + ), + returnValue: _i6.Future<_i5.XFile>.value(_FakeXFile_7( + this, + Invocation.method( + #stopVideoRecording, + [], + ), + )), + returnValueForMissingStub: _i6.Future<_i5.XFile>.value(_FakeXFile_7( + this, + Invocation.method( + #stopVideoRecording, + [], + ), + )), + ) as _i6.Future<_i5.XFile>); + + @override + _i6.Future dispose() => (super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Stream<_i5.CameraImageData> cameraFrameStream( + {_i5.CameraImageStreamOptions? options}) => + (super.noSuchMethod( + Invocation.method( + #cameraFrameStream, + [], + {#options: options}, + ), + returnValue: _i6.Stream<_i5.CameraImageData>.empty(), + returnValueForMissingStub: _i6.Stream<_i5.CameraImageData>.empty(), + ) as _i6.Stream<_i5.CameraImageData>); } /// A class which mocks [CameraOptions]. @@ -678,41 +894,38 @@ class MockCamera extends _i1.Mock implements _i10.Camera { // ignore: must_be_immutable class MockCameraOptions extends _i1.Mock implements _i3.CameraOptions { @override - _i3.AudioConstraints get audio => - (super.noSuchMethod( - Invocation.getter(#audio), - returnValue: _FakeAudioConstraints_7( - this, - Invocation.getter(#audio), - ), - returnValueForMissingStub: _FakeAudioConstraints_7( - this, - Invocation.getter(#audio), - ), - ) - as _i3.AudioConstraints); - - @override - _i3.VideoConstraints get video => - (super.noSuchMethod( - Invocation.getter(#video), - returnValue: _FakeVideoConstraints_8( - this, - Invocation.getter(#video), - ), - returnValueForMissingStub: _FakeVideoConstraints_8( - this, - Invocation.getter(#video), - ), - ) - as _i3.VideoConstraints); - - @override - _i6.MediaStreamConstraints toMediaStreamConstraints() => - (super.noSuchMethod( - Invocation.method(#toMediaStreamConstraints, []), - returnValue: _i9.toMediaStreamConstraintsShim(), - returnValueForMissingStub: _i9.toMediaStreamConstraintsShim(), - ) - as _i6.MediaStreamConstraints); + _i3.AudioConstraints get audio => (super.noSuchMethod( + Invocation.getter(#audio), + returnValue: _FakeAudioConstraints_8( + this, + Invocation.getter(#audio), + ), + returnValueForMissingStub: _FakeAudioConstraints_8( + this, + Invocation.getter(#audio), + ), + ) as _i3.AudioConstraints); + + @override + _i3.VideoConstraints get video => (super.noSuchMethod( + Invocation.getter(#video), + returnValue: _FakeVideoConstraints_9( + this, + Invocation.getter(#video), + ), + returnValueForMissingStub: _FakeVideoConstraints_9( + this, + Invocation.getter(#video), + ), + ) as _i3.VideoConstraints); + + @override + _i7.MediaStreamConstraints toMediaStreamConstraints() => (super.noSuchMethod( + Invocation.method( + #toMediaStreamConstraints, + [], + ), + returnValue: _i9.toMediaStreamConstraintsShim(), + returnValueForMissingStub: _i9.toMediaStreamConstraintsShim(), + ) as _i7.MediaStreamConstraints); } diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 9b5659edfff..667e7049006 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -49,8 +49,8 @@ class Camera { required CameraService cameraService, this.options = const CameraOptions(), this.recorderOptions = const (audioBitrate: null, videoBitrate: null), - }) : _cameraService = cameraService, - canUseOffscreenCanvas = cameraService.hasPropertyOffScreenCanvas(); + }) : _cameraService = cameraService, + canUseOffscreenCanvas = cameraService.hasPropertyOffScreenCanvas(); /// The texture id used to register the camera view. final int textureId; @@ -162,7 +162,10 @@ class Camera { /// Used to check if allowed to paint canvas off screen @visibleForTesting - bool canUseOffscreenCanvas = false; + final bool canUseOffscreenCanvas; + + /// The tolerance for the camera streaming frame time. + final int _frameTimeToleranceMs = 8; /// Initializes the camera stream displayed in the [videoElement]. /// Registers the camera view with [textureId] under [_getViewType] type. @@ -655,8 +658,8 @@ class Camera { final StreamController _cameraFrameStreamController = StreamController.broadcast(); - // TODO(replace): introduced fps in - /// [CameraImageStreamOptions] + // TODO(TecHaxter): package:camera_platform_interface has CameraImageStreamOptions class. FPS should be introduced there instead of here. + /// Used for running the camera frame stream at a specified FPS final int cameraStreamFPS = 60; /// Returns a stream of camera frames. @@ -677,23 +680,21 @@ class Camera { /// Triggers animation frames in a loop at a specified FPS /// as long as [animationFrameId] is not cancelled - void _triggerAnimationFramesLoop( - VoidCallback action, { - required int fps, - }) { + void _triggerAnimationFramesLoop(VoidCallback action, {required int fps}) { int? animationFrameId; - num then = 0, fpsInterval = 1000 / fps; + final num fpsInterval = 1000 / fps; + num lastFrameTimestamp = 0; int? animate(num timestamp) { // Schedule the next frame animationFrameId = window.requestAnimationFrame(animate.toJS); // Calculate the elapsed time since the last frame - final num elapsed = timestamp - then; + final num elapsed = timestamp - lastFrameTimestamp; - // if we're close to the next frame (by ~8ms), do it. - if (fpsInterval - elapsed <= 8) { + // If we're close to the next frame (~`_frameTimeToleranceMs`), do it. + if (fpsInterval - elapsed <= _frameTimeToleranceMs) { // Get ready for next frame - then = timestamp; + lastFrameTimestamp = timestamp; // Perform the action task action(); } diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index c7152d11b64..5cd81c9d8a4 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -366,16 +366,14 @@ class CameraService { return jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS); } - ///Used in [takeFrame] if [canUseOffscreenCanvas] is false + ///Used in [takeFrame] if `OffscreenCanvas` is not supported web.CanvasElement? _canvasElement; - ///Used in [takeFrame] if [canUseOffscreenCanvas] is false + ///Used in [takeFrame] if `OffscreenCanvas` is supported web.OffscreenCanvas? _offscreenCanvas; ///Returns frame at a specific time using video element - CameraImageData takeFrame( - web.VideoElement videoElement, - ) { + CameraImageData takeFrame(web.VideoElement videoElement) { final int width = videoElement.videoWidth; final int height = videoElement.videoHeight; if (width == 0 || height == 0) { @@ -392,18 +390,20 @@ class CameraService { } final web.OffscreenCanvasRenderingContext2D context = _offscreenCanvas!.getContext( - '2d', - {'willReadFrequently': true}.jsify(), - )! as web.OffscreenCanvasRenderingContext2D; + '2d', + {'willReadFrequently': true}.jsify(), + )! + as web.OffscreenCanvasRenderingContext2D; context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); } else { if (_canvasElement == null || _canvasElement!.width != width || _canvasElement!.height != height) { - _canvasElement = web.CanvasElement() - ..height = height - ..width = width; + _canvasElement = + web.CanvasElement() + ..height = height + ..width = width; } final web.CanvasRenderingContext2D context = _canvasElement!.context2D; @@ -419,10 +419,7 @@ class CameraService { final ByteBuffer byteBuffer = imageData.data.toDart.buffer; return CameraImageData( - format: const CameraImageFormat( - ImageFormatGroup.unknown, - raw: 0, - ), + format: const CameraImageFormat(ImageFormatGroup.unknown, raw: 0), planes: [ CameraImagePlane( bytes: byteBuffer.asUint8List(), From f0a1d27531a561b2798fc42377ec95f54fcd1085 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sat, 27 Sep 2025 22:20:10 +0530 Subject: [PATCH 51/68] [camera_web] fix: reformatting --- packages/camera/camera_web/CHANGELOG.md | 80 +-- .../integration_test/camera_web_test.dart | 655 +++++++++--------- 2 files changed, 370 insertions(+), 365 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index fb025bbe8af..4017585f5c5 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,114 +1,114 @@ ## 0.3.6 -- Adds support for camera image stream on web. -- Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. +* Adds support for camera image stream on web. +* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. ## 0.3.5 -- Migrates to package:web to support WASM -- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Migrates to package:web to support WASM +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.3.4 -- Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at +* Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at the app-facing package level, and is deprecated at the platform interface level. -- Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. +* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 0.3.3 -- Adds support to control video FPS and bitrate. See `CameraController.withSettings`. -- Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Adds support to control video FPS and bitrate. See `CameraController.withSettings`. +* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.3.2+4 -- Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. -- Fixes new lint warnings. +* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. +* Fixes new lint warnings. ## 0.3.2+3 -- Migrates to `dart:ui_web` APIs. -- Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. +* Migrates to `dart:ui_web` APIs. +* Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. ## 0.3.2+2 -- Adds pub topics to package metadata. -- Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. ## 0.3.2+1 -- Updates README to improve example of `Image` creation. +* Updates README to improve example of `Image` creation. ## 0.3.2 -- Changes `availableCameras` to not ask for the microphone permission. +* Changes `availableCameras` to not ask for the microphone permission. ## 0.3.1+4 -- Removes obsolete null checks on non-nullable values. -- Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +* Removes obsolete null checks on non-nullable values. +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 0.3.1+3 -- Clarifies explanation of endorsement in README. -- Aligns Dart and Flutter SDK constraints. +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. ## 0.3.1+2 -- Updates links for the merge of flutter/plugins into flutter/packages. -- Updates minimum Flutter version to 3.0. +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates minimum Flutter version to 3.0. ## 0.3.1+1 -- Updates code for stricter lint checks. +* Updates code for stricter lint checks. ## 0.3.1 -- Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). +* Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). ## 0.3.0+1 -- Updates imports for `prefer_relative_imports`. -- Updates minimum Flutter version to 2.10. -- Fixes avoid_redundant_argument_values lint warnings and minor typos. -- Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Updates imports for `prefer_relative_imports`. +* Updates minimum Flutter version to 2.10. +* Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.3.0 -- **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. +* **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. ## 0.2.1+6 -- Minor fixes for new analysis options. +* Minor fixes for new analysis options. ## 0.2.1+5 -- Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors lint warnings. ## 0.2.1+4 -- Migrates from `ui.hash*` to `Object.hash*`. -- Updates minimum Flutter version for changes in 0.2.1+3. +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version for changes in 0.2.1+3. ## 0.2.1+3 -- Internal code cleanup for stricter analysis options. +* Internal code cleanup for stricter analysis options. ## 0.2.1+2 -- Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. -- Implemented support for new Dart SDKs with an async requestFullscreen API. +* Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. +* Implemented support for new Dart SDKs with an async requestFullscreen API. ## 0.2.1+1 -- Update usage documentation. +* Update usage documentation. ## 0.2.1 -- Add video recording functionality. -- Fix cameraNotReadable error that prevented access to the camera on some Android devices. +* Add video recording functionality. +* Fix cameraNotReadable error that prevented access to the camera on some Android devices. ## 0.2.0 -- Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. +* Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 73a7c81dabc..e8daefb0006 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -111,11 +111,12 @@ void main() { setUp(() { when(cameraService.getFacingModeForVideoTrack(any)).thenReturn(null); - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [].toJS, - ).toJS; - }.toJS; + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [].toJS, + ).toJS; + }.toJS; }); testWidgets('requests video permissions', (WidgetTester tester) async { @@ -127,25 +128,26 @@ void main() { ).called(1); }); - testWidgets( - 'releases the camera stream ' + testWidgets('releases the camera stream ' 'used to request video permissions', (WidgetTester tester) async { final MockMediaStreamTrack mockVideoTrack = MockMediaStreamTrack(); final MediaStreamTrack videoTrack = createJSInteropWrapper(mockVideoTrack) as MediaStreamTrack; bool videoTrackStopped = false; - mockVideoTrack.stop = () { - videoTrackStopped = true; - }.toJS; + mockVideoTrack.stop = + () { + videoTrackStopped = true; + }.toJS; when( cameraService.getMediaStreamForOptions(const CameraOptions()), ).thenAnswer( (_) => Future.value( createJSInteropWrapper( - FakeMediaStream([videoTrack]), - ) as MediaStream, + FakeMediaStream([videoTrack]), + ) + as MediaStream, ), ); @@ -155,22 +157,24 @@ void main() { expect(videoTrackStopped, isTrue); }); - testWidgets( - 'gets a video stream ' + testWidgets('gets a video stream ' 'for a video input device', (WidgetTester tester) async { - final MediaDeviceInfo videoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '1', - 'Camera 1', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; - - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [videoDevice].toJS, - ).toJS; - }.toJS; + final MediaDeviceInfo videoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '1', + 'Camera 1', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; + + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [videoDevice].toJS, + ).toJS; + }.toJS; final List _ = await CameraPlatform.instance.availableCameras(); @@ -184,23 +188,25 @@ void main() { ).called(1); }); - testWidgets( - 'does not get a video stream ' + testWidgets('does not get a video stream ' 'for the video input device ' 'with an empty device id', (WidgetTester tester) async { - final MediaDeviceInfo videoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '', - 'Camera 1', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; - - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [videoDevice].toJS, - ).toJS; - }.toJS; + final MediaDeviceInfo videoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '', + 'Camera 1', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; + + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [videoDevice].toJS, + ).toJS; + }.toJS; final List _ = await CameraPlatform.instance.availableCameras(); @@ -214,24 +220,29 @@ void main() { ); }); - testWidgets( - 'gets the facing mode ' + testWidgets('gets the facing mode ' 'from the first available video track ' 'of the video input device', (WidgetTester tester) async { - final MediaDeviceInfo videoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '1', - 'Camera 1', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; + final MediaDeviceInfo videoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '1', + 'Camera 1', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; - final MediaStream videoStream = createJSInteropWrapper( - FakeMediaStream([ - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - ]), - ) as MediaStream; + final MediaStream videoStream = + createJSInteropWrapper( + FakeMediaStream([ + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + ]), + ) + as MediaStream; when( cameraService.getMediaStreamForOptions( @@ -241,11 +252,12 @@ void main() { ), ).thenAnswer((_) => Future.value(videoStream)); - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [videoDevice].toJS, - ).toJS; - }.toJS; + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [videoDevice].toJS, + ).toJS; + }.toJS; final List _ = await CameraPlatform.instance.availableCameras(); @@ -257,65 +269,78 @@ void main() { ).called(1); }); - testWidgets( - 'returns appropriate camera descriptions ' + testWidgets('returns appropriate camera descriptions ' 'for multiple video devices ' 'based on video streams', (WidgetTester tester) async { - final MediaDeviceInfo firstVideoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '1', - 'Camera 1', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; - - final MediaDeviceInfo secondVideoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '4', - 'Camera 4', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; + final MediaDeviceInfo firstVideoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '1', + 'Camera 1', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; + + final MediaDeviceInfo secondVideoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '4', + 'Camera 4', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; // Create a video stream for the first video device. - final MediaStream firstVideoStream = createJSInteropWrapper( - FakeMediaStream([ - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - ]), - ) as MediaStream; + final MediaStream firstVideoStream = + createJSInteropWrapper( + FakeMediaStream([ + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + ]), + ) + as MediaStream; // Create a video stream for the second video device. - final MediaStream secondVideoStream = createJSInteropWrapper( - FakeMediaStream([ - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - ]), - ) as MediaStream; + final MediaStream secondVideoStream = + createJSInteropWrapper( + FakeMediaStream([ + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + ]), + ) + as MediaStream; // Mock media devices to return two video input devices // and two audio devices. - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [ - firstVideoDevice, - createJSInteropWrapper( - FakeMediaDeviceInfo( - '2', - 'Audio Input 2', - MediaDeviceKind.audioInput, - ), - ) as MediaDeviceInfo, - createJSInteropWrapper( - FakeMediaDeviceInfo( - '3', - 'Audio Output 3', - MediaDeviceKind.audioOutput, - ), - ) as MediaDeviceInfo, - secondVideoDevice, - ].toJS, - ).toJS; - }.toJS; + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [ + firstVideoDevice, + createJSInteropWrapper( + FakeMediaDeviceInfo( + '2', + 'Audio Input 2', + MediaDeviceKind.audioInput, + ), + ) + as MediaDeviceInfo, + createJSInteropWrapper( + FakeMediaDeviceInfo( + '3', + 'Audio Output 3', + MediaDeviceKind.audioOutput, + ), + ) + as MediaDeviceInfo, + secondVideoDevice, + ].toJS, + ).toJS; + }.toJS; // Mock camera service to return the first video stream // for the first video device. @@ -382,29 +407,35 @@ void main() { ); }); - testWidgets( - 'sets camera metadata ' + testWidgets('sets camera metadata ' 'for the camera description', (WidgetTester tester) async { - final MediaDeviceInfo videoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '1', - 'Camera 1', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; - - final MediaStream videoStream = createJSInteropWrapper( - FakeMediaStream([ - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - createJSInteropWrapper(MockMediaStreamTrack()) as MediaStreamTrack, - ]), - ) as MediaStream; + final MediaDeviceInfo videoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '1', + 'Camera 1', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [videoDevice].toJS, - ).toJS; - }.toJS; + final MediaStream videoStream = + createJSInteropWrapper( + FakeMediaStream([ + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + createJSInteropWrapper(MockMediaStreamTrack()) + as MediaStreamTrack, + ]), + ) + as MediaStream; + + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [videoDevice].toJS, + ).toJS; + }.toJS; when( cameraService.getMediaStreamForOptions( @@ -438,35 +469,38 @@ void main() { ); }); - testWidgets( - 'releases the video stream ' + testWidgets('releases the video stream ' 'of a video input device', (WidgetTester tester) async { - final MediaDeviceInfo videoDevice = createJSInteropWrapper( - FakeMediaDeviceInfo( - '1', - 'Camera 1', - MediaDeviceKind.videoInput, - ), - ) as MediaDeviceInfo; + final MediaDeviceInfo videoDevice = + createJSInteropWrapper( + FakeMediaDeviceInfo( + '1', + 'Camera 1', + MediaDeviceKind.videoInput, + ), + ) + as MediaDeviceInfo; final List tracks = []; final List stops = List.generate(2, (_) => false); for (int i = 0; i < stops.length; i++) { final MockMediaStreamTrack track = MockMediaStreamTrack(); - track.stop = () { - stops[i] = true; - }.toJS; + track.stop = + () { + stops[i] = true; + }.toJS; tracks.add(createJSInteropWrapper(track) as MediaStreamTrack); } final MediaStream videoStream = createJSInteropWrapper(FakeMediaStream(tracks)) as MediaStream; - mockMediaDevices.enumerateDevices = () { - return Future>.value( - [videoDevice].toJS, - ).toJS; - }.toJS; + mockMediaDevices.enumerateDevices = + () { + return Future>.value( + [videoDevice].toJS, + ).toJS; + }.toJS; when( cameraService.getMediaStreamForOptions( @@ -488,13 +522,14 @@ void main() { ) async { final DOMException exception = DOMException('UnknownError'); - mockMediaDevices.enumerateDevices = () { - throw exception; - // ignore: dead_code - return Future>.value( - [].toJS, - ).toJS; - }.toJS; + mockMediaDevices.enumerateDevices = + () { + throw exception; + // ignore: dead_code + return Future>.value( + [].toJS, + ).toJS; + }.toJS; expect( () => CameraPlatform.instance.availableCameras(), @@ -508,8 +543,7 @@ void main() { ); }); - testWidgets( - 'when CameraService.getMediaStreamForOptions ' + testWidgets('when CameraService.getMediaStreamForOptions ' 'throws CameraWebException', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -533,8 +567,7 @@ void main() { ); }); - testWidgets( - 'when CameraService.getMediaStreamForOptions ' + testWidgets('when CameraService.getMediaStreamForOptions ' 'throws PlatformException', (WidgetTester tester) async { final PlatformException exception = PlatformException( code: CameraErrorCode.notSupported.toString(), @@ -578,7 +611,8 @@ void main() { setUp(() { // Add metadata for the camera description. (CameraPlatform.instance as CameraPlugin) - .camerasMetadata[cameraDescription] = cameraMetadata; + .camerasMetadata[cameraDescription] = + cameraMetadata; when( cameraService.mapFacingModeToCameraType('user'), @@ -624,16 +658,16 @@ void main() { cameraService.mapResolutionPresetToSize(ResolutionPreset.ultraHigh), ).thenReturn(ultraHighResolutionSize); - final int cameraId = - await CameraPlatform.instance.createCameraWithSettings( - cameraDescription, - const MediaSettings( - resolutionPreset: ResolutionPreset.ultraHigh, - videoBitrate: 200000, - audioBitrate: 32000, - enableAudio: true, - ), - ); + final int cameraId = await CameraPlatform.instance + .createCameraWithSettings( + cameraDescription, + const MediaSettings( + resolutionPreset: ResolutionPreset.ultraHigh, + videoBitrate: 200000, + audioBitrate: 32000, + enableAudio: true, + ), + ); final Camera? camera = (CameraPlatform.instance as CameraPlugin).cameras[cameraId]; @@ -656,8 +690,7 @@ void main() { expect(camera.options.video.deviceId, cameraMetadata.deviceId); }); - testWidgets( - 'with a max resolution preset ' + testWidgets('with a max resolution preset ' 'and enabled audio set to false ' 'when no options are specified', (WidgetTester tester) async { when( @@ -690,8 +723,7 @@ void main() { expect(camera.options.video.deviceId, cameraMetadata.deviceId); }); - testWidgets( - 'with a max resolution preset ' + testWidgets('with a max resolution preset ' 'and enabled audio set to false ' 'when no options are specified ' 'using createCameraWithSettings', (WidgetTester tester) async { @@ -699,11 +731,11 @@ void main() { cameraService.mapResolutionPresetToSize(ResolutionPreset.max), ).thenReturn(maxResolutionSize); - final int cameraId = - await CameraPlatform.instance.createCameraWithSettings( - cameraDescription, - const MediaSettings(resolutionPreset: ResolutionPreset.max), - ); + final int cameraId = await CameraPlatform.instance + .createCameraWithSettings( + cameraDescription, + const MediaSettings(resolutionPreset: ResolutionPreset.max), + ); final Camera? camera = (CameraPlatform.instance as CameraPlugin).cameras[cameraId]; @@ -726,8 +758,7 @@ void main() { }); }); - testWidgets( - 'throws CameraException ' + testWidgets('throws CameraException ' 'with missingMetadata error ' 'if there is no metadata ' 'for the given camera description', (WidgetTester tester) async { @@ -750,8 +781,7 @@ void main() { ); }); - testWidgets( - 'throws CameraException ' + testWidgets('throws CameraException ' 'with missingMetadata error ' 'if there is no metadata ' 'for the given camera description ' @@ -868,8 +898,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.initializeCamera(cameraId), @@ -941,14 +970,14 @@ void main() { ).thenReturn(OrientationType.portraitPrimary); }); - testWidgets( - 'requests full-screen mode ' + testWidgets('requests full-screen mode ' 'on documentElement', (WidgetTester tester) async { int fullscreenCalls = 0; - mockDocumentElement.requestFullscreen = ([FullscreenOptions? options]) { - fullscreenCalls++; - return Future.value().toJS; - }.toJS; + mockDocumentElement.requestFullscreen = + ([FullscreenOptions? options]) { + fullscreenCalls++; + return Future.value().toJS; + }.toJS; await CameraPlatform.instance.lockCaptureOrientation( cameraId, @@ -958,8 +987,7 @@ void main() { expect(fullscreenCalls, 1); }); - testWidgets( - 'locks the capture orientation ' + testWidgets('locks the capture orientation ' 'based on the given device orientation', (WidgetTester tester) async { when( cameraService.mapDeviceOrientationToOrientationType( @@ -968,10 +996,11 @@ void main() { ).thenReturn(OrientationType.landscapeSecondary); final List capturedTypes = []; - mockScreenOrientation.lock = (OrientationLockType orientation) { - capturedTypes.add(orientation); - return Future.value().toJS; - }.toJS; + mockScreenOrientation.lock = + (OrientationLockType orientation) { + capturedTypes.add(orientation); + return Future.value().toJS; + }.toJS; await CameraPlatform.instance.lockCaptureOrientation( cameraId, @@ -989,8 +1018,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with orientationNotSupported error ' + testWidgets('with orientationNotSupported error ' 'when documentElement is not available', ( WidgetTester tester, ) async { @@ -1018,11 +1046,12 @@ void main() { ) async { final DOMException exception = DOMException('NotAllowedError'); - mockScreenOrientation.lock = (OrientationLockType orientation) { - throw exception; - // ignore: dead_code - return Future.value().toJS; - }.toJS; + mockScreenOrientation.lock = + (OrientationLockType orientation) { + throw exception; + // ignore: dead_code + return Future.value().toJS; + }.toJS; expect( () => CameraPlatform.instance.lockCaptureOrientation( @@ -1052,9 +1081,10 @@ void main() { WidgetTester tester, ) async { int unlocks = 0; - mockScreenOrientation.unlock = () { - unlocks++; - }.toJS; + mockScreenOrientation.unlock = + () { + unlocks++; + }.toJS; await CameraPlatform.instance.unlockCaptureOrientation(cameraId); @@ -1062,8 +1092,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with orientationNotSupported error ' + testWidgets('with orientationNotSupported error ' 'when documentElement is not available', ( WidgetTester tester, ) async { @@ -1088,11 +1117,12 @@ void main() { ) async { final DOMException exception = DOMException('NotAllowedError'); - mockScreenOrientation.unlock = () { - throw exception; - // ignore: dead_code - return Future.value().toJS; - }.toJS; + mockScreenOrientation.unlock = + () { + throw exception; + // ignore: dead_code + return Future.value().toJS; + }.toJS; expect( () => CameraPlatform.instance.unlockCaptureOrientation(cameraId), @@ -1128,8 +1158,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.takePicture(cameraId), @@ -1236,8 +1265,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.startVideoRecording(cameraId), @@ -1381,8 +1409,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.stopVideoRecording(cameraId), @@ -1463,8 +1490,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.pauseVideoRecording(cameraId), @@ -1545,8 +1571,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.resumeVideoRecording(cameraId), @@ -1628,8 +1653,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.setFlashMode( @@ -1804,8 +1828,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () async => CameraPlatform.instance.getMaxZoomLevel(cameraId), @@ -1892,8 +1915,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () async => CameraPlatform.instance.getMinZoomLevel(cameraId), @@ -1976,8 +1998,7 @@ void main() { }); group('throws CameraException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () async => CameraPlatform.instance.setZoomLevel(cameraId, 100.0), @@ -2082,8 +2103,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () async => CameraPlatform.instance.pausePreview(cameraId), @@ -2137,8 +2157,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () async => CameraPlatform.instance.resumePreview(cameraId), @@ -2204,8 +2223,7 @@ void main() { }); }); - testWidgets( - 'buildPreview returns an HtmlElementView ' + testWidgets('buildPreview returns an HtmlElementView ' 'with an appropriate view type', (WidgetTester tester) async { final Camera camera = Camera( textureId: cameraId, @@ -2347,8 +2365,7 @@ void main() { }); group('throws PlatformException', () { - testWidgets( - 'with notFound error ' + testWidgets('with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.dispose(cameraId), @@ -2403,8 +2420,7 @@ void main() { ); }); - testWidgets( - 'throws PlatformException ' + testWidgets('throws PlatformException ' 'with notFound error ' 'if the camera does not exist', (WidgetTester tester) async { expect( @@ -2472,8 +2488,7 @@ void main() { when(camera.startVideoRecording()).thenAnswer((_) async {}); }); - testWidgets( - 'onCameraInitialized emits a CameraInitializedEvent ' + testWidgets('onCameraInitialized emits a CameraInitializedEvent ' 'on initializeCamera', (WidgetTester tester) async { // Mock the camera to use a blank video stream of size 1280x720. const Size videoSize = Size(1280, 720); @@ -2492,8 +2507,9 @@ void main() { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; - final Stream eventStream = - CameraPlatform.instance.onCameraInitialized(cameraId); + final Stream eventStream = CameraPlatform + .instance + .onCameraInitialized(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2521,19 +2537,19 @@ void main() { testWidgets('onCameraResolutionChanged emits an empty stream', ( WidgetTester tester, ) async { - final Stream stream = - CameraPlatform.instance.onCameraResolutionChanged(cameraId); + final Stream stream = CameraPlatform + .instance + .onCameraResolutionChanged(cameraId); expect(await stream.isEmpty, isTrue); }); - testWidgets( - 'onCameraClosing emits a CameraClosingEvent ' + testWidgets('onCameraClosing emits a CameraClosingEvent ' 'on the camera ended event', (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; - final Stream eventStream = - CameraPlatform.instance.onCameraClosing(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraClosing(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2558,24 +2574,25 @@ void main() { (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on the camera video error event ' 'with a message', (WidgetTester tester) async { - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); - final MediaError error = createJSInteropWrapper( - FakeMediaError( - MediaError.MEDIA_ERR_NETWORK, - 'A network error occurred.', - ), - ) as MediaError; + final MediaError error = + createJSInteropWrapper( + FakeMediaError( + MediaError.MEDIA_ERR_NETWORK, + 'A network error occurred.', + ), + ) + as MediaError; final CameraErrorCode errorCode = CameraErrorCode.fromMediaError( error, @@ -2597,21 +2614,22 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on the camera video error event ' 'with no message', (WidgetTester tester) async { - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); - final MediaError error = createJSInteropWrapper( - FakeMediaError(MediaError.MEDIA_ERR_NETWORK), - ) as MediaError; + final MediaError error = + createJSInteropWrapper( + FakeMediaError(MediaError.MEDIA_ERR_NETWORK), + ) + as MediaError; final CameraErrorCode errorCode = CameraErrorCode.fromMediaError( error, ); @@ -2632,11 +2650,10 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on the camera video abort event', (WidgetTester tester) async { - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2658,8 +2675,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on takePicture error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2669,8 +2685,8 @@ void main() { when(camera.takePicture()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2693,8 +2709,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on setFlashMode error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2704,8 +2719,8 @@ void main() { when(camera.setFlashMode(any)).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2731,8 +2746,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on getMaxZoomLevel error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2742,8 +2756,8 @@ void main() { when(camera.getMaxZoomLevel()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2766,8 +2780,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on getMinZoomLevel error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2777,8 +2790,8 @@ void main() { when(camera.getMinZoomLevel()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2801,8 +2814,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on setZoomLevel error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2812,8 +2824,8 @@ void main() { when(camera.setZoomLevel(any)).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2836,8 +2848,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on resumePreview error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2847,8 +2858,8 @@ void main() { when(camera.play()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2871,8 +2882,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on startVideoRecording error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2886,8 +2896,8 @@ void main() { when(camera.startVideoRecording()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2910,13 +2920,12 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on the camera video recording error event', ( WidgetTester tester, ) async { - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2943,8 +2952,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on stopVideoRecording error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2954,8 +2962,8 @@ void main() { when(camera.stopVideoRecording()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -2978,8 +2986,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on pauseVideoRecording error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -2989,8 +2996,8 @@ void main() { when(camera.pauseVideoRecording()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -3013,8 +3020,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a CameraErrorEvent ' + testWidgets('emits a CameraErrorEvent ' 'on resumeVideoRecording error', (WidgetTester tester) async { final CameraWebException exception = CameraWebException( cameraId, @@ -3024,8 +3030,8 @@ void main() { when(camera.resumeVideoRecording()).thenThrow(exception); - final Stream eventStream = - CameraPlatform.instance.onCameraError(cameraId); + final Stream eventStream = CameraPlatform.instance + .onCameraError(cameraId); final StreamQueue streamQueue = StreamQueue(eventStream); @@ -3056,8 +3062,8 @@ void main() { final XFile capturedVideo = XFile('/bogus/test'); final Stream stream = Stream.value( - VideoRecordedEvent(cameraId, capturedVideo, Duration.zero), - ); + VideoRecordedEvent(cameraId, capturedVideo, Duration.zero), + ); when(camera.onVideoRecordedEvent).thenAnswer((_) => stream); // Save the camera in the camera plugin. @@ -3065,8 +3071,8 @@ void main() { final StreamQueue streamQueue = StreamQueue( - CameraPlatform.instance.onVideoRecordedEvent(cameraId), - ); + CameraPlatform.instance.onVideoRecordedEvent(cameraId), + ); expect( await streamQueue.next, @@ -3116,8 +3122,7 @@ void main() { await streamQueue.cancel(); }); - testWidgets( - 'emits a DeviceOrientationChangedEvent ' + testWidgets('emits a DeviceOrientationChangedEvent ' 'when the screen orientation is changed', ( WidgetTester tester, ) async { From def248d5c4325a630a8a7cac8faf7e79bd2a15b9 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sat, 27 Sep 2025 22:56:19 +0530 Subject: [PATCH 52/68] [camera_web] fix: todo comment --- packages/camera/camera_web/lib/src/camera.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index f19950442fe..a84b7a3a7f9 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -658,7 +658,10 @@ class Camera { final StreamController _cameraFrameStreamController = StreamController.broadcast(); - // TODO(TecHaxter): package:camera_platform_interface has CameraImageStreamOptions class. FPS should be introduced there instead of here. + // TODO(TecHaxter): Introduce FPS in CameraImageStreamOptions of + // package:camera_platform_interface. + // https://github.com/flutter/flutter/issues/176148 + // This class is deprecated and will be removed in the future. /// Used for running the camera frame stream at a specified FPS final int cameraStreamFPS = 60; From 1b5e9a1f7cfdc367bfaa22a6db26628c987a04e2 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sat, 27 Sep 2025 22:58:07 +0530 Subject: [PATCH 53/68] [camera_web] fix: todo comment --- packages/camera/camera_web/lib/src/camera.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index a84b7a3a7f9..91d0700e422 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -661,7 +661,6 @@ class Camera { // TODO(TecHaxter): Introduce FPS in CameraImageStreamOptions of // package:camera_platform_interface. // https://github.com/flutter/flutter/issues/176148 - // This class is deprecated and will be removed in the future. /// Used for running the camera frame stream at a specified FPS final int cameraStreamFPS = 60; From 33174ca48289e247d85e65628f1299012f11551b Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sun, 28 Sep 2025 15:50:51 +0530 Subject: [PATCH 54/68] [camera_web] fix: reformatting --- .../integration_test/helpers/mocks.mocks.dart | 1202 +++++++---------- 1 file changed, 524 insertions(+), 678 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart index 4061d5b494c..e5bfb421726 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart @@ -35,109 +35,59 @@ import 'mocks.dart' as _i9; // ignore_for_file: subtype_of_sealed_class class _FakeJsUtil_0 extends _i1.SmartFake implements _i2.JsUtil { - _FakeJsUtil_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeJsUtil_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeZoomLevelCapability_1 extends _i1.SmartFake implements _i3.ZoomLevelCapability { - _FakeZoomLevelCapability_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeZoomLevelCapability_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeSize_2 extends _i1.SmartFake implements _i4.Size { - _FakeSize_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeSize_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeCameraImageData_3 extends _i1.SmartFake implements _i5.CameraImageData { - _FakeCameraImageData_3( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeCameraImageData_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeCameraOptions_4 extends _i1.SmartFake implements _i3.CameraOptions { - _FakeCameraOptions_4( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeCameraOptions_4(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeStreamController_5 extends _i1.SmartFake implements _i6.StreamController { - _FakeStreamController_5( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeStreamController_5(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeEventStreamProvider_6 extends _i1.SmartFake implements _i7.EventStreamProvider { - _FakeEventStreamProvider_6( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeEventStreamProvider_6(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeXFile_7 extends _i1.SmartFake implements _i5.XFile { - _FakeXFile_7( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeXFile_7(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeAudioConstraints_8 extends _i1.SmartFake implements _i3.AudioConstraints { - _FakeAudioConstraints_8( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeAudioConstraints_8(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeVideoConstraints_9 extends _i1.SmartFake implements _i3.VideoConstraints { - _FakeVideoConstraints_9( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeVideoConstraints_9(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } /// A class which mocks [CameraService]. @@ -145,42 +95,37 @@ class _FakeVideoConstraints_9 extends _i1.SmartFake /// See the documentation for Mockito's code generation for more information. class MockCameraService extends _i1.Mock implements _i8.CameraService { @override - _i7.Window get window => (super.noSuchMethod( - Invocation.getter(#window), - returnValue: _i9.windowShim(), - returnValueForMissingStub: _i9.windowShim(), - ) as _i7.Window); + _i7.Window get window => + (super.noSuchMethod( + Invocation.getter(#window), + returnValue: _i9.windowShim(), + returnValueForMissingStub: _i9.windowShim(), + ) + as _i7.Window); @override set window(_i7.Window? _window) => super.noSuchMethod( - Invocation.setter( - #window, - _window, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#window, _window), + returnValueForMissingStub: null, + ); @override - _i2.JsUtil get jsUtil => (super.noSuchMethod( - Invocation.getter(#jsUtil), - returnValue: _FakeJsUtil_0( - this, - Invocation.getter(#jsUtil), - ), - returnValueForMissingStub: _FakeJsUtil_0( - this, - Invocation.getter(#jsUtil), - ), - ) as _i2.JsUtil); + _i2.JsUtil get jsUtil => + (super.noSuchMethod( + Invocation.getter(#jsUtil), + returnValue: _FakeJsUtil_0(this, Invocation.getter(#jsUtil)), + returnValueForMissingStub: _FakeJsUtil_0( + this, + Invocation.getter(#jsUtil), + ), + ) + as _i2.JsUtil); @override set jsUtil(_i2.JsUtil? _jsUtil) => super.noSuchMethod( - Invocation.setter( - #jsUtil, - _jsUtil, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#jsUtil, _jsUtil), + returnValueForMissingStub: null, + ); @override _i6.Future<_i7.MediaStream> getMediaStreamForOptions( @@ -188,192 +133,165 @@ class MockCameraService extends _i1.Mock implements _i8.CameraService { int? cameraId = 0, }) => (super.noSuchMethod( - Invocation.method( - #getMediaStreamForOptions, - [options], - {#cameraId: cameraId}, - ), - returnValue: _i9.getMediaStreamForOptionsShim( - options, - cameraId: cameraId, - ), - returnValueForMissingStub: _i9.getMediaStreamForOptionsShim( - options, - cameraId: cameraId, - ), - ) as _i6.Future<_i7.MediaStream>); + Invocation.method( + #getMediaStreamForOptions, + [options], + {#cameraId: cameraId}, + ), + returnValue: _i9.getMediaStreamForOptionsShim( + options, + cameraId: cameraId, + ), + returnValueForMissingStub: _i9.getMediaStreamForOptionsShim( + options, + cameraId: cameraId, + ), + ) + as _i6.Future<_i7.MediaStream>); @override _i3.ZoomLevelCapability getZoomLevelCapabilityForCamera( - _i10.Camera? camera) => - (super.noSuchMethod( - Invocation.method( - #getZoomLevelCapabilityForCamera, - [camera], - ), - returnValue: _FakeZoomLevelCapability_1( - this, - Invocation.method( - #getZoomLevelCapabilityForCamera, - [camera], - ), - ), - returnValueForMissingStub: _FakeZoomLevelCapability_1( - this, - Invocation.method( - #getZoomLevelCapabilityForCamera, - [camera], - ), - ), - ) as _i3.ZoomLevelCapability); + _i10.Camera? camera, + ) => + (super.noSuchMethod( + Invocation.method(#getZoomLevelCapabilityForCamera, [camera]), + returnValue: _FakeZoomLevelCapability_1( + this, + Invocation.method(#getZoomLevelCapabilityForCamera, [camera]), + ), + returnValueForMissingStub: _FakeZoomLevelCapability_1( + this, + Invocation.method(#getZoomLevelCapabilityForCamera, [camera]), + ), + ) + as _i3.ZoomLevelCapability); @override String? getFacingModeForVideoTrack(_i7.MediaStreamTrack? videoTrack) => (super.noSuchMethod( - Invocation.method( - #getFacingModeForVideoTrack, - [videoTrack], - ), - returnValueForMissingStub: null, - ) as String?); + Invocation.method(#getFacingModeForVideoTrack, [videoTrack]), + returnValueForMissingStub: null, + ) + as String?); @override _i5.CameraLensDirection mapFacingModeToLensDirection(String? facingMode) => (super.noSuchMethod( - Invocation.method( - #mapFacingModeToLensDirection, - [facingMode], - ), - returnValue: _i5.CameraLensDirection.front, - returnValueForMissingStub: _i5.CameraLensDirection.front, - ) as _i5.CameraLensDirection); + Invocation.method(#mapFacingModeToLensDirection, [facingMode]), + returnValue: _i5.CameraLensDirection.front, + returnValueForMissingStub: _i5.CameraLensDirection.front, + ) + as _i5.CameraLensDirection); @override _i3.CameraType mapFacingModeToCameraType(String? facingMode) => (super.noSuchMethod( - Invocation.method( - #mapFacingModeToCameraType, - [facingMode], - ), - returnValue: _i3.CameraType.environment, - returnValueForMissingStub: _i3.CameraType.environment, - ) as _i3.CameraType); + Invocation.method(#mapFacingModeToCameraType, [facingMode]), + returnValue: _i3.CameraType.environment, + returnValueForMissingStub: _i3.CameraType.environment, + ) + as _i3.CameraType); @override _i4.Size mapResolutionPresetToSize(_i5.ResolutionPreset? resolutionPreset) => (super.noSuchMethod( - Invocation.method( - #mapResolutionPresetToSize, - [resolutionPreset], - ), - returnValue: _FakeSize_2( - this, - Invocation.method( - #mapResolutionPresetToSize, - [resolutionPreset], - ), - ), - returnValueForMissingStub: _FakeSize_2( - this, - Invocation.method( - #mapResolutionPresetToSize, - [resolutionPreset], - ), - ), - ) as _i4.Size); + Invocation.method(#mapResolutionPresetToSize, [resolutionPreset]), + returnValue: _FakeSize_2( + this, + Invocation.method(#mapResolutionPresetToSize, [resolutionPreset]), + ), + returnValueForMissingStub: _FakeSize_2( + this, + Invocation.method(#mapResolutionPresetToSize, [resolutionPreset]), + ), + ) + as _i4.Size); @override int mapResolutionPresetToVideoBitrate( - _i5.ResolutionPreset? resolutionPreset) => + _i5.ResolutionPreset? resolutionPreset, + ) => (super.noSuchMethod( - Invocation.method( - #mapResolutionPresetToVideoBitrate, - [resolutionPreset], - ), - returnValue: 0, - returnValueForMissingStub: 0, - ) as int); + Invocation.method(#mapResolutionPresetToVideoBitrate, [ + resolutionPreset, + ]), + returnValue: 0, + returnValueForMissingStub: 0, + ) + as int); @override int mapResolutionPresetToAudioBitrate( - _i5.ResolutionPreset? resolutionPreset) => + _i5.ResolutionPreset? resolutionPreset, + ) => (super.noSuchMethod( - Invocation.method( - #mapResolutionPresetToAudioBitrate, - [resolutionPreset], - ), - returnValue: 0, - returnValueForMissingStub: 0, - ) as int); + Invocation.method(#mapResolutionPresetToAudioBitrate, [ + resolutionPreset, + ]), + returnValue: 0, + returnValueForMissingStub: 0, + ) + as int); @override String mapDeviceOrientationToOrientationType( - _i11.DeviceOrientation? deviceOrientation) => - (super.noSuchMethod( - Invocation.method( - #mapDeviceOrientationToOrientationType, - [deviceOrientation], - ), - returnValue: _i12.dummyValue( - this, - Invocation.method( - #mapDeviceOrientationToOrientationType, - [deviceOrientation], - ), - ), - returnValueForMissingStub: _i12.dummyValue( - this, - Invocation.method( - #mapDeviceOrientationToOrientationType, - [deviceOrientation], - ), - ), - ) as String); + _i11.DeviceOrientation? deviceOrientation, + ) => + (super.noSuchMethod( + Invocation.method(#mapDeviceOrientationToOrientationType, [ + deviceOrientation, + ]), + returnValue: _i12.dummyValue( + this, + Invocation.method(#mapDeviceOrientationToOrientationType, [ + deviceOrientation, + ]), + ), + returnValueForMissingStub: _i12.dummyValue( + this, + Invocation.method(#mapDeviceOrientationToOrientationType, [ + deviceOrientation, + ]), + ), + ) + as String); @override _i11.DeviceOrientation mapOrientationTypeToDeviceOrientation( - String? orientationType) => + String? orientationType, + ) => (super.noSuchMethod( - Invocation.method( - #mapOrientationTypeToDeviceOrientation, - [orientationType], - ), - returnValue: _i11.DeviceOrientation.portraitUp, - returnValueForMissingStub: _i11.DeviceOrientation.portraitUp, - ) as _i11.DeviceOrientation); + Invocation.method(#mapOrientationTypeToDeviceOrientation, [ + orientationType, + ]), + returnValue: _i11.DeviceOrientation.portraitUp, + returnValueForMissingStub: _i11.DeviceOrientation.portraitUp, + ) + as _i11.DeviceOrientation); @override - bool hasPropertyOffScreenCanvas() => (super.noSuchMethod( - Invocation.method( - #hasPropertyOffScreenCanvas, - [], - ), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); + bool hasPropertyOffScreenCanvas() => + (super.noSuchMethod( + Invocation.method(#hasPropertyOffScreenCanvas, []), + returnValue: false, + returnValueForMissingStub: false, + ) + as bool); @override _i5.CameraImageData takeFrame(_i7.HTMLVideoElement? videoElement) => (super.noSuchMethod( - Invocation.method( - #takeFrame, - [videoElement], - ), - returnValue: _FakeCameraImageData_3( - this, - Invocation.method( - #takeFrame, - [videoElement], - ), - ), - returnValueForMissingStub: _FakeCameraImageData_3( - this, - Invocation.method( - #takeFrame, - [videoElement], - ), - ), - ) as _i5.CameraImageData); + Invocation.method(#takeFrame, [videoElement]), + returnValue: _FakeCameraImageData_3( + this, + Invocation.method(#takeFrame, [videoElement]), + ), + returnValueForMissingStub: _FakeCameraImageData_3( + this, + Invocation.method(#takeFrame, [videoElement]), + ), + ) + as _i5.CameraImageData); } /// A class which mocks [JsUtil]. @@ -381,37 +299,21 @@ class MockCameraService extends _i1.Mock implements _i8.CameraService { /// See the documentation for Mockito's code generation for more information. class MockJsUtil extends _i1.Mock implements _i2.JsUtil { @override - bool hasProperty( - _i13.JSObject? o, - _i13.JSAny? name, - ) => + bool hasProperty(_i13.JSObject? o, _i13.JSAny? name) => (super.noSuchMethod( - Invocation.method( - #hasProperty, - [ - o, - name, - ], - ), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); - - @override - _i13.JSAny? getProperty( - _i13.JSObject? o, - _i13.JSAny? name, - ) => + Invocation.method(#hasProperty, [o, name]), + returnValue: false, + returnValueForMissingStub: false, + ) + as bool); + + @override + _i13.JSAny? getProperty(_i13.JSObject? o, _i13.JSAny? name) => (super.noSuchMethod( - Invocation.method( - #getProperty, - [ - o, - name, - ], - ), - returnValueForMissingStub: null, - ) as _i13.JSAny?); + Invocation.method(#getProperty, [o, name]), + returnValueForMissingStub: null, + ) + as _i13.JSAny?); } /// A class which mocks [Camera]. @@ -419,473 +321,414 @@ class MockJsUtil extends _i1.Mock implements _i2.JsUtil { /// See the documentation for Mockito's code generation for more information. class MockCamera extends _i1.Mock implements _i10.Camera { @override - int get textureId => (super.noSuchMethod( - Invocation.getter(#textureId), - returnValue: 0, - returnValueForMissingStub: 0, - ) as int); + int get textureId => + (super.noSuchMethod( + Invocation.getter(#textureId), + returnValue: 0, + returnValueForMissingStub: 0, + ) + as int); @override - _i3.CameraOptions get options => (super.noSuchMethod( - Invocation.getter(#options), - returnValue: _FakeCameraOptions_4( - this, - Invocation.getter(#options), - ), - returnValueForMissingStub: _FakeCameraOptions_4( - this, - Invocation.getter(#options), - ), - ) as _i3.CameraOptions); + _i3.CameraOptions get options => + (super.noSuchMethod( + Invocation.getter(#options), + returnValue: _FakeCameraOptions_4( + this, + Invocation.getter(#options), + ), + returnValueForMissingStub: _FakeCameraOptions_4( + this, + Invocation.getter(#options), + ), + ) + as _i3.CameraOptions); @override ({int? audioBitrate, int? videoBitrate}) get recorderOptions => (super.noSuchMethod( - Invocation.getter(#recorderOptions), - returnValue: (audioBitrate: null, videoBitrate: null), - returnValueForMissingStub: (audioBitrate: null, videoBitrate: null), - ) as ({int? audioBitrate, int? videoBitrate})); + Invocation.getter(#recorderOptions), + returnValue: (audioBitrate: null, videoBitrate: null), + returnValueForMissingStub: (audioBitrate: null, videoBitrate: null), + ) + as ({int? audioBitrate, int? videoBitrate})); @override - _i7.HTMLVideoElement get videoElement => (super.noSuchMethod( - Invocation.getter(#videoElement), - returnValue: _i9.videoElementShim(), - returnValueForMissingStub: _i9.videoElementShim(), - ) as _i7.HTMLVideoElement); + _i7.HTMLVideoElement get videoElement => + (super.noSuchMethod( + Invocation.getter(#videoElement), + returnValue: _i9.videoElementShim(), + returnValueForMissingStub: _i9.videoElementShim(), + ) + as _i7.HTMLVideoElement); @override set videoElement(_i7.HTMLVideoElement? _videoElement) => super.noSuchMethod( - Invocation.setter( - #videoElement, - _videoElement, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#videoElement, _videoElement), + returnValueForMissingStub: null, + ); @override - _i7.HTMLDivElement get divElement => (super.noSuchMethod( - Invocation.getter(#divElement), - returnValue: _i9.divElementShim(), - returnValueForMissingStub: _i9.divElementShim(), - ) as _i7.HTMLDivElement); + _i7.HTMLDivElement get divElement => + (super.noSuchMethod( + Invocation.getter(#divElement), + returnValue: _i9.divElementShim(), + returnValueForMissingStub: _i9.divElementShim(), + ) + as _i7.HTMLDivElement); @override set divElement(_i7.HTMLDivElement? _divElement) => super.noSuchMethod( - Invocation.setter( - #divElement, - _divElement, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#divElement, _divElement), + returnValueForMissingStub: null, + ); @override set stream(_i7.MediaStream? _stream) => super.noSuchMethod( - Invocation.setter( - #stream, - _stream, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#stream, _stream), + returnValueForMissingStub: null, + ); @override _i6.StreamController<_i7.MediaStreamTrack> get onEndedController => (super.noSuchMethod( - Invocation.getter(#onEndedController), - returnValue: _FakeStreamController_5<_i7.MediaStreamTrack>( - this, - Invocation.getter(#onEndedController), - ), - returnValueForMissingStub: - _FakeStreamController_5<_i7.MediaStreamTrack>( - this, - Invocation.getter(#onEndedController), - ), - ) as _i6.StreamController<_i7.MediaStreamTrack>); + Invocation.getter(#onEndedController), + returnValue: _FakeStreamController_5<_i7.MediaStreamTrack>( + this, + Invocation.getter(#onEndedController), + ), + returnValueForMissingStub: + _FakeStreamController_5<_i7.MediaStreamTrack>( + this, + Invocation.getter(#onEndedController), + ), + ) + as _i6.StreamController<_i7.MediaStreamTrack>); @override _i7.EventStreamProvider<_i7.Event> get mediaRecorderOnErrorProvider => (super.noSuchMethod( - Invocation.getter(#mediaRecorderOnErrorProvider), - returnValue: _FakeEventStreamProvider_6<_i7.Event>( - this, - Invocation.getter(#mediaRecorderOnErrorProvider), - ), - returnValueForMissingStub: _FakeEventStreamProvider_6<_i7.Event>( - this, - Invocation.getter(#mediaRecorderOnErrorProvider), - ), - ) as _i7.EventStreamProvider<_i7.Event>); + Invocation.getter(#mediaRecorderOnErrorProvider), + returnValue: _FakeEventStreamProvider_6<_i7.Event>( + this, + Invocation.getter(#mediaRecorderOnErrorProvider), + ), + returnValueForMissingStub: _FakeEventStreamProvider_6<_i7.Event>( + this, + Invocation.getter(#mediaRecorderOnErrorProvider), + ), + ) + as _i7.EventStreamProvider<_i7.Event>); @override set mediaRecorderOnErrorProvider( - _i7.EventStreamProvider<_i7.Event>? _mediaRecorderOnErrorProvider) => - super.noSuchMethod( - Invocation.setter( - #mediaRecorderOnErrorProvider, - _mediaRecorderOnErrorProvider, - ), - returnValueForMissingStub: null, - ); + _i7.EventStreamProvider<_i7.Event>? _mediaRecorderOnErrorProvider, + ) => super.noSuchMethod( + Invocation.setter( + #mediaRecorderOnErrorProvider, + _mediaRecorderOnErrorProvider, + ), + returnValueForMissingStub: null, + ); @override _i6.StreamController<_i7.ErrorEvent> get videoRecordingErrorController => (super.noSuchMethod( - Invocation.getter(#videoRecordingErrorController), - returnValue: _FakeStreamController_5<_i7.ErrorEvent>( - this, - Invocation.getter(#videoRecordingErrorController), - ), - returnValueForMissingStub: _FakeStreamController_5<_i7.ErrorEvent>( - this, - Invocation.getter(#videoRecordingErrorController), - ), - ) as _i6.StreamController<_i7.ErrorEvent>); + Invocation.getter(#videoRecordingErrorController), + returnValue: _FakeStreamController_5<_i7.ErrorEvent>( + this, + Invocation.getter(#videoRecordingErrorController), + ), + returnValueForMissingStub: _FakeStreamController_5<_i7.ErrorEvent>( + this, + Invocation.getter(#videoRecordingErrorController), + ), + ) + as _i6.StreamController<_i7.ErrorEvent>); @override set flashMode(_i5.FlashMode? _flashMode) => super.noSuchMethod( - Invocation.setter( - #flashMode, - _flashMode, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#flashMode, _flashMode), + returnValueForMissingStub: null, + ); @override - _i7.Window get window => (super.noSuchMethod( - Invocation.getter(#window), - returnValue: _i9.windowShim(), - returnValueForMissingStub: _i9.windowShim(), - ) as _i7.Window); + _i7.Window get window => + (super.noSuchMethod( + Invocation.getter(#window), + returnValue: _i9.windowShim(), + returnValueForMissingStub: _i9.windowShim(), + ) + as _i7.Window); @override set window(_i7.Window? _window) => super.noSuchMethod( - Invocation.setter( - #window, - _window, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#window, _window), + returnValueForMissingStub: null, + ); @override set mediaRecorder(_i7.MediaRecorder? _mediaRecorder) => super.noSuchMethod( - Invocation.setter( - #mediaRecorder, - _mediaRecorder, - ), - returnValueForMissingStub: null, - ); + Invocation.setter(#mediaRecorder, _mediaRecorder), + returnValueForMissingStub: null, + ); @override - bool Function(String) get isVideoTypeSupported => (super.noSuchMethod( - Invocation.getter(#isVideoTypeSupported), - returnValue: (String __p0) => false, - returnValueForMissingStub: (String __p0) => false, - ) as bool Function(String)); + bool Function(String) get isVideoTypeSupported => + (super.noSuchMethod( + Invocation.getter(#isVideoTypeSupported), + returnValue: (String __p0) => false, + returnValueForMissingStub: (String __p0) => false, + ) + as bool Function(String)); @override set isVideoTypeSupported(bool Function(String)? _isVideoTypeSupported) => super.noSuchMethod( - Invocation.setter( - #isVideoTypeSupported, - _isVideoTypeSupported, - ), + Invocation.setter(#isVideoTypeSupported, _isVideoTypeSupported), returnValueForMissingStub: null, ); @override - _i7.Blob Function( - List<_i7.Blob>, - String, - ) get blobBuilder => (super.noSuchMethod( - Invocation.getter(#blobBuilder), - returnValue: _i9.blobBuilderShim(), - returnValueForMissingStub: _i9.blobBuilderShim(), - ) as _i7.Blob Function( - List<_i7.Blob>, - String, - )); - - @override - set blobBuilder( - _i7.Blob Function( - List<_i7.Blob>, - String, - )? _blobBuilder) => + _i7.Blob Function(List<_i7.Blob>, String) get blobBuilder => + (super.noSuchMethod( + Invocation.getter(#blobBuilder), + returnValue: _i9.blobBuilderShim(), + returnValueForMissingStub: _i9.blobBuilderShim(), + ) + as _i7.Blob Function(List<_i7.Blob>, String)); + + @override + set blobBuilder(_i7.Blob Function(List<_i7.Blob>, String)? _blobBuilder) => super.noSuchMethod( - Invocation.setter( - #blobBuilder, - _blobBuilder, - ), + Invocation.setter(#blobBuilder, _blobBuilder), returnValueForMissingStub: null, ); @override _i6.StreamController<_i5.VideoRecordedEvent> get videoRecorderController => (super.noSuchMethod( - Invocation.getter(#videoRecorderController), - returnValue: _FakeStreamController_5<_i5.VideoRecordedEvent>( - this, - Invocation.getter(#videoRecorderController), - ), - returnValueForMissingStub: - _FakeStreamController_5<_i5.VideoRecordedEvent>( - this, - Invocation.getter(#videoRecorderController), - ), - ) as _i6.StreamController<_i5.VideoRecordedEvent>); - - @override - bool get canUseOffscreenCanvas => (super.noSuchMethod( - Invocation.getter(#canUseOffscreenCanvas), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); + Invocation.getter(#videoRecorderController), + returnValue: _FakeStreamController_5<_i5.VideoRecordedEvent>( + this, + Invocation.getter(#videoRecorderController), + ), + returnValueForMissingStub: + _FakeStreamController_5<_i5.VideoRecordedEvent>( + this, + Invocation.getter(#videoRecorderController), + ), + ) + as _i6.StreamController<_i5.VideoRecordedEvent>); + + @override + bool get canUseOffscreenCanvas => + (super.noSuchMethod( + Invocation.getter(#canUseOffscreenCanvas), + returnValue: false, + returnValueForMissingStub: false, + ) + as bool); @override - int get cameraStreamFPS => (super.noSuchMethod( - Invocation.getter(#cameraStreamFPS), - returnValue: 0, - returnValueForMissingStub: 0, - ) as int); + int get cameraStreamFPS => + (super.noSuchMethod( + Invocation.getter(#cameraStreamFPS), + returnValue: 0, + returnValueForMissingStub: 0, + ) + as int); @override - _i6.Stream<_i7.MediaStreamTrack> get onEnded => (super.noSuchMethod( - Invocation.getter(#onEnded), - returnValue: _i6.Stream<_i7.MediaStreamTrack>.empty(), - returnValueForMissingStub: _i6.Stream<_i7.MediaStreamTrack>.empty(), - ) as _i6.Stream<_i7.MediaStreamTrack>); + _i6.Stream<_i7.MediaStreamTrack> get onEnded => + (super.noSuchMethod( + Invocation.getter(#onEnded), + returnValue: _i6.Stream<_i7.MediaStreamTrack>.empty(), + returnValueForMissingStub: _i6.Stream<_i7.MediaStreamTrack>.empty(), + ) + as _i6.Stream<_i7.MediaStreamTrack>); @override - _i6.Stream<_i7.ErrorEvent> get onVideoRecordingError => (super.noSuchMethod( - Invocation.getter(#onVideoRecordingError), - returnValue: _i6.Stream<_i7.ErrorEvent>.empty(), - returnValueForMissingStub: _i6.Stream<_i7.ErrorEvent>.empty(), - ) as _i6.Stream<_i7.ErrorEvent>); + _i6.Stream<_i7.ErrorEvent> get onVideoRecordingError => + (super.noSuchMethod( + Invocation.getter(#onVideoRecordingError), + returnValue: _i6.Stream<_i7.ErrorEvent>.empty(), + returnValueForMissingStub: _i6.Stream<_i7.ErrorEvent>.empty(), + ) + as _i6.Stream<_i7.ErrorEvent>); @override _i6.Stream<_i5.VideoRecordedEvent> get onVideoRecordedEvent => (super.noSuchMethod( - Invocation.getter(#onVideoRecordedEvent), - returnValue: _i6.Stream<_i5.VideoRecordedEvent>.empty(), - returnValueForMissingStub: _i6.Stream<_i5.VideoRecordedEvent>.empty(), - ) as _i6.Stream<_i5.VideoRecordedEvent>); + Invocation.getter(#onVideoRecordedEvent), + returnValue: _i6.Stream<_i5.VideoRecordedEvent>.empty(), + returnValueForMissingStub: + _i6.Stream<_i5.VideoRecordedEvent>.empty(), + ) + as _i6.Stream<_i5.VideoRecordedEvent>); @override - _i6.Future initialize() => (super.noSuchMethod( - Invocation.method( - #initialize, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + _i6.Future initialize() => + (super.noSuchMethod( + Invocation.method(#initialize, []), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); @override - _i6.Future play() => (super.noSuchMethod( - Invocation.method( - #play, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + _i6.Future play() => + (super.noSuchMethod( + Invocation.method(#play, []), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); @override void pause() => super.noSuchMethod( - Invocation.method( - #pause, - [], - ), - returnValueForMissingStub: null, - ); + Invocation.method(#pause, []), + returnValueForMissingStub: null, + ); @override void stop() => super.noSuchMethod( - Invocation.method( - #stop, - [], - ), - returnValueForMissingStub: null, - ); + Invocation.method(#stop, []), + returnValueForMissingStub: null, + ); @override - _i6.Future<_i5.XFile> takePicture() => (super.noSuchMethod( - Invocation.method( - #takePicture, - [], - ), - returnValue: _i6.Future<_i5.XFile>.value(_FakeXFile_7( - this, - Invocation.method( - #takePicture, - [], - ), - )), - returnValueForMissingStub: _i6.Future<_i5.XFile>.value(_FakeXFile_7( - this, - Invocation.method( - #takePicture, - [], - ), - )), - ) as _i6.Future<_i5.XFile>); - - @override - _i4.Size getVideoSize() => (super.noSuchMethod( - Invocation.method( - #getVideoSize, - [], - ), - returnValue: _FakeSize_2( - this, - Invocation.method( - #getVideoSize, - [], - ), - ), - returnValueForMissingStub: _FakeSize_2( - this, - Invocation.method( - #getVideoSize, - [], - ), - ), - ) as _i4.Size); + _i6.Future<_i5.XFile> takePicture() => + (super.noSuchMethod( + Invocation.method(#takePicture, []), + returnValue: _i6.Future<_i5.XFile>.value( + _FakeXFile_7(this, Invocation.method(#takePicture, [])), + ), + returnValueForMissingStub: _i6.Future<_i5.XFile>.value( + _FakeXFile_7(this, Invocation.method(#takePicture, [])), + ), + ) + as _i6.Future<_i5.XFile>); + + @override + _i4.Size getVideoSize() => + (super.noSuchMethod( + Invocation.method(#getVideoSize, []), + returnValue: _FakeSize_2( + this, + Invocation.method(#getVideoSize, []), + ), + returnValueForMissingStub: _FakeSize_2( + this, + Invocation.method(#getVideoSize, []), + ), + ) + as _i4.Size); @override void setFlashMode(_i5.FlashMode? mode) => super.noSuchMethod( - Invocation.method( - #setFlashMode, - [mode], - ), - returnValueForMissingStub: null, - ); + Invocation.method(#setFlashMode, [mode]), + returnValueForMissingStub: null, + ); @override - double getMaxZoomLevel() => (super.noSuchMethod( - Invocation.method( - #getMaxZoomLevel, - [], - ), - returnValue: 0.0, - returnValueForMissingStub: 0.0, - ) as double); + double getMaxZoomLevel() => + (super.noSuchMethod( + Invocation.method(#getMaxZoomLevel, []), + returnValue: 0.0, + returnValueForMissingStub: 0.0, + ) + as double); @override - double getMinZoomLevel() => (super.noSuchMethod( - Invocation.method( - #getMinZoomLevel, - [], - ), - returnValue: 0.0, - returnValueForMissingStub: 0.0, - ) as double); + double getMinZoomLevel() => + (super.noSuchMethod( + Invocation.method(#getMinZoomLevel, []), + returnValue: 0.0, + returnValueForMissingStub: 0.0, + ) + as double); @override void setZoomLevel(double? zoom) => super.noSuchMethod( - Invocation.method( - #setZoomLevel, - [zoom], - ), - returnValueForMissingStub: null, - ); + Invocation.method(#setZoomLevel, [zoom]), + returnValueForMissingStub: null, + ); + + @override + String getViewType() => + (super.noSuchMethod( + Invocation.method(#getViewType, []), + returnValue: _i12.dummyValue( + this, + Invocation.method(#getViewType, []), + ), + returnValueForMissingStub: _i12.dummyValue( + this, + Invocation.method(#getViewType, []), + ), + ) + as String); + + @override + _i6.Future startVideoRecording() => + (super.noSuchMethod( + Invocation.method(#startVideoRecording, []), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); + + @override + _i6.Future pauseVideoRecording() => + (super.noSuchMethod( + Invocation.method(#pauseVideoRecording, []), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); + + @override + _i6.Future resumeVideoRecording() => + (super.noSuchMethod( + Invocation.method(#resumeVideoRecording, []), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); + + @override + _i6.Future<_i5.XFile> stopVideoRecording() => + (super.noSuchMethod( + Invocation.method(#stopVideoRecording, []), + returnValue: _i6.Future<_i5.XFile>.value( + _FakeXFile_7(this, Invocation.method(#stopVideoRecording, [])), + ), + returnValueForMissingStub: _i6.Future<_i5.XFile>.value( + _FakeXFile_7(this, Invocation.method(#stopVideoRecording, [])), + ), + ) + as _i6.Future<_i5.XFile>); + + @override + _i6.Future dispose() => + (super.noSuchMethod( + Invocation.method(#dispose, []), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); @override - String getViewType() => (super.noSuchMethod( - Invocation.method( - #getViewType, - [], - ), - returnValue: _i12.dummyValue( - this, - Invocation.method( - #getViewType, - [], - ), - ), - returnValueForMissingStub: _i12.dummyValue( - this, - Invocation.method( - #getViewType, - [], - ), - ), - ) as String); - - @override - _i6.Future startVideoRecording() => (super.noSuchMethod( - Invocation.method( - #startVideoRecording, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - - @override - _i6.Future pauseVideoRecording() => (super.noSuchMethod( - Invocation.method( - #pauseVideoRecording, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - - @override - _i6.Future resumeVideoRecording() => (super.noSuchMethod( - Invocation.method( - #resumeVideoRecording, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - - @override - _i6.Future<_i5.XFile> stopVideoRecording() => (super.noSuchMethod( - Invocation.method( - #stopVideoRecording, - [], - ), - returnValue: _i6.Future<_i5.XFile>.value(_FakeXFile_7( - this, - Invocation.method( - #stopVideoRecording, - [], - ), - )), - returnValueForMissingStub: _i6.Future<_i5.XFile>.value(_FakeXFile_7( - this, - Invocation.method( - #stopVideoRecording, - [], - ), - )), - ) as _i6.Future<_i5.XFile>); - - @override - _i6.Future dispose() => (super.noSuchMethod( - Invocation.method( - #dispose, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - - @override - _i6.Stream<_i5.CameraImageData> cameraFrameStream( - {_i5.CameraImageStreamOptions? options}) => - (super.noSuchMethod( - Invocation.method( - #cameraFrameStream, - [], - {#options: options}, - ), - returnValue: _i6.Stream<_i5.CameraImageData>.empty(), - returnValueForMissingStub: _i6.Stream<_i5.CameraImageData>.empty(), - ) as _i6.Stream<_i5.CameraImageData>); + _i6.Stream<_i5.CameraImageData> cameraFrameStream({ + _i5.CameraImageStreamOptions? options, + }) => + (super.noSuchMethod( + Invocation.method(#cameraFrameStream, [], {#options: options}), + returnValue: _i6.Stream<_i5.CameraImageData>.empty(), + returnValueForMissingStub: _i6.Stream<_i5.CameraImageData>.empty(), + ) + as _i6.Stream<_i5.CameraImageData>); } /// A class which mocks [CameraOptions]. @@ -894,38 +737,41 @@ class MockCamera extends _i1.Mock implements _i10.Camera { // ignore: must_be_immutable class MockCameraOptions extends _i1.Mock implements _i3.CameraOptions { @override - _i3.AudioConstraints get audio => (super.noSuchMethod( - Invocation.getter(#audio), - returnValue: _FakeAudioConstraints_8( - this, - Invocation.getter(#audio), - ), - returnValueForMissingStub: _FakeAudioConstraints_8( - this, - Invocation.getter(#audio), - ), - ) as _i3.AudioConstraints); - - @override - _i3.VideoConstraints get video => (super.noSuchMethod( - Invocation.getter(#video), - returnValue: _FakeVideoConstraints_9( - this, - Invocation.getter(#video), - ), - returnValueForMissingStub: _FakeVideoConstraints_9( - this, - Invocation.getter(#video), - ), - ) as _i3.VideoConstraints); - - @override - _i7.MediaStreamConstraints toMediaStreamConstraints() => (super.noSuchMethod( - Invocation.method( - #toMediaStreamConstraints, - [], - ), - returnValue: _i9.toMediaStreamConstraintsShim(), - returnValueForMissingStub: _i9.toMediaStreamConstraintsShim(), - ) as _i7.MediaStreamConstraints); + _i3.AudioConstraints get audio => + (super.noSuchMethod( + Invocation.getter(#audio), + returnValue: _FakeAudioConstraints_8( + this, + Invocation.getter(#audio), + ), + returnValueForMissingStub: _FakeAudioConstraints_8( + this, + Invocation.getter(#audio), + ), + ) + as _i3.AudioConstraints); + + @override + _i3.VideoConstraints get video => + (super.noSuchMethod( + Invocation.getter(#video), + returnValue: _FakeVideoConstraints_9( + this, + Invocation.getter(#video), + ), + returnValueForMissingStub: _FakeVideoConstraints_9( + this, + Invocation.getter(#video), + ), + ) + as _i3.VideoConstraints); + + @override + _i7.MediaStreamConstraints toMediaStreamConstraints() => + (super.noSuchMethod( + Invocation.method(#toMediaStreamConstraints, []), + returnValue: _i9.toMediaStreamConstraintsShim(), + returnValueForMissingStub: _i9.toMediaStreamConstraintsShim(), + ) + as _i7.MediaStreamConstraints); } From 08d33a8c6300ab44400869e5e6df56ca4d6535a8 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 17:13:12 +0530 Subject: [PATCH 55/68] [camera_web] chore: streaming frame time tolerance --- packages/camera/camera_web/lib/src/camera.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 44dd9a16c08..8bc8dad81bf 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -165,7 +165,7 @@ class Camera { final bool canUseOffscreenCanvas; /// The tolerance for the camera streaming frame time. - final int _frameTimeToleranceMs = 8; + int get _frameTimeToleranceMs => 1000 / cameraStreamFPS ~/ 2; /// Initializes the camera stream displayed in the [videoElement]. /// Registers the camera view with [textureId] under [_getViewType] type. From cc1b89582346a6f3ee637dd997c68e3c442d0b4d Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 17:14:07 +0530 Subject: [PATCH 56/68] [camera_web] docs: updated comment indentation --- packages/camera/camera_web/lib/src/camera_service.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index af086ca875f..280caa3a3c9 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -363,18 +363,18 @@ class CameraService { } } - ///Used to check if browser has OffscreenCanvas capability + /// Used to check if browser has OffscreenCanvas capability bool hasPropertyOffScreenCanvas() { return jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS); } - ///Used in [takeFrame] if `OffscreenCanvas` is not supported + /// Used in [takeFrame] if `OffscreenCanvas` is not supported web.CanvasElement? _canvasElement; - ///Used in [takeFrame] if `OffscreenCanvas` is supported + /// Used in [takeFrame] if `OffscreenCanvas` is supported web.OffscreenCanvas? _offscreenCanvas; - ///Returns frame at a specific time using video element + /// Returns frame at a specific time using video element CameraImageData takeFrame(web.VideoElement videoElement) { final int width = videoElement.videoWidth; final int height = videoElement.videoHeight; From 37a029e9afe27b56df07998075ac28d497bfe637 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 17:16:10 +0530 Subject: [PATCH 57/68] [camera_web] chore: imageData late to final --- packages/camera/camera_web/lib/src/camera_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 280caa3a3c9..c4badf109db 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -383,7 +383,7 @@ class CameraService { 'Computed dimensions are zero: width=$width, height=$height', ); } - late web.ImageData imageData; + final web.ImageData imageData; if (hasPropertyOffScreenCanvas()) { if (_offscreenCanvas == null || _offscreenCanvas!.width != width || From 0a3390a5e7ad0901eb500c69173059994f8b5e06 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 17:22:48 +0530 Subject: [PATCH 58/68] [camera_web] chore: optimized offscreen canvas resize --- .../camera/camera_web/lib/src/camera_service.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index c4badf109db..110ba870e69 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -385,17 +385,20 @@ class CameraService { } final web.ImageData imageData; if (hasPropertyOffScreenCanvas()) { - if (_offscreenCanvas == null || - _offscreenCanvas!.width != width || + _offscreenCanvas ??= web.OffscreenCanvas(width, height); + if (_offscreenCanvas!.width != width || _offscreenCanvas!.height != height) { - _offscreenCanvas = web.OffscreenCanvas(width, height); + _offscreenCanvas! + ..width = width + ..height = height; } - final web.OffscreenCanvasRenderingContext2D context = + final context = _offscreenCanvas!.getContext( '2d', {'willReadFrequently': true}.jsify(), )! as web.OffscreenCanvasRenderingContext2D; + context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); } else { From 19ec1c306b97767f78d5778868ed44ab890e1ab9 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 17:23:17 +0530 Subject: [PATCH 59/68] [camera_web] chore: optimized canvas element resize --- .../camera/camera_web/lib/src/camera_service.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 110ba870e69..0e8f0754f89 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -402,13 +402,13 @@ class CameraService { context.drawImage(videoElement, 0, 0); imageData = context.getImageData(0, 0, width, height); } else { - if (_canvasElement == null || - _canvasElement!.width != width || - _canvasElement!.height != height) { - _canvasElement = - web.CanvasElement() - ..height = height - ..width = width; + _canvasElement ??= web.CanvasElement() + ..height = height + ..width = width; + if (_canvasElement!.width != width || _canvasElement!.height != height) { + _canvasElement! + ..width = width + ..height = height; } final web.CanvasRenderingContext2D context = _canvasElement!.context2D; From 526df97e9bf36d3d8d10bfce7f34580b30061644 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 17:32:46 +0530 Subject: [PATCH 60/68] [camera_web] perf: Reuse offscreen canvas context --- packages/camera/camera_web/lib/src/camera_service.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 0e8f0754f89..23b41230af3 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -374,6 +374,9 @@ class CameraService { /// Used in [takeFrame] if `OffscreenCanvas` is supported web.OffscreenCanvas? _offscreenCanvas; + /// Used in [takeFrame] to cache 2D rendering context of [_offscreenCanvas] + web.OffscreenCanvasRenderingContext2D? _offscreenCanvasContext; + /// Returns frame at a specific time using video element CameraImageData takeFrame(web.VideoElement videoElement) { final int width = videoElement.videoWidth; @@ -392,15 +395,15 @@ class CameraService { ..width = width ..height = height; } - final context = + _offscreenCanvasContext ??= _offscreenCanvas!.getContext( '2d', {'willReadFrequently': true}.jsify(), )! as web.OffscreenCanvasRenderingContext2D; - context.drawImage(videoElement, 0, 0); - imageData = context.getImageData(0, 0, width, height); + _offscreenCanvasContext!.drawImage(videoElement, 0, 0); + imageData = _offscreenCanvasContext!.getImageData(0, 0, width, height); } else { _canvasElement ??= web.CanvasElement() ..height = height From e0ec25e09f296c9ad747c366a657d61581df5ed0 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 18:45:35 +0530 Subject: [PATCH 61/68] [camera_web] bump: camera_platform_interface --- packages/camera/camera_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index e0d446e6582..e0807d39a80 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -17,7 +17,7 @@ flutter: fileName: camera_web.dart dependencies: - camera_platform_interface: ^2.6.0 + camera_platform_interface: ^2.12.0 flutter: sdk: flutter flutter_web_plugins: From 0c0d694600f36b518d12cf45944dd8ad7c0cfa97 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 18:46:32 +0530 Subject: [PATCH 62/68] [camera_web] feat: enabled supportsImageStreaming to return true --- packages/camera/camera_web/lib/src/camera_web.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index ba12e5b3f2d..f346a6fb95b 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -726,4 +726,7 @@ class CameraPlugin extends CameraPlatform { ), ); } + + @override + bool supportsImageStreaming() => true; } From f38e0b793acd2e9624df23f46a9f9ee8e839c09b Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 18:46:51 +0530 Subject: [PATCH 63/68] [camera_web] test: added test for supportsImageStreaming --- .../example/integration_test/camera_web_test.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 2bced6f0195..46875c06008 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -2221,6 +2221,12 @@ void main() { ); }); + testWidgets('supportsImageStreaming returns true', ( + WidgetTester tester, + ) async { + expect(CameraPlatform.instance.supportsImageStreaming(), isTrue); + }); + group('dispose', () { late Camera camera; late MockVideoElement mockVideoElement; From 6ac4c876bfb30ea326792f3a247488acb78c93e8 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Fri, 3 Apr 2026 20:10:49 +0530 Subject: [PATCH 64/68] [camera_web] fix: changelog * --- packages/camera/camera_web/CHANGELOG.md | 88 ++++++++++++------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 7d3587770a4..93751515450 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,129 +1,129 @@ ## 0.3.6 -- Adds support for camera image stream on web. -- Updates minimum supported SDK version to Flutter 3.35/Dart 3.9. +* Adds support for camera image stream on web. +* Updates minimum supported SDK version to Flutter 3.35/Dart 3.9. ## 0.3.5+3 -- Fixes camera initialization failure on Safari by fixing a null check operator error using +* Fixes camera initialization failure on Safari by fixing a null check operator error using a nullable getter and null safe practices. ## 0.3.5+2 -- Fixes camera initialization failure on Firefox Android by using `{video: true}` instead +* Fixes camera initialization failure on Firefox Android by using `{video: true}` instead of `{video: {}}` when no video constraints are specified. ## 0.3.5+1 -- Uses `CanvasRenderingContext2D.drawImage` instead of the deprecated `drawImageScaled`. -- Updates minimum supported SDK version to Flutter 3.32/Dart 3.8. +* Uses `CanvasRenderingContext2D.drawImage` instead of the deprecated `drawImageScaled`. +* Updates minimum supported SDK version to Flutter 3.32/Dart 3.8. ## 0.3.5 -- Migrates to package:web to support WASM -- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Migrates to package:web to support WASM +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.3.4 -- Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at +* Removes `maxVideoDuration`/`maxDuration`, as the feature was never exposed at the app-facing package level, and is deprecated at the platform interface level. -- Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. +* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 0.3.3 -- Adds support to control video FPS and bitrate. See `CameraController.withSettings`. -- Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Adds support to control video FPS and bitrate. See `CameraController.withSettings`. +* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.3.2+4 -- Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. -- Fixes new lint warnings. +* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. +* Fixes new lint warnings. ## 0.3.2+3 -- Migrates to `dart:ui_web` APIs. -- Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. +* Migrates to `dart:ui_web` APIs. +* Updates minimum supported SDK version to Flutter 3.13.0/Dart 3.1.0. ## 0.3.2+2 -- Adds pub topics to package metadata. -- Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. ## 0.3.2+1 -- Updates README to improve example of `Image` creation. +* Updates README to improve example of `Image` creation. ## 0.3.2 -- Changes `availableCameras` to not ask for the microphone permission. +* Changes `availableCameras` to not ask for the microphone permission. ## 0.3.1+4 -- Removes obsolete null checks on non-nullable values. -- Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +* Removes obsolete null checks on non-nullable values. +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 0.3.1+3 -- Clarifies explanation of endorsement in README. -- Aligns Dart and Flutter SDK constraints. +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. ## 0.3.1+2 -- Updates links for the merge of flutter/plugins into flutter/packages. -- Updates minimum Flutter version to 3.0. +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates minimum Flutter version to 3.0. ## 0.3.1+1 -- Updates code for stricter lint checks. +* Updates code for stricter lint checks. ## 0.3.1 -- Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). +* Updates to latest camera platform interface, and fails if user attempts to use streaming with recording (since streaming is currently unsupported on web). ## 0.3.0+1 -- Updates imports for `prefer_relative_imports`. -- Updates minimum Flutter version to 2.10. -- Fixes avoid_redundant_argument_values lint warnings and minor typos. -- Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Updates imports for `prefer_relative_imports`. +* Updates minimum Flutter version to 2.10. +* Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.3.0 -- **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. +* **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. ## 0.2.1+6 -- Minor fixes for new analysis options. +* Minor fixes for new analysis options. ## 0.2.1+5 -- Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors lint warnings. ## 0.2.1+4 -- Migrates from `ui.hash*` to `Object.hash*`. -- Updates minimum Flutter version for changes in 0.2.1+3. +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version for changes in 0.2.1+3. ## 0.2.1+3 -- Internal code cleanup for stricter analysis options. +* Internal code cleanup for stricter analysis options. ## 0.2.1+2 -- Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. -- Implemented support for new Dart SDKs with an async requestFullscreen API. +* Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. +* Implemented support for new Dart SDKs with an async requestFullscreen API. ## 0.2.1+1 -- Update usage documentation. +* Update usage documentation. ## 0.2.1 -- Add video recording functionality. -- Fix cameraNotReadable error that prevented access to the camera on some Android devices. +* Add video recording functionality. +* Fix cameraNotReadable error that prevented access to the camera on some Android devices. ## 0.2.0 -- Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. +* Initial release, adapted from the Flutter [I/O Photobooth](https://photobooth.flutter.dev/) project. From 06ce8de560311a49688c43bc9197fbbc38413599 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Wed, 8 Apr 2026 20:28:49 +0530 Subject: [PATCH 65/68] [camera_web]: fix lint issues --- .../integration_test/camera_service_test.dart | 12 ++++++------ .../example/integration_test/camera_test.dart | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 500aa4c5c1b..e0397f4b9ab 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -983,12 +983,12 @@ void main() { }); testWidgets('returns true if broswer has OffscreenCanvas ' 'otherwise false', (WidgetTester widgetTester) async { - for (final bool supportsOffscreenCanvas in [true, false]) { + for (final supportsOffscreenCanvas in [true, false]) { when( jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS), ).thenReturn(supportsOffscreenCanvas); - final bool hasOffScreenCanvas = - cameraService.hasPropertyOffScreenCanvas(); + final bool hasOffScreenCanvas = cameraService + .hasPropertyOffScreenCanvas(); expect( hasOffScreenCanvas, supportsOffscreenCanvas ? isTrue : isFalse, @@ -1000,8 +1000,8 @@ void main() { 'regardless of OffscreenCanvas support', ( WidgetTester widgetTester, ) async { - const Size size = Size(10, 10); - final Completer completer = Completer(); + const size = Size(10, 10); + final completer = Completer(); final web.VideoElement videoElement = getVideoElementWithBlankStream(size) ..onLoadedMetadata.listen((_) { @@ -1009,7 +1009,7 @@ void main() { }) ..load(); await completer.future; - for (final bool supportsOffscreenCanvas in [true, false]) { + for (final supportsOffscreenCanvas in [true, false]) { when( jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS), ).thenReturn(supportsOffscreenCanvas); diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 398385fd693..398dd278ca9 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1546,12 +1546,12 @@ void main() { final VideoElement videoElement = getVideoElementWithBlankStream( const Size(10, 10), ); - final Camera camera = Camera( + final camera = Camera( textureId: textureId, cameraService: cameraService, )..videoElement = videoElement; - for (final bool supportsOffscreenCanvas in [true, false]) { + for (final supportsOffscreenCanvas in [true, false]) { when( cameraService.hasPropertyOffScreenCanvas(), ).thenReturn(supportsOffscreenCanvas); @@ -1570,8 +1570,9 @@ void main() { ), ); - final CameraImageData cameraImageData = - await camera.cameraFrameStream().first; + final CameraImageData cameraImageData = await camera + .cameraFrameStream() + .first; expect( cameraImageData, From 15f28b58efe8a36e2da63f7a66e347dd9287092a Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Thu, 9 Apr 2026 00:09:04 +0530 Subject: [PATCH 66/68] [camera_web] fix mockito mock code gen by removing const record --- .../integration_test/helpers/mocks.mocks.dart | 172 +++++++++--------- .../camera/camera_web/lib/src/camera.dart | 6 +- 2 files changed, 88 insertions(+), 90 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart index e5bfb421726..44959b87e7e 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in camera_web_integration_tests/integration_test/helpers/mocks.dart. // Do not manually edit this file. @@ -33,6 +33,7 @@ import 'mocks.dart' as _i9; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeJsUtil_0 extends _i1.SmartFake implements _i2.JsUtil { _FakeJsUtil_0(Object parent, Invocation parentInvocation) @@ -103,12 +104,6 @@ class MockCameraService extends _i1.Mock implements _i8.CameraService { ) as _i7.Window); - @override - set window(_i7.Window? _window) => super.noSuchMethod( - Invocation.setter(#window, _window), - returnValueForMissingStub: null, - ); - @override _i2.JsUtil get jsUtil => (super.noSuchMethod( @@ -122,8 +117,14 @@ class MockCameraService extends _i1.Mock implements _i8.CameraService { as _i2.JsUtil); @override - set jsUtil(_i2.JsUtil? _jsUtil) => super.noSuchMethod( - Invocation.setter(#jsUtil, _jsUtil), + set window(_i7.Window? value) => super.noSuchMethod( + Invocation.setter(#window, value), + returnValueForMissingStub: null, + ); + + @override + set jsUtil(_i2.JsUtil? value) => super.noSuchMethod( + Invocation.setter(#jsUtil, value), returnValueForMissingStub: null, ); @@ -362,12 +363,6 @@ class MockCamera extends _i1.Mock implements _i10.Camera { ) as _i7.HTMLVideoElement); - @override - set videoElement(_i7.HTMLVideoElement? _videoElement) => super.noSuchMethod( - Invocation.setter(#videoElement, _videoElement), - returnValueForMissingStub: null, - ); - @override _i7.HTMLDivElement get divElement => (super.noSuchMethod( @@ -378,16 +373,13 @@ class MockCamera extends _i1.Mock implements _i10.Camera { as _i7.HTMLDivElement); @override - set divElement(_i7.HTMLDivElement? _divElement) => super.noSuchMethod( - Invocation.setter(#divElement, _divElement), - returnValueForMissingStub: null, - ); - - @override - set stream(_i7.MediaStream? _stream) => super.noSuchMethod( - Invocation.setter(#stream, _stream), - returnValueForMissingStub: null, - ); + _i6.Stream<_i7.MediaStreamTrack> get onEnded => + (super.noSuchMethod( + Invocation.getter(#onEnded), + returnValue: _i6.Stream<_i7.MediaStreamTrack>.empty(), + returnValueForMissingStub: _i6.Stream<_i7.MediaStreamTrack>.empty(), + ) + as _i6.Stream<_i7.MediaStreamTrack>); @override _i6.StreamController<_i7.MediaStreamTrack> get onEndedController => @@ -405,6 +397,15 @@ class MockCamera extends _i1.Mock implements _i10.Camera { ) as _i6.StreamController<_i7.MediaStreamTrack>); + @override + _i6.Stream<_i7.ErrorEvent> get onVideoRecordingError => + (super.noSuchMethod( + Invocation.getter(#onVideoRecordingError), + returnValue: _i6.Stream<_i7.ErrorEvent>.empty(), + returnValueForMissingStub: _i6.Stream<_i7.ErrorEvent>.empty(), + ) + as _i6.Stream<_i7.ErrorEvent>); + @override _i7.EventStreamProvider<_i7.Event> get mediaRecorderOnErrorProvider => (super.noSuchMethod( @@ -420,17 +421,6 @@ class MockCamera extends _i1.Mock implements _i10.Camera { ) as _i7.EventStreamProvider<_i7.Event>); - @override - set mediaRecorderOnErrorProvider( - _i7.EventStreamProvider<_i7.Event>? _mediaRecorderOnErrorProvider, - ) => super.noSuchMethod( - Invocation.setter( - #mediaRecorderOnErrorProvider, - _mediaRecorderOnErrorProvider, - ), - returnValueForMissingStub: null, - ); - @override _i6.StreamController<_i7.ErrorEvent> get videoRecordingErrorController => (super.noSuchMethod( @@ -446,12 +436,6 @@ class MockCamera extends _i1.Mock implements _i10.Camera { ) as _i6.StreamController<_i7.ErrorEvent>); - @override - set flashMode(_i5.FlashMode? _flashMode) => super.noSuchMethod( - Invocation.setter(#flashMode, _flashMode), - returnValueForMissingStub: null, - ); - @override _i7.Window get window => (super.noSuchMethod( @@ -461,18 +445,6 @@ class MockCamera extends _i1.Mock implements _i10.Camera { ) as _i7.Window); - @override - set window(_i7.Window? _window) => super.noSuchMethod( - Invocation.setter(#window, _window), - returnValueForMissingStub: null, - ); - - @override - set mediaRecorder(_i7.MediaRecorder? _mediaRecorder) => super.noSuchMethod( - Invocation.setter(#mediaRecorder, _mediaRecorder), - returnValueForMissingStub: null, - ); - @override bool Function(String) get isVideoTypeSupported => (super.noSuchMethod( @@ -482,13 +454,6 @@ class MockCamera extends _i1.Mock implements _i10.Camera { ) as bool Function(String)); - @override - set isVideoTypeSupported(bool Function(String)? _isVideoTypeSupported) => - super.noSuchMethod( - Invocation.setter(#isVideoTypeSupported, _isVideoTypeSupported), - returnValueForMissingStub: null, - ); - @override _i7.Blob Function(List<_i7.Blob>, String) get blobBuilder => (super.noSuchMethod( @@ -499,11 +464,14 @@ class MockCamera extends _i1.Mock implements _i10.Camera { as _i7.Blob Function(List<_i7.Blob>, String)); @override - set blobBuilder(_i7.Blob Function(List<_i7.Blob>, String)? _blobBuilder) => - super.noSuchMethod( - Invocation.setter(#blobBuilder, _blobBuilder), - returnValueForMissingStub: null, - ); + _i6.Stream<_i5.VideoRecordedEvent> get onVideoRecordedEvent => + (super.noSuchMethod( + Invocation.getter(#onVideoRecordedEvent), + returnValue: _i6.Stream<_i5.VideoRecordedEvent>.empty(), + returnValueForMissingStub: + _i6.Stream<_i5.VideoRecordedEvent>.empty(), + ) + as _i6.Stream<_i5.VideoRecordedEvent>); @override _i6.StreamController<_i5.VideoRecordedEvent> get videoRecorderController => @@ -540,32 +508,60 @@ class MockCamera extends _i1.Mock implements _i10.Camera { as int); @override - _i6.Stream<_i7.MediaStreamTrack> get onEnded => - (super.noSuchMethod( - Invocation.getter(#onEnded), - returnValue: _i6.Stream<_i7.MediaStreamTrack>.empty(), - returnValueForMissingStub: _i6.Stream<_i7.MediaStreamTrack>.empty(), - ) - as _i6.Stream<_i7.MediaStreamTrack>); + set videoElement(_i7.HTMLVideoElement? value) => super.noSuchMethod( + Invocation.setter(#videoElement, value), + returnValueForMissingStub: null, + ); @override - _i6.Stream<_i7.ErrorEvent> get onVideoRecordingError => - (super.noSuchMethod( - Invocation.getter(#onVideoRecordingError), - returnValue: _i6.Stream<_i7.ErrorEvent>.empty(), - returnValueForMissingStub: _i6.Stream<_i7.ErrorEvent>.empty(), - ) - as _i6.Stream<_i7.ErrorEvent>); + set divElement(_i7.HTMLDivElement? value) => super.noSuchMethod( + Invocation.setter(#divElement, value), + returnValueForMissingStub: null, + ); @override - _i6.Stream<_i5.VideoRecordedEvent> get onVideoRecordedEvent => - (super.noSuchMethod( - Invocation.getter(#onVideoRecordedEvent), - returnValue: _i6.Stream<_i5.VideoRecordedEvent>.empty(), - returnValueForMissingStub: - _i6.Stream<_i5.VideoRecordedEvent>.empty(), - ) - as _i6.Stream<_i5.VideoRecordedEvent>); + set stream(_i7.MediaStream? value) => super.noSuchMethod( + Invocation.setter(#stream, value), + returnValueForMissingStub: null, + ); + + @override + set mediaRecorderOnErrorProvider(_i7.EventStreamProvider<_i7.Event>? value) => + super.noSuchMethod( + Invocation.setter(#mediaRecorderOnErrorProvider, value), + returnValueForMissingStub: null, + ); + + @override + set flashMode(_i5.FlashMode? value) => super.noSuchMethod( + Invocation.setter(#flashMode, value), + returnValueForMissingStub: null, + ); + + @override + set window(_i7.Window? value) => super.noSuchMethod( + Invocation.setter(#window, value), + returnValueForMissingStub: null, + ); + + @override + set mediaRecorder(_i7.MediaRecorder? value) => super.noSuchMethod( + Invocation.setter(#mediaRecorder, value), + returnValueForMissingStub: null, + ); + + @override + set isVideoTypeSupported(bool Function(String)? value) => super.noSuchMethod( + Invocation.setter(#isVideoTypeSupported, value), + returnValueForMissingStub: null, + ); + + @override + set blobBuilder(_i7.Blob Function(List<_i7.Blob>, String)? value) => + super.noSuchMethod( + Invocation.setter(#blobBuilder, value), + returnValueForMissingStub: null, + ); @override _i6.Future initialize() => diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 8bc8dad81bf..135c6bcd278 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -48,8 +48,10 @@ class Camera { required this.textureId, required CameraService cameraService, this.options = const CameraOptions(), - this.recorderOptions = const (audioBitrate: null, videoBitrate: null), - }) : _cameraService = cameraService, + ({int? audioBitrate, int? videoBitrate})? recorderOptions, + }) : recorderOptions = + recorderOptions ?? (audioBitrate: null, videoBitrate: null), + _cameraService = cameraService, canUseOffscreenCanvas = cameraService.hasPropertyOffScreenCanvas(); /// The texture id used to register the camera view. From 4c031b4ab0f9f9a044fa6f36ef3c2b46c42c9f12 Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Sat, 11 Apr 2026 13:22:33 +0530 Subject: [PATCH 67/68] [camera_web] dart format --- .../example/integration_test/camera_bitrate_test.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart b/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart index a8097ddaad1..5c0017d8eb0 100644 --- a/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_bitrate_test.dart @@ -65,11 +65,10 @@ void main() { mockWindow.navigator = navigator; mockNavigator.mediaDevices = mediaDevices; - final canvasElement = - HTMLCanvasElement() - ..width = videoSize.width.toInt() - ..height = videoSize.height.toInt() - ..context2D.clearRect(0, 0, videoSize.width, videoSize.height); + final canvasElement = HTMLCanvasElement() + ..width = videoSize.width.toInt() + ..height = videoSize.height.toInt() + ..context2D.clearRect(0, 0, videoSize.width, videoSize.height); final videoElement = HTMLVideoElement(); From 8869a5c3ec6429b40bda5e1121e8932a4206b71e Mon Sep 17 00:00:00 2001 From: Khushal Singh Rao Date: Mon, 18 May 2026 03:37:38 +0530 Subject: [PATCH 68/68] [camera_web]: Addressed comments --- .../example/integration_test/camera_test.dart | 7 + .../camera/camera_web/lib/src/camera.dart | 6 +- .../camera_web/lib/src/camera_service.dart | 121 ++++++++++++------ .../camera_web/lib/src/pkg_web_tweaks.dart | 15 +++ 4 files changed, 106 insertions(+), 43 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 398dd278ca9..da1e41512ac 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1541,6 +1541,13 @@ void main() { }); }); group('cameraFrameStream', () { + testWidgets('Target cameraStreamFPS is 60', (WidgetTester tester) async { + final camera = Camera( + textureId: textureId, + cameraService: cameraService, + ); + expect(camera.cameraStreamFPS, equals(60)); + }); testWidgets('CameraImageData bytes is a multiple of 4 ' 'regardless of OffscreenCanvas support', (WidgetTester tester) async { final VideoElement videoElement = getVideoElementWithBlankStream( diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 135c6bcd278..cf17e34bf0f 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -659,7 +659,11 @@ class Camera { // TODO(TecHaxter): Introduce FPS in CameraImageStreamOptions of // package:camera_platform_interface. // https://github.com/flutter/flutter/issues/176148 - /// Used for running the camera frame stream at a specified FPS + /// The target FPS for the camera frame stream. + /// + /// Frames are emitted within a tolerance window, so actual delivery may + /// slightly exceed or fall below this target depending on browser timing. + @visibleForTesting final int cameraStreamFPS = 60; /// Returns a stream of camera frames. diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 23b41230af3..bcc49ae1e76 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -368,15 +368,6 @@ class CameraService { return jsUtil.hasProperty(window, 'OffscreenCanvas'.toJS); } - /// Used in [takeFrame] if `OffscreenCanvas` is not supported - web.CanvasElement? _canvasElement; - - /// Used in [takeFrame] if `OffscreenCanvas` is supported - web.OffscreenCanvas? _offscreenCanvas; - - /// Used in [takeFrame] to cache 2D rendering context of [_offscreenCanvas] - web.OffscreenCanvasRenderingContext2D? _offscreenCanvasContext; - /// Returns frame at a specific time using video element CameraImageData takeFrame(web.VideoElement videoElement) { final int width = videoElement.videoWidth; @@ -386,48 +377,33 @@ class CameraService { 'Computed dimensions are zero: width=$width, height=$height', ); } + final imageDataSettings = WebTweakImageDataSettings(format: 'rgba-unorm8'); final web.ImageData imageData; if (hasPropertyOffScreenCanvas()) { - _offscreenCanvas ??= web.OffscreenCanvas(width, height); - if (_offscreenCanvas!.width != width || - _offscreenCanvas!.height != height) { - _offscreenCanvas! - ..width = width - ..height = height; - } - _offscreenCanvasContext ??= - _offscreenCanvas!.getContext( - '2d', - {'willReadFrequently': true}.jsify(), - )! - as web.OffscreenCanvasRenderingContext2D; - - _offscreenCanvasContext!.drawImage(videoElement, 0, 0); - imageData = _offscreenCanvasContext!.getImageData(0, 0, width, height); + imageData = _takeOffscreenCanvasFrame( + videoElement, + width: width, + height: height, + settings: imageDataSettings, + ); } else { - _canvasElement ??= web.CanvasElement() - ..height = height - ..width = width; - if (_canvasElement!.width != width || _canvasElement!.height != height) { - _canvasElement! - ..width = width - ..height = height; - } - final web.CanvasRenderingContext2D context = _canvasElement!.context2D; - - context.drawImageScaled( + imageData = _takeFallbackCanvasFrame( videoElement, - 0, - 0, - width.toDouble(), - height.toDouble(), + width: width, + height: height, + settings: imageDataSettings, ); - imageData = context.getImageData(0, 0, width, height); } final ByteBuffer byteBuffer = imageData.data.toDart.buffer; return CameraImageData( - format: const CameraImageFormat(ImageFormatGroup.unknown, raw: 0), + format: const CameraImageFormat( + // TODO(TecHaxter): Introduce ImageFormatGroup.rgba8888 in + // package:camera_platform_interface. + // https://github.com/flutter/flutter/issues/151193 + ImageFormatGroup.unknown, + raw: 'rgba8888', + ), planes: [ CameraImagePlane( bytes: byteBuffer.asUint8List(), @@ -438,4 +414,65 @@ class CameraService { width: width, ); } + + /// Used by [_takeOffscreenCanvasFrame] to cache the offscreen canvas + web.OffscreenCanvas? _offscreenCanvas; + + /// Used by [_takeOffscreenCanvasFrame] to cache the offscreen canvas context + web.OffscreenCanvasRenderingContext2D? _offscreenCanvasContext; + + /// Takes a video frame using `OffscreenCanvas` for better performance + web.ImageData _takeOffscreenCanvasFrame( + web.VideoElement videoElement, { + required int width, + required int height, + required WebTweakImageDataSettings settings, + }) { + _offscreenCanvas ??= web.OffscreenCanvas(width, height); + if (_offscreenCanvas!.width != width || + _offscreenCanvas!.height != height) { + _offscreenCanvas! + ..width = width + ..height = height; + } + _offscreenCanvasContext ??= + _offscreenCanvas!.getContext( + '2d', + {'willReadFrequently': true}.jsify(), + )! + as web.OffscreenCanvasRenderingContext2D; + + _offscreenCanvasContext!.drawImage(videoElement, 0, 0); + return _offscreenCanvasContext!.getImageData(0, 0, width, height, settings); + } + + /// Used by [_takeFallbackCanvasFrame] to cache the canvas element + web.CanvasElement? _canvasElement; + + /// Takes a video frame using a regular `CanvasElement` + web.ImageData _takeFallbackCanvasFrame( + web.VideoElement videoElement, { + required int width, + required int height, + required WebTweakImageDataSettings settings, + }) { + _canvasElement ??= web.CanvasElement() + ..height = height + ..width = width; + if (_canvasElement!.width != width || _canvasElement!.height != height) { + _canvasElement! + ..width = width + ..height = height; + } + final web.CanvasRenderingContext2D context = _canvasElement!.context2D; + + context.drawImageScaled( + videoElement, + 0, + 0, + width.toDouble(), + height.toDouble(), + ); + return context.getImageData(0, 0, width, height, settings); + } } diff --git a/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart b/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart index f5d54d7dfdf..31098db3594 100644 --- a/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart +++ b/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart @@ -72,3 +72,18 @@ extension type WebTweakMediaTrackConstraints._(JSObject _) implements JSObject { ConstrainBoolean torch, }); } + +extension type WebTweakImageDataSettings._(JSObject _) + implements ImageDataSettings { + external factory WebTweakImageDataSettings({ + PredefinedColorSpace colorSpace, + WebTweakPredefinedFormat? format, + }); + + external PredefinedColorSpace get colorSpace; + external set colorSpace(PredefinedColorSpace value); + external WebTweakPredefinedFormat? get format; + external set format(WebTweakPredefinedFormat? value); +} + +typedef WebTweakPredefinedFormat = String;