add plugin camera desktop
This commit is contained in:
172
plugins/camera_desktop/linux/camera.h
Normal file
172
plugins/camera_desktop/linux/camera.h
Normal file
@@ -0,0 +1,172 @@
|
||||
#ifndef CAMERA_H_
|
||||
#define CAMERA_H_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/gstappsink.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "camera_texture.h"
|
||||
#include "device_enumerator.h"
|
||||
#include "record_handler.h"
|
||||
|
||||
enum class CameraBackend {
|
||||
kV4L2, // Traditional /dev/video* + v4l2src
|
||||
kPipeWire, // Portal-authorized pipewiresrc
|
||||
};
|
||||
|
||||
enum class CameraState {
|
||||
kCreated,
|
||||
kInitializing,
|
||||
kRunning,
|
||||
kPaused,
|
||||
kDisposing,
|
||||
kDisposed,
|
||||
};
|
||||
|
||||
// Alias for the image-stream callback function pointer type.
|
||||
using ImageStreamCallback = void (*)(int32_t);
|
||||
|
||||
struct CameraConfig {
|
||||
std::string device_path;
|
||||
int resolution_preset;
|
||||
bool enable_audio;
|
||||
int target_width;
|
||||
int target_height;
|
||||
int target_fps;
|
||||
int target_bitrate;
|
||||
int audio_bitrate = 0;
|
||||
CameraBackend backend = CameraBackend::kV4L2;
|
||||
int pw_fd = -1; // PipeWire remote fd (only used when backend == kPipeWire)
|
||||
};
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
Camera(int camera_id,
|
||||
FlTextureRegistrar* texture_registrar,
|
||||
FlMethodChannel* method_channel,
|
||||
const CameraConfig& config);
|
||||
~Camera();
|
||||
|
||||
int camera_id() const { return camera_id_; }
|
||||
int64_t texture_id() const { return texture_id_; }
|
||||
CameraState state() const { return state_; }
|
||||
|
||||
// Allocates the texture and registers it. Must be called before Initialize.
|
||||
// Returns the texture_id on success, -1 on failure.
|
||||
int64_t RegisterTexture();
|
||||
|
||||
// Builds and starts the GStreamer pipeline. Responds to |method_call|
|
||||
// asynchronously once the first frame arrives or an error/timeout occurs.
|
||||
void Initialize(FlMethodCall* method_call);
|
||||
|
||||
// Captures a still image and saves it to a temporary JPEG file.
|
||||
// Responds to |method_call| with the file path or an error.
|
||||
void TakePicture(FlMethodCall* method_call);
|
||||
|
||||
// Pauses/resumes the live preview.
|
||||
void PausePreview();
|
||||
void ResumePreview();
|
||||
|
||||
// Starts video recording (silent, no audio).
|
||||
void StartVideoRecording(FlMethodCall* method_call);
|
||||
|
||||
// Stops video recording and returns the file path.
|
||||
void StopVideoRecording(FlMethodCall* method_call);
|
||||
|
||||
// Starts/stops sending raw frame data to Dart via method channel.
|
||||
void StartImageStream();
|
||||
void StopImageStream();
|
||||
|
||||
// FFI image stream access.
|
||||
void* GetImageStreamBuffer() const { return image_stream_buffer_; }
|
||||
void RegisterImageStreamCallback(void (*callback)(int32_t));
|
||||
void UnregisterImageStreamCallback();
|
||||
|
||||
// Toggles horizontal mirroring on the live video feed.
|
||||
void SetMirror(bool mirrored);
|
||||
|
||||
// Tears down the pipeline and releases all resources.
|
||||
void Dispose();
|
||||
|
||||
private:
|
||||
bool BuildPipeline(GError** error);
|
||||
void RespondToPendingInit(bool success, const char* error_message);
|
||||
|
||||
// GStreamer callbacks (static with user_data = Camera*).
|
||||
static GstFlowReturn OnNewSample(GstAppSink* sink, gpointer user_data);
|
||||
static gboolean OnBusMessage(GstBus* bus, GstMessage* msg,
|
||||
gpointer user_data);
|
||||
static gboolean OnInitTimeout(gpointer user_data);
|
||||
|
||||
// Sends an error event to Dart via the method channel.
|
||||
void SendError(const std::string& description);
|
||||
|
||||
int camera_id_;
|
||||
int64_t texture_id_;
|
||||
// state_ is written from the GStreamer streaming thread (OnNewSample) and
|
||||
// read/written from the main thread. Must be atomic. (C-2)
|
||||
std::atomic<CameraState> state_;
|
||||
CameraConfig config_;
|
||||
|
||||
FlTextureRegistrar* texture_registrar_; // Not owned.
|
||||
FlMethodChannel* method_channel_; // Not owned.
|
||||
CameraTexture* texture_; // Owned (GObject ref).
|
||||
|
||||
GstElement* pipeline_;
|
||||
GstElement* tee_; // For branching preview + recording.
|
||||
GstElement* appsink_;
|
||||
GstElement* videoflip_; // Named element in pipeline for mirror toggle.
|
||||
guint bus_watch_id_;
|
||||
guint init_timeout_id_;
|
||||
|
||||
std::unique_ptr<RecordHandler> record_handler_;
|
||||
|
||||
// Pending async initialization, stores the FlMethodCall until first frame.
|
||||
// Only accessed from the main thread (set in Initialize, cleared in
|
||||
// RespondToPendingInit which is always dispatched via g_idle_add to main).
|
||||
FlMethodCall* pending_init_call_;
|
||||
|
||||
// Written from the GStreamer streaming thread; read from main thread. (C-2)
|
||||
std::atomic<bool> first_frame_received_;
|
||||
|
||||
// Read from GStreamer streaming thread, written from main thread. (C-3)
|
||||
std::atomic<bool> preview_paused_;
|
||||
|
||||
std::atomic<bool> image_streaming_;
|
||||
|
||||
// FFI image stream shared buffer.
|
||||
// NOTE: The |ready| field acts as a release/acquire flag between the
|
||||
// GStreamer thread (writer) and Dart (reader). The native side MUST issue a
|
||||
// std::atomic_thread_fence(release) before writing ready=1, ensuring all
|
||||
// pixel writes are visible before Dart observes ready==1. (C-5)
|
||||
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[]; // flexible array member
|
||||
};
|
||||
|
||||
ImageStreamBuffer* image_stream_buffer_ = nullptr;
|
||||
size_t image_stream_buffer_size_ = 0;
|
||||
|
||||
// Written from the main thread, read from the GStreamer streaming thread.
|
||||
// Must be atomic to avoid data races and torn reads. (C-4)
|
||||
std::atomic<ImageStreamCallback> image_stream_callback_{nullptr};
|
||||
|
||||
int64_t image_stream_sequence_ = 0;
|
||||
|
||||
// Written from the GStreamer streaming thread on first frame, read from the
|
||||
// main thread in StartVideoRecording. Must be atomic. (H-2)
|
||||
std::atomic<int> actual_width_;
|
||||
std::atomic<int> actual_height_;
|
||||
};
|
||||
|
||||
#endif // CAMERA_H_
|
||||
Reference in New Issue
Block a user