add plugin camera desktop
This commit is contained in:
220
plugins/camera_desktop/windows/camera.h
Normal file
220
plugins/camera_desktop/windows/camera.h
Normal file
@@ -0,0 +1,220 @@
|
||||
#pragma once
|
||||
|
||||
#include <flutter/encodable_value.h>
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/method_result.h>
|
||||
#include <flutter/texture_registrar.h>
|
||||
#include <d3d11.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfcaptureengine.h>
|
||||
#include <mfidl.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "camera_texture.h"
|
||||
#include "record_handler.h"
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
enum class CameraState {
|
||||
kCreated,
|
||||
kInitializing,
|
||||
kRunning,
|
||||
kPaused,
|
||||
kDisposing,
|
||||
kDisposed,
|
||||
};
|
||||
|
||||
struct CameraConfig {
|
||||
std::wstring symbolic_link;
|
||||
int resolution_preset = 4; // 0=low … 4=max
|
||||
bool enable_audio = false;
|
||||
int target_fps = 30;
|
||||
int target_bitrate = 0; // <=0 means use dynamic default ladder.
|
||||
int audio_bitrate = 0;
|
||||
};
|
||||
|
||||
class Camera : public std::enable_shared_from_this<Camera> {
|
||||
public:
|
||||
using PlatformTaskPoster =
|
||||
std::function<void(std::function<void()>, const char* tag)>;
|
||||
|
||||
Camera(int camera_id, flutter::TextureRegistrar* texture_registrar,
|
||||
flutter::MethodChannel<flutter::EncodableValue>* channel,
|
||||
CameraConfig config,
|
||||
PlatformTaskPoster platform_task_poster);
|
||||
~Camera();
|
||||
|
||||
int64_t RegisterTexture();
|
||||
|
||||
void Initialize(
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void TakePicture(
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void StartVideoRecording(
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
void StopVideoRecording(
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void StartImageStream();
|
||||
void StopImageStream();
|
||||
|
||||
// FFI image stream access.
|
||||
void* GetImageStreamBuffer();
|
||||
void RegisterImageStreamCallback(void (*callback)(int32_t));
|
||||
void UnregisterImageStreamCallback();
|
||||
|
||||
void PausePreview();
|
||||
void ResumePreview();
|
||||
void DisposeAsync(std::function<void()> on_done);
|
||||
void Dispose();
|
||||
bool IsDisposedOrDisposing() const;
|
||||
|
||||
// Called from COM callbacks, must be public.
|
||||
void OnEngineEvent(IMFMediaEvent* event);
|
||||
void OnPreviewSample(IMFSample* sample);
|
||||
|
||||
private:
|
||||
uint32_t MaxPreviewHeightForPreset() const;
|
||||
uint32_t MaxRecordHeightForPreset() const;
|
||||
int ComputeDefaultBitrate(int width, int height, int fps) const;
|
||||
|
||||
HRESULT CreateCaptureEngine();
|
||||
HRESULT FindBaseMediaTypes();
|
||||
HRESULT StartPreviewInternal();
|
||||
|
||||
void CompleteInit(bool success, const std::string& error,
|
||||
int width = 0, int height = 0);
|
||||
void FailAllPendingResults(const std::string& error);
|
||||
|
||||
void DisposeInternal();
|
||||
void SendError(const std::string& description);
|
||||
int InitElapsedMs() const;
|
||||
|
||||
static void FlipHorizontal(uint8_t* data, int width, int height);
|
||||
static void SwapRBChannels(uint8_t* data, int width, int height);
|
||||
|
||||
void PostImageStreamFrame(const uint8_t* data, int width, int height);
|
||||
void ImageStreamLoop();
|
||||
|
||||
// ── Identity ────────────────────────────────────────────────────────────
|
||||
int camera_id_;
|
||||
int64_t texture_id_ = -1;
|
||||
CameraConfig config_;
|
||||
|
||||
flutter::TextureRegistrar* texture_registrar_;
|
||||
flutter::MethodChannel<flutter::EncodableValue>* channel_;
|
||||
PlatformTaskPoster platform_task_poster_;
|
||||
std::shared_ptr<CameraTexture> texture_;
|
||||
// Guards texture_ against concurrent teardown: preview samples arrive on an
|
||||
// MF callback thread and touch texture_, while DisposeInternal frees it on
|
||||
// the dispose thread. See CRASH.md.
|
||||
std::mutex texture_mutex_;
|
||||
|
||||
// ── Capture engine + D3D11 ─────────────────────────────────────────────
|
||||
ComPtr<IMFCaptureEngine> capture_engine_;
|
||||
ComPtr<IMFCapturePreviewSink> preview_sink_;
|
||||
ComPtr<ID3D11Device> dx11_device_;
|
||||
ComPtr<IMFDXGIDeviceManager> dxgi_device_manager_;
|
||||
UINT dx_device_reset_token_ = 0;
|
||||
|
||||
// Negotiated media types (set in FindBaseMediaTypes before preview starts).
|
||||
ComPtr<IMFMediaType> base_preview_media_type_;
|
||||
ComPtr<IMFMediaType> base_capture_media_type_;
|
||||
int preview_width_ = 0;
|
||||
int preview_height_ = 0;
|
||||
int record_width_ = 0;
|
||||
int record_height_ = 0;
|
||||
int record_fps_ = 0;
|
||||
|
||||
// ── Recording ──────────────────────────────────────────────────────────
|
||||
std::unique_ptr<RecordHandler> record_handler_;
|
||||
std::wstring current_record_path_;
|
||||
std::atomic<bool> is_recording_{false};
|
||||
int active_record_bitrate_ = 0;
|
||||
|
||||
// ── Preview / frame state ───────────────────────────────────────────────
|
||||
std::atomic<bool> first_frame_received_{false};
|
||||
std::atomic<bool> preview_paused_{false};
|
||||
std::atomic<bool> image_streaming_{false};
|
||||
|
||||
// ── Latest frame for photo capture (natural BGRA) ─────────────────────
|
||||
std::vector<uint8_t> latest_frame_;
|
||||
std::mutex latest_frame_mutex_;
|
||||
|
||||
// ── Per-frame working buffer ────────────────────────────────────────────
|
||||
std::vector<uint8_t> packed_frame_;
|
||||
|
||||
// ── Camera state ────────────────────────────────────────────────────────
|
||||
CameraState state_ = CameraState::kCreated;
|
||||
mutable std::mutex state_mutex_;
|
||||
|
||||
// ── Pending async MethodResults ─────────────────────────────────────────
|
||||
mutable std::mutex pending_mutex_;
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
|
||||
pending_init_;
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
|
||||
pending_start_record_;
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
|
||||
pending_stop_record_;
|
||||
|
||||
// ── Init timing (diagnostics) ───────────────────────────────────────────
|
||||
std::chrono::steady_clock::time_point create_start_{};
|
||||
|
||||
// ── Initialisation timeout ──────────────────────────────────────────────
|
||||
std::thread init_timeout_thread_;
|
||||
std::mutex init_timeout_cancel_mutex_;
|
||||
std::condition_variable init_timeout_cancel_cv_;
|
||||
bool init_timeout_cancelled_ = false;
|
||||
|
||||
// ── Image stream delivery ───────────────────────────────────────────────
|
||||
struct ImageStreamBuffer {
|
||||
int64_t sequence;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t bytes_per_row;
|
||||
int32_t format; // 0=BGRA, 1=RGBA
|
||||
int32_t ready; // 1=Dart may read, 0=native writing
|
||||
int32_t _pad;
|
||||
uint8_t pixels[1];
|
||||
};
|
||||
|
||||
ImageStreamBuffer* image_stream_buffer_ = nullptr;
|
||||
size_t image_stream_buffer_size_ = 0;
|
||||
void (*image_stream_callback_)(int32_t) = nullptr;
|
||||
int64_t image_stream_sequence_ = 0;
|
||||
std::mutex image_stream_ffi_mutex_;
|
||||
|
||||
struct ImageStreamSlot {
|
||||
std::vector<uint8_t> data;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
bool dirty = false;
|
||||
};
|
||||
std::mutex image_stream_mutex_;
|
||||
std::condition_variable image_stream_cv_;
|
||||
ImageStreamSlot image_stream_slot_;
|
||||
std::thread image_stream_thread_;
|
||||
std::atomic<bool> image_stream_running_{false};
|
||||
std::thread image_stream_join_thread_;
|
||||
std::mutex image_stream_thread_mutex_;
|
||||
|
||||
// ── Async dispose ───────────────────────────────────────────────────────
|
||||
std::thread dispose_thread_;
|
||||
std::mutex dispose_mutex_;
|
||||
std::vector<std::function<void()>> dispose_callbacks_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user