Skip to content

Commit

Permalink
[camera] CameraPlatform.createCameraWithSettings (flutter#3615)
Browse files Browse the repository at this point in the history
## Platform interface of federated plugin
This is the `platform-interface` part of `camera` PR flutter#3586.

## App-facing change

Previously, CameraController was unable to setup fps and bitrates, allowing only resolution settings like this:
```dart
controller = CameraController(_cameras[0], ResolutionPreset.max);
```

This PR gives additional functionality to set `fps` and `bitrates` via `withSettings`:

```dart
controller = CameraController.withSettings(
      _cameras[0],
      mediaSettings: const MediaSettings(
        resolutionPreset: ResolutionPreset.low,
        fps: 15,
        videoBitrate: 200000,
        audioBitrate: 32000,
        enableAudio: true,
      ),
    );
```

## Android, iOS, etc.

All platforms must implement `CameraPlatform.createCameraWithSettings` in addition to `CameraPlatform.createCamera`, providing platform specific code for `fps` and `bitrate' platform:
```dart
Future<int> createCamera(
    CameraDescription cameraDescription,
    CameraDescription cameraDescription,
    ResolutionPreset? resolutionPreset, {
    ResolutionPreset? resolutionPreset, {
    bool enableAudio = false,
    bool enableAudio = false,
  }) {
  }) =>
    throw UnimplementedError('createCamera() is not implemented.');
      createCameraWithSettings(
        cameraDescription,
        MediaSettings(
          resolutionPreset: resolutionPreset,
          enableAudio: enableAudio,
        ),
      );

  /// Creates an uninitialized camera instance and returns the cameraId.
  Future<int> createCameraWithSettings(
    CameraDescription cameraDescription,
    MediaSettings? mediaSettings,
  ) {
    throw UnimplementedError('createCameraWithSettings() is not implemented.');
  }
  }
```
  • Loading branch information
PROGrand committed Oct 23, 2023
1 parent a2d8672 commit 4bf5114
Show file tree
Hide file tree
Showing 10 changed files with 471 additions and 20 deletions.
4 changes: 4 additions & 0 deletions packages/camera/camera_platform_interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.6.0

* Adds support to control video fps and bitrate. See `CameraPlatform.createCameraWithSettings`.

## 2.5.2

* Adds pub topics to package metadata.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export 'package:cross_file/cross_file.dart';
export 'src/events/camera_event.dart';
export 'src/events/device_event.dart';
export 'src/platform_interface/camera_platform.dart';
export 'src/types/media_settings.dart';
export 'src/types/types.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,29 @@ class MethodChannelCamera extends CameraPlatform {
CameraDescription cameraDescription,
ResolutionPreset? resolutionPreset, {
bool enableAudio = false,
}) async {
}) async =>
createCameraWithSettings(
cameraDescription,
MediaSettings(
resolutionPreset: resolutionPreset, enableAudio: enableAudio));

@override
Future<int> createCameraWithSettings(
CameraDescription cameraDescription,
MediaSettings mediaSettings,
) async {
try {
final ResolutionPreset? resolutionPreset = mediaSettings.resolutionPreset;
final Map<String, dynamic>? reply = await _channel
.invokeMapMethod<String, dynamic>('create', <String, dynamic>{
'cameraName': cameraDescription.name,
'resolutionPreset': resolutionPreset != null
? _serializeResolutionPreset(resolutionPreset)
? _serializeResolutionPreset(mediaSettings.resolutionPreset!)
: null,
'enableAudio': enableAudio,
'fps': mediaSettings.fps,
'videoBitrate': mediaSettings.videoBitrate,
'audioBitrate': mediaSettings.audioBitrate,
'enableAudio': mediaSettings.enableAudio,
});

return reply!['cameraId']! as int;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ abstract class CameraPlatform extends PlatformInterface {
throw UnimplementedError('createCamera() is not implemented.');
}

/// Creates an uninitialized camera instance and returns the cameraId.
///
/// Pass MediaSettings() for defaults
Future<int> createCameraWithSettings(
CameraDescription cameraDescription,
MediaSettings mediaSettings,
) {
return createCamera(
cameraDescription,
mediaSettings.resolutionPreset,
enableAudio: mediaSettings.enableAudio,
);
}

/// Initializes the camera on the device.
///
/// [imageFormatGroup] is used to specify the image formatting used.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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.

// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes

import 'resolution_preset.dart';

/// Recording media settings.
///
/// Used in [CameraPlatform.createCameraWithSettings].
/// Allows to tune recorded video parameters, such as resolution, frame rate, bitrate.
/// If [fps], [videoBitrate] or [audioBitrate] are passed, they must be greater than zero.
class MediaSettings {
/// Creates a [MediaSettings].
const MediaSettings({
this.resolutionPreset,
this.fps,
this.videoBitrate,
this.audioBitrate,
this.enableAudio = false,
}) : assert(fps == null || fps > 0, 'fps must be null or greater than zero'),
assert(videoBitrate == null || videoBitrate > 0,
'videoBitrate must be null or greater than zero'),
assert(audioBitrate == null || audioBitrate > 0,
'audioBitrate must be null or greater than zero');

/// [ResolutionPreset] affect the quality of video recording and image capture.
final ResolutionPreset? resolutionPreset;

/// Rate at which frames should be captured by the camera in frames per second.
final int? fps;

/// The video encoding bit rate for recording.
final int? videoBitrate;

/// The audio encoding bit rate for recording.
final int? audioBitrate;

/// Controls audio presence in recorded video.
final bool enableAudio;

@override
bool operator ==(Object other) {
if (identical(other, this)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is MediaSettings &&
resolutionPreset == other.resolutionPreset &&
fps == other.fps &&
videoBitrate == other.videoBitrate &&
audioBitrate == other.audioBitrate &&
enableAudio == other.enableAudio;
}

@override
int get hashCode => Object.hash(
resolutionPreset,
fps,
videoBitrate,
audioBitrate,
enableAudio,
);

@override
String toString() {
return 'MediaSettings{'
'resolutionPreset: $resolutionPreset, '
'fps: $fps, '
'videoBitrate: $videoBitrate, '
'audioBitrate: $audioBitrate, '
'enableAudio: $enableAudio}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export 'exposure_mode.dart';
export 'flash_mode.dart';
export 'focus_mode.dart';
export 'image_format_group.dart';
export 'media_settings.dart';
export 'resolution_preset.dart';
export 'video_capture_options.dart';
2 changes: 1 addition & 1 deletion packages/camera/camera_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
version: 2.5.2
version: 2.6.0

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,67 @@ void main() {
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
ResolutionPreset.low,
),
throwsUnimplementedError,
);
});

test(
'Default implementation of createCameraWithSettings() should call createCamera() passing parameters',
() {
// Arrange
const CameraDescription cameraDescription = CameraDescription(
name: 'back',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
);

const MediaSettings mediaSettings = MediaSettings(
resolutionPreset: ResolutionPreset.low,
fps: 15,
videoBitrate: 200000,
audioBitrate: 32000,
enableAudio: true,
);

bool createCameraCalled = false;

final OverriddenCameraPlatform cameraPlatform = OverriddenCameraPlatform((
CameraDescription cameraDescriptionArg,
ResolutionPreset? resolutionPresetArg,
bool enableAudioArg,
) {
expect(
cameraDescriptionArg,
cameraDescription,
reason: 'should pass camera description',
);
expect(
resolutionPresetArg,
mediaSettings.resolutionPreset,
reason: 'should pass resolution preset',
);
expect(
enableAudioArg,
mediaSettings.enableAudio,
reason: 'should pass enableAudio',
);

createCameraCalled = true;
});

// Act & Assert
cameraPlatform.createCameraWithSettings(
cameraDescription,
mediaSettings,
);

expect(createCameraCalled, isTrue,
reason:
'default implementation of createCameraWithSettings should call createCamera passing parameters');
});

test(
'Default implementation of initializeCamera() should throw unimplemented error',
() {
Expand Down Expand Up @@ -496,3 +551,21 @@ class ImplementsCameraPlatform implements CameraPlatform {
}

class ExtendsCameraPlatform extends CameraPlatform {}

class OverriddenCameraPlatform extends CameraPlatform {
OverriddenCameraPlatform(this._onCreateCameraCalled);

final void Function(
CameraDescription cameraDescription,
ResolutionPreset? resolutionPreset,
bool enableAudio,
) _onCreateCameraCalled;

@override
Future<int> createCamera(
CameraDescription cameraDescription, ResolutionPreset? resolutionPreset,
{bool enableAudio = false}) {
_onCreateCameraCalled(cameraDescription, resolutionPreset, enableAudio);
return Future<int>.value(0);
}
}
Loading

0 comments on commit 4bf5114

Please sign in to comment.