Skip to content

Commit

Permalink
[camera_windows] Revert: Support image streams on Windows platform (#…
Browse files Browse the repository at this point in the history
…7951)

This reverts commit b31a279 (#7067)

The implementation had significant bugs:
- It had asynchronous results, but never responded to them, violating the engine API contract and preventing futures from completing.
- It `std::move`d pointers between classes, while still keeping them as ivars and global references and referring to them from long-lived handlers.
  • Loading branch information
stuartmorgan authored Nov 4, 2024
1 parent 2c8f226 commit b3a5e33
Show file tree
Hide file tree
Showing 15 changed files with 7 additions and 605 deletions.
5 changes: 5 additions & 0 deletions packages/camera/camera_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.2.6

* Reverts streaming frame support, as the implementation was incorrect and never
exposed.

## 0.2.5+1

* Updates C++ to Dart communication to use Pigeon.
Expand Down
58 changes: 0 additions & 58 deletions packages/camera/camera_windows/lib/camera_windows.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import 'package:flutter/widgets.dart';
import 'package:stream_transform/stream_transform.dart';

import 'src/messages.g.dart';
import 'type_conversion.dart';

/// An implementation of [CameraPlatform] for Windows.
class CameraWindows extends CameraPlatform {
Expand All @@ -33,12 +32,6 @@ class CameraWindows extends CameraPlatform {
final Map<int, HostCameraMessageHandler> hostCameraHandlers =
<int, HostCameraMessageHandler>{};

// The stream to receive frames from the native code.
StreamSubscription<dynamic>? _platformImageStreamSubscription;

// The stream for vending frames to platform interface clients.
StreamController<CameraImageData>? _frameStreamController;

/// The controller that broadcasts events coming from handleCameraMethodCall
///
/// It is a `broadcast` because multiple controllers will connect to
Expand Down Expand Up @@ -241,57 +234,6 @@ class CameraWindows extends CameraPlatform {
'resumeVideoRecording() is not supported due to Win32 API limitations.');
}

@override
Stream<CameraImageData> onStreamedFrameAvailable(int cameraId,
{CameraImageStreamOptions? options}) {
_installStreamController(
onListen: () => _onFrameStreamListen(cameraId),
onCancel: () => _onFrameStreamCancel(cameraId));
return _frameStreamController!.stream;
}

StreamController<CameraImageData> _installStreamController(
{void Function()? onListen, void Function()? onCancel}) {
_frameStreamController = StreamController<CameraImageData>(
onListen: onListen ?? () {},
onPause: _onFrameStreamPauseResume,
onResume: _onFrameStreamPauseResume,
onCancel: onCancel ?? () {},
);
return _frameStreamController!;
}

void _onFrameStreamListen(int cameraId) {
_startPlatformStream(cameraId);
}

Future<void> _startPlatformStream(int cameraId) async {
_startStreamListener();
await _hostApi.startImageStream(cameraId);
}

void _startStreamListener() {
const EventChannel cameraEventChannel =
EventChannel('plugins.flutter.io/camera_android/imageStream');
_platformImageStreamSubscription =
cameraEventChannel.receiveBroadcastStream().listen((dynamic imageData) {
_frameStreamController!
.add(cameraImageFromPlatformData(imageData as Map<dynamic, dynamic>));
});
}

FutureOr<void> _onFrameStreamCancel(int cameraId) async {
await _hostApi.stopImageStream(cameraId);
await _platformImageStreamSubscription?.cancel();
_platformImageStreamSubscription = null;
_frameStreamController = null;
}

void _onFrameStreamPauseResume() {
throw CameraException('InvalidCall',
'Pause and resume are not supported for onStreamedFrameAvailable');
}

@override
Future<void> setFlashMode(int cameraId, FlashMode mode) async {
// TODO(jokerttu): Implement flash mode support, https://github.com/flutter/flutter/issues/97537.
Expand Down
50 changes: 0 additions & 50 deletions packages/camera/camera_windows/lib/src/messages.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -362,56 +362,6 @@ class CameraApi {
}
}

/// Starts the image stream for the given camera.
Future<void> startImageStream(int cameraId) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.camera_windows.CameraApi.startImageStream$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[cameraId]) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

/// Stops the image stream for the given camera.
Future<void> stopImageStream(int cameraId) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.camera_windows.CameraApi.stopImageStream$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[cameraId]) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

/// Starts the preview stream for the given camera.
Future<void> pausePreview(int cameraId) async {
final String pigeonVar_channelName =
Expand Down
25 changes: 0 additions & 25 deletions packages/camera/camera_windows/lib/type_conversion.dart

This file was deleted.

8 changes: 0 additions & 8 deletions packages/camera/camera_windows/pigeons/messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ abstract class CameraApi {
@async
String stopVideoRecording(int cameraId);

/// Starts the image stream for the given camera.
@async
void startImageStream(int cameraId);

/// Stops the image stream for the given camera.
@async
void stopImageStream(int cameraId);

/// Starts the preview stream for the given camera.
@async
void pausePreview(int cameraId);
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_windows/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_windows
description: A Flutter plugin for getting information about and controlling the camera on Windows.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_windows
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.2.5+1
version: 0.2.6

environment:
sdk: ^3.3.0
Expand Down
2 changes: 0 additions & 2 deletions packages/camera/camera_windows/windows/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ enum class PendingResultType {
kTakePicture,
kStartRecord,
kStopRecord,
kStartStream,
kStopStream,
kPausePreview,
kResumePreview,
};
Expand Down
75 changes: 0 additions & 75 deletions packages/camera/camera_windows/windows/camera_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

#include "camera_plugin.h"

#include <flutter/event_channel.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/flutter_view.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
Expand Down Expand Up @@ -34,10 +32,6 @@ namespace {

const std::string kPictureCaptureExtension = "jpeg";
const std::string kVideoCaptureExtension = "mp4";
constexpr char kFrameEventChannelName[] =
"plugins.flutter.io/camera_android/imageStream";

std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink;

// Builds CaptureDeviceInfo object from given device holding device name and id.
std::unique_ptr<CaptureDeviceInfo> GetDeviceInfo(IMFActivate* device) {
Expand Down Expand Up @@ -122,34 +116,12 @@ std::optional<std::string> GetFilePathForVideo() {
}
} // namespace

// a setter for the event sink helpful for testing.
void CameraPlugin::SetEventSink(
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> events) {
event_sink = std::move(events);
}

// static
void CameraPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows* registrar) {
std::unique_ptr<CameraPlugin> plugin = std::make_unique<CameraPlugin>(
registrar->texture_registrar(), registrar->messenger());

auto frameEventchannel = std::make_unique<flutter::EventChannel<>>(
registrar->messenger(), kFrameEventChannelName,
&flutter::StandardMethodCodec::GetInstance());

auto event_channel_handler =
std::make_unique<flutter::StreamHandlerFunctions<>>(
[plugin = plugin.get()](auto arguments, auto events) {
plugin->SetEventSink(std::move(events));
return nullptr;
},
[](auto arguments) {
event_sink.reset();
return nullptr;
});
frameEventchannel->SetStreamHandler(std::move(event_channel_handler));

CameraApi::SetUp(registrar->messenger(), plugin.get());

registrar->AddPlugin(std::move(plugin));
Expand Down Expand Up @@ -369,53 +341,6 @@ void CameraPlugin::StopVideoRecording(
}
}

void CameraPlugin::StartImageStream(
int64_t camera_id,
std::function<void(std::optional<FlutterError> reply)> result) {
// check if request already exists
Camera* camera = GetCameraByCameraId(camera_id);
if (!camera) {
return result(FlutterError("camera_error", "Camera not created"));
}
if (camera->HasPendingResultByType(PendingResultType::kStartStream)) {
return result(
FlutterError("camera_error", "Pending start stream request exists"));
}

if (!event_sink) {
return result(FlutterError("camera_error",
"Unable to make event channel from windows"));
}

if (camera->AddPendingVoidResult(PendingResultType::kStartStream,
std::move(result))) {
CaptureController* cc = camera->GetCaptureController();
assert(cc);
cc->StartImageStream(std::move(event_sink));
}
}

void CameraPlugin::StopImageStream(
int64_t camera_id,
std::function<void(std::optional<FlutterError> reply)> result) {
// check if request already exists
Camera* camera = GetCameraByCameraId(camera_id);
if (!camera) {
return result(FlutterError("camera_error", "Camera not created"));
}
if (camera->HasPendingResultByType(PendingResultType::kStopStream)) {
return result(
FlutterError("camera_error", "Pending stop stream request exists"));
}

if (camera->AddPendingVoidResult(PendingResultType::kStopStream,
std::move(result))) {
CaptureController* cc = camera->GetCaptureController();
assert(cc);
cc->StopImageStream();
}
}

void CameraPlugin::TakePicture(
int64_t camera_id, std::function<void(ErrorOr<std::string> reply)> result) {
auto camera = GetCameraByCameraId(camera_id);
Expand Down
8 changes: 0 additions & 8 deletions packages/camera/camera_windows/windows/camera_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ class CameraPlugin : public flutter::Plugin,
public CameraApi,
public VideoCaptureDeviceEnumerator {
public:
void SetEventSink(
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> events);
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);

CameraPlugin(flutter::TextureRegistrar* texture_registrar,
Expand Down Expand Up @@ -70,12 +68,6 @@ class CameraPlugin : public flutter::Plugin,
void StopVideoRecording(
int64_t camera_id,
std::function<void(ErrorOr<std::string> reply)> result) override;
void StartImageStream(
int64_t camera_id,
std::function<void(std::optional<FlutterError> reply)> result) override;
void StopImageStream(
int64_t camera_id,
std::function<void(std::optional<FlutterError> reply)> result) override;
void TakePicture(
int64_t camera_id,
std::function<void(ErrorOr<std::string> reply)> result) override;
Expand Down
39 changes: 0 additions & 39 deletions packages/camera/camera_windows/windows/capture_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
#include "capture_controller.h"

#include <comdef.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/standard_method_codec.h>
#include <wincodec.h>
#include <wrl/client.h>

#include <cassert>
#include <chrono>
#include <iostream>

#include "com_heap_ptr.h"
#include "photo_handler.h"
Expand Down Expand Up @@ -553,16 +550,6 @@ void CaptureControllerImpl::StopRecord() {
"Failed to stop video recording");
}
}
void CaptureControllerImpl::StartImageStream(
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> sink) {
assert(capture_controller_listener_);
image_stream_sink_ = std::move(sink);
}

void CaptureControllerImpl::StopImageStream() {
assert(capture_controller_listener_);
image_stream_sink_.reset();
}

// Starts capturing preview frames using preview handler
// After first frame is captured, OnPreviewStarted is called
Expand Down Expand Up @@ -856,32 +843,6 @@ bool CaptureControllerImpl::UpdateBuffer(uint8_t* buffer,
if (!texture_handler_) {
return false;
}
if (image_stream_sink_) {
// Convert the buffer data to a std::vector<uint8_t>.
std::vector<uint8_t> buffer_data(buffer, buffer + data_length);

// Ensure preview_frame_height_ and preview_frame_width_ are of supported
// types.
int preview_frame_height = static_cast<int>(preview_frame_height_);
int preview_frame_width = static_cast<int>(preview_frame_width_);

// Create a map to hold the buffer data and data length.
flutter::EncodableMap data_map;
data_map[flutter::EncodableValue("data")] =
flutter::EncodableValue(buffer_data);
data_map[flutter::EncodableValue("height")] =
flutter::EncodableValue(preview_frame_height);
data_map[flutter::EncodableValue("width")] =
flutter::EncodableValue(preview_frame_width);
data_map[flutter::EncodableValue("length")] =
flutter::EncodableValue(static_cast<int>(data_length));

// Wrap the map in a flutter::EncodableValue.
flutter::EncodableValue encoded_value(data_map);

// Send the encoded value through the image_stream_sink_.
image_stream_sink_->Success(encoded_value);
}
return texture_handler_->UpdateBuffer(buffer, data_length);
}

Expand Down
Loading

0 comments on commit b3a5e33

Please sign in to comment.