blob: 3bf946029c27ba1d2b5f7aa780e11513c81d7c18 [file] [log] [blame]
// 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));
});
}