Skip to content

[video_player_avfoundation] Adds Swift Package Manager compatibility #6634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ci/flutter_master.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bf7191fd388442428fc1ec20cb39514768cc20d4
f1037a01b796bbd5e7c2bd6320525fbd47702473
3 changes: 2 additions & 1 deletion packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.10.6

* Adds support to control video fps and bitrate. See `CameraController` constructor.
* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1.
* Updates support matrix in README to indicate that iOS 11 is no longer supported.
* Clients on versions of Flutter that still support iOS 11 can continue to use this
Expand Down
51 changes: 39 additions & 12 deletions packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,30 @@ class CameraValue {
/// To show the camera preview on the screen use a [CameraPreview] widget.
class CameraController extends ValueNotifier<CameraValue> {
/// Creates a new camera controller in an uninitialized state.
///
/// - [resolutionPreset] affect the quality of video recording and image capture.
/// - [enableAudio] controls audio presence in recorded video.
///
/// Following parameters (if present) will overwrite [resolutionPreset] settings:
/// - [fps] controls rate at which frames should be captured by the camera in frames per second.
/// - [videoBitrate] controls the video encoding bit rate for recording.
/// - [audioBitrate] controls the audio encoding bit rate for recording.
CameraController(
CameraDescription description,
this.resolutionPreset, {
this.enableAudio = true,
ResolutionPreset resolutionPreset, {
bool enableAudio = true,
int? fps,
int? videoBitrate,
int? audioBitrate,
this.imageFormatGroup,
}) : super(CameraValue.uninitialized(description));
}) : mediaSettings = MediaSettings(
resolutionPreset: resolutionPreset,
enableAudio: enableAudio,
fps: fps,
videoBitrate: videoBitrate,
audioBitrate: audioBitrate),
super(CameraValue.uninitialized(description));

/// The properties of the camera device controlled by this controller.
CameraDescription get description => value.description;
Expand All @@ -248,10 +266,19 @@ class CameraController extends ValueNotifier<CameraValue> {
/// if unavailable a lower resolution will be used.
///
/// See also: [ResolutionPreset].
final ResolutionPreset resolutionPreset;
ResolutionPreset get resolutionPreset =>
mediaSettings.resolutionPreset ?? ResolutionPreset.max;

/// Whether to include audio when recording a video.
final bool enableAudio;
bool get enableAudio => mediaSettings.enableAudio;

/// The media settings this controller is targeting.
///
/// This media settings are not guaranteed to be available on the device,
/// if unavailable a [resolutionPreset] default values will be used.
///
/// See also: [MediaSettings].
final MediaSettings mediaSettings;

/// The [ImageFormatGroup] describes the output of the raw image format.
///
Expand All @@ -265,6 +292,7 @@ class CameraController extends ValueNotifier<CameraValue> {

bool _isDisposed = false;
StreamSubscription<CameraImageData>? _imageStreamSubscription;

// A Future awaiting an attempt to initialize (e.g. after `initialize` was
// just called). If the controller has not been initialized at least once,
// this value is null.
Expand Down Expand Up @@ -313,10 +341,9 @@ class CameraController extends ValueNotifier<CameraValue> {
);
});

_cameraId = await CameraPlatform.instance.createCamera(
_cameraId = await CameraPlatform.instance.createCameraWithSettings(
description,
resolutionPreset,
enableAudio: enableAudio,
mediaSettings,
);

_unawaited(CameraPlatform.instance
Expand Down Expand Up @@ -372,7 +399,7 @@ class CameraController extends ValueNotifier<CameraValue> {

/// Pauses the current camera preview
Future<void> pausePreview() async {
if (value.isPreviewPaused) {
if (value.isPreviewPaused || !value.isInitialized || _isDisposed) {
return;
}
try {
Expand Down Expand Up @@ -923,7 +950,7 @@ class Optional<T> extends IterableBase<T> {
if (_value == null) {
throw StateError('value called on absent Optional.');
}
return _value!;
return _value;
}

/// Executes a function if the Optional value is present.
Expand Down Expand Up @@ -960,7 +987,7 @@ class Optional<T> extends IterableBase<T> {
Optional<S> transform<S>(S Function(T value) transformer) {
return _value == null
? Optional<S>.absent()
: Optional<S>.of(transformer(_value as T));
: Optional<S>.of(transformer(_value));
}

/// Transforms the Optional value.
Expand All @@ -971,7 +998,7 @@ class Optional<T> extends IterableBase<T> {
Optional<S> transformNullable<S>(S? Function(T value) transformer) {
return _value == null
? Optional<S>.absent()
: Optional<S>.fromNullable(transformer(_value as T));
: Optional<S>.fromNullable(transformer(_value));
}

@override
Expand Down
14 changes: 7 additions & 7 deletions packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ description: A Flutter plugin for controlling the camera. Supports previewing
Dart.
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
version: 0.10.5+9
version: 0.10.6

environment:
sdk: ^3.1.0
flutter: ">=3.13.0"
sdk: ^3.2.3
flutter: ">=3.16.6"

flutter:
plugin:
Expand All @@ -21,10 +21,10 @@ flutter:
default_package: camera_web

dependencies:
camera_android: ^0.10.7
camera_avfoundation: ^0.9.13
camera_platform_interface: ^2.5.0
camera_web: ^0.3.1
camera_android: ^0.10.9
camera_avfoundation: ^0.9.15
camera_platform_interface: ^2.6.0
camera_web: ^0.3.3
flutter:
sdk: flutter
flutter_plugin_android_lifecycle: ^2.0.2
Expand Down
10 changes: 10 additions & 0 deletions packages/camera/camera/test/camera_preview_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

import 'package:camera/camera.dart';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Expand Down Expand Up @@ -68,6 +69,15 @@ class FakeController extends ValueNotifier<CameraValue>
@override
ResolutionPreset get resolutionPreset => ResolutionPreset.low;

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

@override
Future<void> resumeVideoRecording() async {}

Expand Down
51 changes: 48 additions & 3 deletions packages/camera/camera/test/camera_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,46 @@ void main() {
expect(cameraController.value.isInitialized, isTrue);
});

test('can be initialized with media settings', () async {
final CameraController cameraController = CameraController(
const CameraDescription(
name: 'cam',
lensDirection: CameraLensDirection.back,
sensorOrientation: 90),
ResolutionPreset.low,
fps: 15,
videoBitrate: 200000,
audioBitrate: 32000,
enableAudio: false,
);
await cameraController.initialize();

expect(cameraController.value.aspectRatio, 1);
expect(cameraController.value.previewSize, const Size(75, 75));
expect(cameraController.value.isInitialized, isTrue);
expect(cameraController.resolutionPreset, ResolutionPreset.low);
expect(cameraController.enableAudio, false);
expect(cameraController.mediaSettings.fps, 15);
expect(cameraController.mediaSettings.videoBitrate, 200000);
expect(cameraController.mediaSettings.audioBitrate, 32000);
});

test('default constructor initializes media settings', () async {
final CameraController cameraController = CameraController(
const CameraDescription(
name: 'cam',
lensDirection: CameraLensDirection.back,
sensorOrientation: 90),
ResolutionPreset.max);
await cameraController.initialize();

expect(cameraController.resolutionPreset, ResolutionPreset.max);
expect(cameraController.enableAudio, true);
expect(cameraController.mediaSettings.fps, isNull);
expect(cameraController.mediaSettings.videoBitrate, isNull);
expect(cameraController.mediaSettings.audioBitrate, isNull);
});

test('can be disposed', () async {
final CameraController cameraController = CameraController(
const CameraDescription(
Expand Down Expand Up @@ -1429,15 +1469,20 @@ class MockCameraPlatform extends Mock
Future<List<CameraDescription>> availableCameras() =>
Future<List<CameraDescription>>.value(mockAvailableCameras);

@override
Future<int> createCameraWithSettings(
CameraDescription cameraDescription, MediaSettings? mediaSettings) =>
mockPlatformException
? throw PlatformException(code: 'foo', message: 'bar')
: Future<int>.value(mockInitializeCamera);

@override
Future<int> createCamera(
CameraDescription description,
ResolutionPreset? resolutionPreset, {
bool enableAudio = false,
}) =>
mockPlatformException
? throw PlatformException(code: 'foo', message: 'bar')
: Future<int>.value(mockInitializeCamera);
createCameraWithSettings(description, null);

@override
Stream<CameraInitializedEvent> onCameraInitialized(int cameraId) =>
Expand Down
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.4

* Prevents usage of unsupported concurrent `UseCase`s based on the capabiliites of the camera device.

## 0.6.3

* Shortens default interval that internal Java `InstanceManager` uses to release garbage collected weak references to
Expand Down
19 changes: 19 additions & 0 deletions packages/camera/camera_android_camerax/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ from your project's root directory.

## Limitations

### Concurrent preview display, video recording, image capture, and image streaming

The CameraX plugin only supports the concurrent camera use cases supported by Camerax; see
[their documentation][6] for more information. To avoid the usage of unsupported concurrent
use cases, the plugin behaves according to the following:

* If the preview is paused (via `pausePreview`), concurrent video recording and image capture
and/or image streaming (via `startVideoCapturing(cameraId, VideoCaptureOptions(streamCallback:...))`)
is supported.
* If the preview is not paused
* **and** the camera device is at least supported hardware [`LIMITED`][8], then concurrent
image capture and video recording is supported.
* **and** the camera device is at least supported hardware [`LEVEL_3`][7], then concurrent
video recording and image streaming is supported, but concurrent video recording, image
streaming, and image capture is not supported.

### 240p resolution configuration for video recording

240p resolution configuration for video recording is unsupported by CameraX,
Expand All @@ -45,6 +61,9 @@ For more information on contributing to this plugin, see [`CONTRIBUTING.md`](CON
[3]: https://docs.flutter.dev/packages-and-plugins/developing-packages#non-endorsed-federated-plugin
[4]: https://pub.dev/packages/camera_android
[5]: https://github.com/flutter/flutter/issues/new/choose
[6]: https://developer.android.com/media/camera/camerax/architecture#combine-use-cases
[7]: https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3
[8]: https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
[120462]: https://github.com/flutter/flutter/issues/120462
[125915]: https://github.com/flutter/flutter/issues/125915
[120715]: https://github.com/flutter/flutter/issues/120715
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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.

package io.flutter.plugins.camerax;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.camera2.interop.Camera2CameraInfo;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Camera2CameraInfoFlutterApi;

public class Camera2CameraInfoFlutterApiImpl extends Camera2CameraInfoFlutterApi {
private final InstanceManager instanceManager;

public Camera2CameraInfoFlutterApiImpl(
@Nullable BinaryMessenger binaryMessenger, @Nullable InstanceManager instanceManager) {
super(binaryMessenger);
this.instanceManager = instanceManager;
}

void create(@NonNull Camera2CameraInfo camera2CameraInfo, @Nullable Reply<Void> reply) {
if (!instanceManager.containsInstance(camera2CameraInfo)) {
create(instanceManager.addHostCreatedInstance(camera2CameraInfo), reply);
}
}
}
Loading