diff --git a/packages/camera/elinux/camera_elinux_plugin.cc b/packages/camera/elinux/camera_elinux_plugin.cc index c9c1bff..24a3285 100644 --- a/packages/camera/elinux/camera_elinux_plugin.cc +++ b/packages/camera/elinux/camera_elinux_plugin.cc @@ -113,6 +113,12 @@ class CameraPlugin : public flutter::Plugin { void HandleDisposeCall( const flutter::EncodableValue* message, std::unique_ptr> result); + void HandleStartVideoRecording( + const flutter::EncodableValue* message, + std::unique_ptr> result); + void HandleStopVideoRecording( + const flutter::EncodableValue* message, + std::unique_ptr> result); flutter::PluginRegistrar* plugin_registrar_; flutter::TextureRegistrar* texture_registrar_; @@ -126,6 +132,7 @@ class CameraPlugin : public flutter::Plugin { nullptr; std::unique_ptr method_channel_camera_; std::unique_ptr method_channel_device_; + int video_recording_count_ = 0; // <-- ADD THIS LINE HERE }; // static @@ -159,11 +166,11 @@ void CameraPlugin::HandleMethodCall( } else if (!method_name.compare(kCameraChannelApiTakePicture)) { HandleTakePictureCall(method_call.arguments(), std::move(result)); } else if (!method_name.compare(kCameraChannelApiPrepareForVideoRecording)) { - result->NotImplemented(); + HandleTakePictureCall(method_call.arguments(), std::move(result)); } else if (!method_name.compare(kCameraChannelApiStartVideoRecording)) { - result->NotImplemented(); + HandleStartVideoRecording(method_call.arguments(), std::move(result)); } else if (!method_name.compare(kCameraChannelApiStopVideoRecording)) { - result->NotImplemented(); + HandleStopVideoRecording(method_call.arguments(), std::move(result)); } else if (!method_name.compare(kCameraChannelApiPauseVideoRecording)) { result->NotImplemented(); } else if (!method_name.compare(kCameraChannelApiResumeVideoRecording)) { @@ -314,6 +321,55 @@ void CameraPlugin::HandleTakePictureCall( }); } +void CameraPlugin::HandleStartVideoRecording( + const flutter::EncodableValue* message, + std::unique_ptr> result) { + if (!camera_) { + result->Error("Not found an active camera", + "Check for creating a camera device"); + return; + } + + if (camera_->IsRecording()) { + result->Error("Already recording", "Stop current recording first"); + return; + } + + // Generate filename + std::string filename = + g_strdup_printf("video_%04u.mp4", video_recording_count_++); + + camera_->StartVideoRecording( + filename, + [p_result = result.release()](const std::string& video_file_path) { + if (!video_file_path.empty()) { + flutter::EncodableValue value(video_file_path); + p_result->Success(value); + } else { + p_result->Error("Failed to record", "Video recording failed"); + } + delete p_result; + }); +} + +void CameraPlugin::HandleStopVideoRecording( + const flutter::EncodableValue* message, + std::unique_ptr> result) { + if (!camera_) { + result->Error("Not found an active camera", + "Check for creating a camera device"); + return; + } + + if (!camera_->IsRecording()) { + result->Error("Not recording", "No active video recording"); + return; + } + + camera_->StopVideoRecording(); + // Note: result will be sent via the callback when video-done message arrives +} + void CameraPlugin::HandleGetMinExposureOffsetCall( const flutter::EncodableValue* message, std::unique_ptr> result) { diff --git a/packages/camera/elinux/gst_camera.cc b/packages/camera/elinux/gst_camera.cc index d10f9e9..e329af0 100644 --- a/packages/camera/elinux/gst_camera.cc +++ b/packages/camera/elinux/gst_camera.cc @@ -90,6 +90,46 @@ void GstCamera::TakePicture(OnNotifyCaptured on_notify_captured) { g_signal_emit_by_name(gst_.camerabin, "start-capture", NULL); } +void GstCamera::StartVideoRecording(const std::string& file_path, + OnNotifyCaptured on_notify_captured) { + if (!gst_.camerabin) { + std::cerr << "Failed to start video recording: camerabin not initialized" + << std::endl; + return; + } + + if (is_recording_) { + std::cerr << "Video recording already in progress" << std::endl; + return; + } + + on_video_captured_ = on_notify_captured; + video_file_path_ = file_path; + + // Set mode to video (2 = video mode) + g_object_set(gst_.camerabin, "mode", 2, NULL); + g_object_set(gst_.camerabin, "location", file_path.c_str(), NULL); + + // Start recording + g_signal_emit_by_name(gst_.camerabin, "start-capture", NULL); + is_recording_ = true; + + std::cout << "Started video recording to: " << file_path << std::endl; +} + +void GstCamera::StopVideoRecording() { + if (!gst_.camerabin || !is_recording_) { + std::cerr << "No active video recording to stop" << std::endl; + return; + } + + // Stop recording + g_signal_emit_by_name(gst_.camerabin, "stop-capture", NULL); + is_recording_ = false; + + std::cout << "Stopped video recording" << std::endl; +} + bool GstCamera::SetZoomLevel(float zoom) { if (zoom_level_ == zoom) { return true; @@ -155,8 +195,7 @@ bool GstCamera::CreatePipeline() { std::cerr << "Failed to create a bus" << std::endl; return false; } - gst_bus_set_sync_handler(gst_.bus, HandleGstMessage, this, - NULL); + gst_bus_set_sync_handler(gst_.bus, HandleGstMessage, this, NULL); // Sets properties to fakesink to get the callback of a decoded frame. g_object_set(G_OBJECT(gst_.video_sink), "sync", TRUE, "qos", FALSE, NULL); @@ -291,19 +330,30 @@ void GstCamera::HandoffHandler(GstElement* fakesink, GstBuffer* buf, } // static -GstBusSyncReply GstCamera::HandleGstMessage(GstBus* bus, - GstMessage* message, +GstBusSyncReply GstCamera::HandleGstMessage(GstBus* bus, GstMessage* message, gpointer user_data) { switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ELEMENT: { auto const* st = gst_message_get_structure(message); if (st) { auto* self = reinterpret_cast(user_data); + + // Handle image capture completion if (gst_structure_has_name(st, "image-done") && self->on_notify_captured_) { auto const* filename = gst_structure_get_string(st, "filename"); self->on_notify_captured_(filename); } + + // Handle video recording completion + if (gst_structure_has_name(st, "video-done") && + self->on_video_captured_) { + auto const* filename = gst_structure_get_string(st, "filename"); + self->on_video_captured_(filename); + self->is_recording_ = false; + // Switch back to image mode (1) + g_object_set(self->gst_.camerabin, "mode", 1, NULL); + } } break; } @@ -336,4 +386,4 @@ GstBusSyncReply GstCamera::HandleGstMessage(GstBus* bus, gst_message_unref(message); return GST_BUS_DROP; -} +} \ No newline at end of file diff --git a/packages/camera/elinux/gst_camera.h b/packages/camera/elinux/gst_camera.h index a27a5fb..7514d62 100644 --- a/packages/camera/elinux/gst_camera.h +++ b/packages/camera/elinux/gst_camera.h @@ -30,6 +30,16 @@ class GstCamera { bool Stop(); void TakePicture(OnNotifyCaptured on_notify_captured); + // Add these public methods in GstCamera class: + void StartVideoRecording(const std::string& file_path, + OnNotifyCaptured on_notify_captured); + void StopVideoRecording(); + bool IsRecording() const { return is_recording_; } + + // Add these private members: + bool is_recording_ = false; + std::string video_file_path_; + OnNotifyCaptured on_video_captured_ = nullptr; bool SetZoomLevel(float zoom); float GetMaxZoomLevel() const { return max_zoom_level_; }; @@ -53,7 +63,7 @@ class GstCamera { static void HandoffHandler(GstElement* fakesink, GstBuffer* buf, GstPad* new_pad, gpointer user_data); static GstBusSyncReply HandleGstMessage(GstBus* bus, GstMessage* message, - gpointer user_data); + gpointer user_data); bool CreatePipeline(); void DestroyPipeline();