Parcourir la source

Implement flash

Fixes #5
Benjamin Schaaf il y a 4 ans
Parent
commit
24835cdc25

+ 2 - 0
config/pine64,pinephone-1.0.ini

@@ -23,6 +23,7 @@ cropfactor=10.81
 fnumber=3.0
 iso-min=100
 iso-max=64000
+flash-path=/sys/class/leds/white:flash
 
 [front]
 driver=gc2145
@@ -40,3 +41,4 @@ mirrored=true
 focallength=2.6
 cropfactor=12.7
 fnumber=2.8
+flash-display=true

+ 2 - 0
config/pine64,pinephone-1.1.ini

@@ -23,6 +23,7 @@ cropfactor=10.81
 fnumber=3.0
 iso-min=100
 iso-max=64000
+flash-path=/sys/class/leds/white:flash
 
 [front]
 driver=gc2145
@@ -40,3 +41,4 @@ mirrored=true
 focallength=2.6
 cropfactor=12.7
 fnumber=2.8
+flash-display=true

+ 2 - 0
config/pine64,pinephone-1.2.ini

@@ -24,6 +24,7 @@ cropfactor=10.81
 fnumber=3.0
 iso-min=100
 iso-max=64000
+flash-path=/sys/class/leds/white:flash
 
 [front]
 driver=gc2145
@@ -41,3 +42,4 @@ mirrored=true
 focallength=2.6
 cropfactor=12.7
 fnumber=2.8
+flash-display=true

+ 4 - 0
data/camera.css

@@ -11,3 +11,7 @@
     opacity: 0.9;
     background-color: rgba(0, 0, 0, 0.2);
 }
+
+.flash {
+    background-color: #ffffff;
+}

+ 9 - 0
data/camera.ui

@@ -59,6 +59,15 @@
                             </style>
                           </object>
                         </child>
+                        <child>
+                          <object class="GtkButton" id="flash-controls-button">
+                            <property name="valign">start</property>
+                            <property name="icon-name">flash-enabled-symbolic</property>
+                            <style>
+                              <class name="circular"/>
+                            </style>
+                          </object>
+                        </child>
                       </object>
                     </child>
                     <child>

+ 32 - 0
data/flash-disabled-symbolic.svg

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   id="svg8"
+   version="1.1"
+   height="32"
+   width="32"
+   viewBox="0 0 8.4666665 8.466667">
+  <defs
+     id="defs2" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <path
+     style="fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="M 3 0 L 1.5 1.5 L 30.5 30.5 L 32 29 L 20.75 17.75 L 24 13 L 16 13 L 15.234375 12.234375 L 19 0 L 13 0 L 10.726562 7.7265625 L 3 0 z M 9.3632812 12.363281 L 8 17 L 14 17 L 9.3632812 12.363281 z M 14.789062 17.789062 L 11 32 L 18.3125 21.3125 L 14.789062 17.789062 z "
+     transform="scale(0.26458334)"
+     id="path1370" />
+</svg>

+ 31 - 0
data/flash-enabled-symbolic.svg

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   id="svg8"
+   version="1.1"
+   height="32"
+   width="32"
+   viewBox="0 0 8.4666665 8.466667">
+  <defs
+     id="defs2" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <path
+     style="fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="M 3.4395834,0 2.1166667,4.4979167 H 3.96875 L 2.9104167,8.4666669 6.3500001,3.4395834 H 3.96875 L 5.0270834,0 Z"
+     id="path1370" />
+</svg>

+ 4 - 2
data/org.postmarketos.Megapixels.gresource.xml

@@ -4,11 +4,13 @@
     <file preprocess="xml-stripblanks">camera.ui</file>
     <file preprocess="xml-stripblanks">controls-popover.ui</file>
     <file>camera.css</file>
-    <file preprocess="xml-stripblanks">switch-camera-symbolic.svg</file>
-    <file preprocess="xml-stripblanks">shutter-button-symbolic.svg</file>
+    <file preprocess="xml-stripblanks">flash-disabled-symbolic.svg</file>
+    <file preprocess="xml-stripblanks">flash-enabled-symbolic.svg</file>
     <file preprocess="xml-stripblanks">folder-symbolic.svg</file>
     <file preprocess="xml-stripblanks">settings-symbolic.svg</file>
+    <file preprocess="xml-stripblanks">shutter-button-symbolic.svg</file>
     <file preprocess="xml-stripblanks">shutter-symbolic.svg</file>
+    <file preprocess="xml-stripblanks">switch-camera-symbolic.svg</file>
     <file>blit.vert</file>
     <file>blit.frag</file>
     <file>solid.vert</file>

+ 8 - 7
meson.build

@@ -34,18 +34,19 @@ if get_option('tiffcfapattern')
 endif
 
 executable('megapixels',
-  'src/main.c',
-  'src/ini.c',
-  'src/gles2_debayer.c',
-  'src/gl_util.c',
   'src/camera.c',
-  'src/device.c',
-  'src/pipeline.c',
   'src/camera_config.c',
+  'src/device.c',
+  'src/flash.c',
+  'src/gl_util.c',
+  'src/gles2_debayer.c',
+  'src/ini.c',
   'src/io_pipeline.c',
+  'src/main.c',
+  'src/matrix.c',
+  'src/pipeline.c',
   'src/process_pipeline.c',
   'src/zbar_pipeline.c',
-  'src/matrix.c',
   resources,
   include_directories: 'src/',
   dependencies: [gtkdep, libm, tiff, zbar, threads, epoxy],

+ 4 - 0
src/camera_config.c

@@ -206,6 +206,10 @@ config_ini_handler(void *user, const char *section, const char *name,
 			cc->iso_min = strtod(value, NULL);
 		} else if (strcmp(name, "iso-max") == 0) {
 			cc->iso_max = strtod(value, NULL);
+		} else if (strcmp(name, "flash-path") == 0) {
+			strcpy(cc->flash_path, value);
+		} else if (strcmp(name, "flash-display") == 0) {
+			cc->flash_display = strcmp(value, "true") == 0;
 		} else {
 			g_printerr("Unknown key '%s' in [%s]\n", name, section);
 			exit(1);

+ 3 - 0
src/camera_config.h

@@ -41,6 +41,9 @@ struct mp_camera_config {
 	double fnumber;
 	int iso_min;
 	int iso_max;
+
+	char flash_path[260];
+	bool flash_display;
 };
 
 bool mp_load_config();

+ 141 - 0
src/flash.c

@@ -0,0 +1,141 @@
+#include "flash.h"
+
+#include "gtk/gtk.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+typedef enum {
+	FLASH_TYPE_LED,
+	FLASH_TYPE_DISPLAY,
+} FlashType;
+
+typedef struct {
+	char path[260];
+	int fd;
+} MPLEDFlash;
+
+typedef struct {
+	GtkWidget *window;
+} MPDisplayFlash;
+
+struct _MPFlash {
+	FlashType type;
+
+	union {
+		MPLEDFlash led;
+		MPDisplayFlash display;
+	};
+};
+
+MPFlash *
+mp_led_flash_from_path(const char * path)
+{
+	MPFlash *flash = malloc(sizeof(MPFlash));
+	flash->type = FLASH_TYPE_LED;
+
+	strncpy(flash->led.path, path, 259);
+
+	char mpath[275];
+	snprintf(mpath, 275, "%s/flash_strobe", path);
+	flash->led.fd = open(mpath, O_WRONLY);
+	if (flash->led.fd == -1) {
+		g_printerr("Failed to open %s\n", mpath);
+		free(flash);
+		return NULL;
+	}
+
+	return flash;
+}
+
+static bool create_display(MPFlash *flash)
+{
+	// Create a full screen full white window as a flash
+	GtkWidget *window = gtk_window_new();
+	// gtk_window_set_accept_focus(GTK_WINDOW(flash->display.window), FALSE);
+	gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
+	gtk_window_fullscreen(GTK_WINDOW(window));
+
+	GtkStyleContext *context;
+	context = gtk_widget_get_style_context(window);
+	gtk_style_context_add_class(context, "flash");
+
+	flash->display.window = window;
+
+	return false;
+}
+
+MPFlash *
+mp_create_display_flash()
+{
+	MPFlash *flash = malloc(sizeof(MPFlash));
+	flash->type = FLASH_TYPE_DISPLAY;
+	flash->display.window = NULL;
+
+	// All GTK functions must be called on the main thread
+	g_main_context_invoke(NULL, (GSourceFunc)create_display, flash);
+
+	return flash;
+}
+
+static bool flash_free(MPFlash *flash)
+{
+	gtk_window_destroy(GTK_WINDOW(flash->display.window));
+	free(flash);
+	return false;
+}
+
+void
+mp_flash_free(MPFlash *flash)
+{
+	switch (flash->type) {
+		case FLASH_TYPE_LED:
+			close(flash->led.fd);
+			free(flash);
+			break;
+		case FLASH_TYPE_DISPLAY:
+			g_main_context_invoke(NULL, (GSourceFunc)flash_free, flash);
+			break;
+	}
+}
+
+static bool show_flash_window(MPFlash *flash)
+{
+	gtk_widget_show(flash->display.window);
+	return false;
+}
+
+void
+mp_flash_enable(MPFlash *flash)
+{
+	switch (flash->type) {
+		case FLASH_TYPE_LED:
+			lseek(flash->led.fd, 0, SEEK_SET);
+			dprintf(flash->led.fd, "1\n");
+			break;
+		case FLASH_TYPE_DISPLAY:
+			g_main_context_invoke(NULL, (GSourceFunc)show_flash_window, flash);
+			break;
+	}
+}
+
+static bool hide_flash_window(MPFlash *flash)
+{
+	gtk_widget_hide(flash->display.window);
+	return false;
+}
+
+void
+mp_flash_disable(MPFlash *flash)
+{
+	switch (flash->type) {
+		case FLASH_TYPE_LED:
+			// Flash gets reset automatically
+			break;
+		case FLASH_TYPE_DISPLAY:
+			g_main_context_invoke(NULL, (GSourceFunc)hide_flash_window, flash);
+			break;
+	}
+}
+

+ 8 - 0
src/flash.h

@@ -0,0 +1,8 @@
+typedef struct _MPFlash MPFlash;
+
+MPFlash *mp_led_flash_from_path(const char *path);
+MPFlash *mp_create_display_flash();
+void mp_flash_free(MPFlash *flash);
+
+void mp_flash_enable(MPFlash *flash);
+void mp_flash_disable(MPFlash *flash);

+ 36 - 8
src/io_pipeline.c

@@ -1,16 +1,17 @@
 #include "io_pipeline.h"
 
-#include "device.h"
 #include "camera.h"
+#include "device.h"
+#include "flash.h"
 #include "pipeline.h"
 #include "process_pipeline.h"
-#include <string.h>
-#include <glib.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <errno.h>
 #include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glib.h>
 #include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
 
 struct media_link_info {
 	unsigned int source_entity_id;
@@ -29,6 +30,8 @@ struct camera_info {
 
 	MPCamera *camera;
 
+	MPFlash *flash;
+
 	int gain_ctrl;
 	int gain_max;
 
@@ -90,6 +93,8 @@ struct control_state {
 static struct control_state desired_controls = {};
 static struct control_state current_controls = {};
 
+static bool flash_enabled = false;
+
 static bool want_focus = false;
 
 static MPPipeline *pipeline;
@@ -216,6 +221,15 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config)
 			info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN;
 			info->gain_max = control.max;
 		}
+
+		// Setup flash
+		if (config->flash_path[0]) {
+			info->flash = mp_led_flash_from_path(config->flash_path);
+		} else if (config->flash_display) {
+			info->flash = mp_create_display_flash();
+		} else {
+			info->flash = NULL;
+		}
 	}
 }
 
@@ -340,6 +354,11 @@ capture(MPPipeline *pipeline, const void *data)
 
 	mp_camera_start_capture(info->camera);
 
+	// Enable flash
+	if (info->flash && flash_enabled) {
+		mp_flash_enable(info->flash);
+	}
+
 	update_process_pipeline();
 
 	mp_process_pipeline_capture();
@@ -490,6 +509,11 @@ on_frame(MPBuffer buffer, void * _data)
 
 			mp_camera_start_capture(info->camera);
 
+			// Disable flash
+			if (info->flash) {
+				mp_flash_disable(info->flash);
+			}
+
 			update_process_pipeline();
 		}
 	}
@@ -571,8 +595,12 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
 		desired_controls.exposure = state->exposure;
 
 		has_changed =
-			has_changed || memcmp(&previous_desired, &desired_controls,
-					      sizeof(struct control_state)) != 0;
+			has_changed
+			|| memcmp(&previous_desired, &desired_controls,
+				  sizeof(struct control_state)) != 0
+			|| flash_enabled != state->flash_enabled;
+
+		flash_enabled = state->flash_enabled;
 	}
 
 	assert(has_changed);

+ 1 - 0
src/io_pipeline.h

@@ -19,6 +19,7 @@ struct mp_io_pipeline_state {
 	int exposure;
 
 	bool save_dng;
+	bool flash_enabled;
 };
 
 void mp_io_pipeline_start();

+ 15 - 0
src/main.c

@@ -52,6 +52,8 @@ static int exposure;
 static bool has_auto_focus_continuous;
 static bool has_auto_focus_start;
 
+static bool flash_enabled = true;
+
 static bool setting_save_dng;
 
 static MPProcessPipelineBuffer *current_preview_buffer = NULL;
@@ -107,6 +109,7 @@ update_io_pipeline()
 		.exposure_is_manual = exposure_is_manual,
 		.exposure = exposure,
 		.save_dng = setting_save_dng,
+		.flash_enabled = flash_enabled,
 	};
 	mp_io_pipeline_update_state(&io_state);
 }
@@ -758,6 +761,16 @@ open_shutter_controls(GtkWidget *button, gpointer user_data)
 	open_controls(button, "Shutter", 1.0, 360.0, exposure, !exposure_is_manual, set_shutter, set_shutter_auto);
 }
 
+static void
+flash_button_clicked(GtkWidget *button, gpointer user_data)
+{
+	flash_enabled = !flash_enabled;
+	update_io_pipeline();
+
+	const char * icon_name = flash_enabled ? "flash-enabled-symbolic" : "flash-disabled-symbolic";
+	gtk_button_set_icon_name(GTK_BUTTON(button), icon_name);
+}
+
 static void
 on_realize(GtkWidget *window, gpointer *data)
 {
@@ -895,6 +908,7 @@ activate(GtkApplication *app, gpointer data)
 	GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
 	GtkWidget *iso_button = GTK_WIDGET(gtk_builder_get_object(builder, "iso-controls-button"));
 	GtkWidget *shutter_button = GTK_WIDGET(gtk_builder_get_object(builder, "shutter-controls-button"));
+	GtkWidget *flash_button = GTK_WIDGET(gtk_builder_get_object(builder, "flash-controls-button"));
 	GtkWidget *setting_dng_button = GTK_WIDGET(gtk_builder_get_object(builder, "setting-raw"));
 	preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
 	main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
@@ -916,6 +930,7 @@ activate(GtkApplication *app, gpointer data)
 
 	g_signal_connect(iso_button, "clicked", G_CALLBACK(open_iso_controls), NULL);
 	g_signal_connect(shutter_button, "clicked", G_CALLBACK(open_shutter_controls), NULL);
+	g_signal_connect(flash_button, "clicked", G_CALLBACK(flash_button_clicked), NULL);
 
 	// Setup actions
 	create_simple_action(app, "capture", G_CALLBACK(run_capture_action));