| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:html'; |
| import 'dart:math'; |
| import 'dart:ui'; |
| |
| import 'package:camera_platform_interface/camera_platform_interface.dart'; |
| import 'package:camera_web/camera_web.dart'; |
| import 'package:camera_web/src/camera.dart'; |
| import 'package:camera_web/src/types/types.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:integration_test/integration_test.dart'; |
| import 'package:mocktail/mocktail.dart'; |
| |
| import 'helpers/helpers.dart'; |
| |
| void main() { |
| IntegrationTestWidgetsFlutterBinding.ensureInitialized(); |
| |
| const Size videoSize = Size(320, 240); |
| |
| /// Draw some seconds of random video frames on canvas in realtime. |
| Future<void> simulateCamera(CanvasElement canvasElement) async { |
| const int fps = 15; |
| const int seconds = 3; |
| const int frameDuration = 1000 ~/ fps; |
| final Random random = Random(0); |
| |
| for (int n = 0; n < fps * seconds; n++) { |
| await Future<void>.delayed(const Duration(milliseconds: frameDuration)); |
| final int w = videoSize.width ~/ 20; |
| final int h = videoSize.height ~/ 20; |
| for (int y = 0; y < videoSize.height; y += h) { |
| for (int x = 0; x < videoSize.width; x += w) { |
| canvasElement.context2D.setFillColorRgb( |
| random.nextInt(255), random.nextInt(255), random.nextInt(255)); |
| canvasElement.context2D.fillRect(x, y, w, h); |
| } |
| } |
| } |
| } |
| |
| setUpAll(() { |
| registerFallbackValue(MockCameraOptions()); |
| }); |
| |
| testWidgets('Camera allows to control video bitrate', |
| (WidgetTester tester) async { |
| //const String supportedVideoType = 'video/webm'; |
| const String supportedVideoType = 'video/webm;codecs="vp9,opus"'; |
| bool isVideoTypeSupported(String type) => type == supportedVideoType; |
| |
| Future<int> recordVideo(int videoBitrate) async { |
| final Window window = MockWindow(); |
| final Navigator navigator = MockNavigator(); |
| final MediaDevices mediaDevices = MockMediaDevices(); |
| |
| when(() => window.navigator).thenReturn(navigator); |
| when(() => navigator.mediaDevices).thenReturn(mediaDevices); |
| |
| final CanvasElement canvasElement = CanvasElement( |
| width: videoSize.width.toInt(), |
| height: videoSize.height.toInt(), |
| )..context2D.clearRect(0, 0, videoSize.width, videoSize.height); |
| |
| final VideoElement videoElement = VideoElement(); |
| |
| final MockCameraService cameraService = MockCameraService(); |
| |
| CameraPlatform.instance = CameraPlugin( |
| cameraService: cameraService, |
| )..window = window; |
| |
| final CameraOptions options = CameraOptions( |
| audio: const AudioConstraints(), |
| video: VideoConstraints( |
| width: VideoSizeConstraint( |
| ideal: videoSize.width.toInt(), |
| ), |
| height: VideoSizeConstraint( |
| ideal: videoSize.height.toInt(), |
| ), |
| ), |
| ); |
| |
| final int cameraId = videoBitrate; |
| |
| when( |
| () { |
| return cameraService.getMediaStreamForOptions( |
| options, |
| cameraId: cameraId, |
| ); |
| }, |
| ).thenAnswer( |
| (_) => Future<MediaStream>.value(canvasElement.captureStream())); |
| |
| final Camera camera = Camera( |
| textureId: cameraId, |
| cameraService: cameraService, |
| options: options, |
| recorderOptions: ( |
| audioBitrate: null, |
| videoBitrate: videoBitrate, |
| )) |
| ..isVideoTypeSupported = isVideoTypeSupported; |
| |
| await camera.initialize(); |
| await camera.play(); |
| |
| await camera.startVideoRecording(); |
| |
| await simulateCamera(canvasElement); |
| |
| final XFile file = await camera.stopVideoRecording(); |
| |
| // Real movie can be saved locally during manual test invocation. |
| // First: add '--no-headless' to _targetDeviceFlags in |
| // `script/tool/lib/src/drive_examples_command.dart`, then uncomment: |
| // Second: uncomment next line |
| // await file.saveTo('movie.$videoBitrate.webm'); |
| |
| await camera.dispose(); |
| |
| final int length = await file.length(); |
| |
| videoElement.remove(); |
| |
| canvasElement.remove(); |
| |
| return length; |
| } |
| |
| const int kilobits = 1024; |
| const int megabits = kilobits * kilobits; |
| |
| final int lengthSmall = await recordVideo(500 * kilobits); |
| final int lengthLarge = await recordVideo(2 * megabits); |
| final int lengthMedium = await recordVideo(1 * megabits); |
| |
| expect(lengthSmall, lessThan(lengthMedium)); |
| expect(lengthMedium, lessThan(lengthLarge)); |
| }); |
| } |