Skip to content
Open
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
62 changes: 59 additions & 3 deletions packages/camera/elinux/camera_elinux_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ class CameraPlugin : public flutter::Plugin {
void HandleDisposeCall(
const flutter::EncodableValue* message,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void HandleStartVideoRecording(
const flutter::EncodableValue* message,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void HandleStopVideoRecording(
const flutter::EncodableValue* message,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);

flutter::PluginRegistrar* plugin_registrar_;
flutter::TextureRegistrar* texture_registrar_;
Expand All @@ -126,6 +132,7 @@ class CameraPlugin : public flutter::Plugin {
nullptr;
std::unique_ptr<MethodChannelCamera> method_channel_camera_;
std::unique_ptr<MethodChannelDevice> method_channel_device_;
int video_recording_count_ = 0; // <-- ADD THIS LINE HERE
};

// static
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -314,6 +321,55 @@ void CameraPlugin::HandleTakePictureCall(
});
}

void CameraPlugin::HandleStartVideoRecording(
const flutter::EncodableValue* message,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> 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<flutter::MethodResult<flutter::EncodableValue>> 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<flutter::MethodResult<flutter::EncodableValue>> result) {
Expand Down
60 changes: 55 additions & 5 deletions packages/camera/elinux/gst_camera.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<GstCamera*>(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;
}
Expand Down Expand Up @@ -336,4 +386,4 @@ GstBusSyncReply GstCamera::HandleGstMessage(GstBus* bus,
gst_message_unref(message);

return GST_BUS_DROP;
}
}
12 changes: 11 additions & 1 deletion packages/camera/elinux/gst_camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_; };
Expand All @@ -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();
Expand Down