GST&UI: add QR code scan logic
Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
parent
482e2188ff
commit
269a72967b
5 changed files with 73 additions and 25 deletions
2
data/icons/actions/qr-code-scanner-symbolic.svg
Normal file
2
data/icons/actions/qr-code-scanner-symbolic.svg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 2 0 s -0.457031 -0.015625 -0.949219 0.230469 c -0.488281 0.246093 -1.050781 0.9375 -1.050781 1.769531 v 3 h 2 v -3 h 3 v -2 z m 9 0 v 2 h 3 v 3 h 2 v -3 c 0 -0.832031 -0.5625 -1.523438 -1.054688 -1.769531 c -0.488281 -0.246094 -0.945312 -0.230469 -0.945312 -0.230469 z m -8 3 v 2 h 2 v -2 z m 2 2 v 2 h 2 v -2 z m 3 -2 v 5 h 5 v -5 z m 0 5 h -5 v 5 h 5 z m 1 -4 h 3 v 3 h -3 z m 1 1 v 1 h 1 v -1 z m -6 4 h 3 v 3 h -3 z m 5 0 v 3 h 1 v -1 h 1 v -1 h 1 v -1 z m 3 1 v 1 h 1 v -1 z m 0 1 h -1 v 1 h 1 z m 0 1 v 1 h 1 v -1 z m -1 0 h -1 v 1 h 1 z m -6 -2 v 1 h 1 v -1 z m -5 1 v 3 c 0 0.832031 0.5625 1.523438 1.050781 1.769531 c 0.492188 0.246094 0.949219 0.230469 0.949219 0.230469 h 3 v -2 h -3 v -3 z m 14 0 v 3 h -3 v 2 h 3 s 0.457031 0.015625 0.945312 -0.230469 c 0.492188 -0.246093 1.054688 -0.9375 1.054688 -1.769531 v -3 z m 0 0" fill="#222222"/></svg>
|
After Width: | Height: | Size: 996 B |
|
@ -8,5 +8,6 @@
|
||||||
<file preprocess="xml-stripblanks">actions/camera-photo-symbolic.svg</file>
|
<file preprocess="xml-stripblanks">actions/camera-photo-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks">actions/pick-camera-alt2-symbolic.svg</file>
|
<file preprocess="xml-stripblanks">actions/pick-camera-alt2-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks">actions/encoder-knob-symbolic.svg</file>
|
<file preprocess="xml-stripblanks">actions/encoder-knob-symbolic.svg</file>
|
||||||
|
<file preprocess="xml-stripblanks">actions/qr-code-scanner-symbolic.svg</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|
71
src/gst.vala
71
src/gst.vala
|
@ -5,9 +5,39 @@ public class EyeNeko.Gstreamer : Object {
|
||||||
PHOTO = 1,
|
PHOTO = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum CameraMode {
|
||||||
|
VIDEO,
|
||||||
|
PHOTO,
|
||||||
|
QR;
|
||||||
|
|
||||||
|
public string to_string () {
|
||||||
|
return ((EnumClass) typeof (CameraMode).class_ref ()).get_value (this).value_nick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CameraMode parse (string data) {
|
||||||
|
return ((EnumClass) typeof (CameraMode).class_ref ()).get_value_by_nick (data) ? .value ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CameraBinMode to_camerabin_mode () {
|
||||||
|
return this == VIDEO ? CameraBinMode.VIDEO : CameraBinMode.PHOTO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Gee.HashMap<CameraMode, Gst.Caps> mode_caps = new Gee.HashMap<CameraMode, Gst.Caps> ();
|
||||||
|
private static Gee.HashMap<CameraMode, int> mode_downscale = new Gee.HashMap<CameraMode, int> ();
|
||||||
|
|
||||||
|
static construct {
|
||||||
|
mode_caps[CameraMode.PHOTO] = new Gst.Caps.any ();
|
||||||
|
mode_caps[CameraMode.VIDEO] = new Gst.Caps.any ();
|
||||||
|
mode_caps[CameraMode.QR] = Gst.Caps.from_string ("video/x-raw, format=GRAY8");
|
||||||
|
mode_downscale[CameraMode.VIDEO] = int.parse (Env.get_variable_or ("DOWNSCALE_VIDEO", "640"));
|
||||||
|
mode_downscale[CameraMode.PHOTO] = int.parse (Env.get_variable_or ("DOWNSCALE_PHOTO", "0"));
|
||||||
|
mode_downscale[CameraMode.QR] = int.parse (Env.get_variable_or ("DOWNSCALE_QR", "0"));
|
||||||
|
}
|
||||||
|
|
||||||
// Public properties
|
// Public properties
|
||||||
public bool ready { get; set; default = false; }
|
public bool ready { get; set; default = false; }
|
||||||
public CameraBinMode camerabin_mode { get; set; default = CameraBinMode.PHOTO; }
|
public CameraMode camera_mode { get; set; default = CameraMode.PHOTO; }
|
||||||
public Gdk.Paintable viewfinder_paintable { get; set; }
|
public Gdk.Paintable viewfinder_paintable { get; set; }
|
||||||
public ListStore available_caps = new ListStore (typeof (FriendlyCaps));
|
public ListStore available_caps = new ListStore (typeof (FriendlyCaps));
|
||||||
public Gtk.SingleSelection caps_selecton_model = new Gtk.SingleSelection (null);
|
public Gtk.SingleSelection caps_selecton_model = new Gtk.SingleSelection (null);
|
||||||
|
@ -19,11 +49,11 @@ public class EyeNeko.Gstreamer : Object {
|
||||||
private Gst.Bin camerasrc_wrapper = (Gst.Bin) Gst.ElementFactory.make ("wrappercamerabinsrc");
|
private Gst.Bin camerasrc_wrapper = (Gst.Bin) Gst.ElementFactory.make ("wrappercamerabinsrc");
|
||||||
private Elements.ColorCorrectionMatrix color_correction_matrix = new Elements.ColorCorrectionMatrix ();
|
private Elements.ColorCorrectionMatrix color_correction_matrix = new Elements.ColorCorrectionMatrix ();
|
||||||
private Elements.Downscale downscale = new Elements.Downscale ();
|
private Elements.Downscale downscale = new Elements.Downscale ();
|
||||||
|
private Gst.Element camera_convert_caps = Gst.ElementFactory.make ("capsfilter");
|
||||||
|
public Elements.QrScaner qr_scaner = new Elements.QrScaner ();
|
||||||
|
|
||||||
// Other Fields
|
// Other Fields
|
||||||
public Camera current_camera = null;
|
public Camera current_camera = null;
|
||||||
public int downscale_video_to = int.parse (Env.get_variable_or ("DOWNSCALE_VIDEO", "640"));
|
|
||||||
public int downscale_photo_to = int.parse (Env.get_variable_or ("DOWNSCALE_PHOTO", "0"));
|
|
||||||
private bool can_start_stream = true;
|
private bool can_start_stream = true;
|
||||||
|
|
||||||
private void set_up_caps (Gst.Device device) {
|
private void set_up_caps (Gst.Device device) {
|
||||||
|
@ -66,7 +96,7 @@ public class EyeNeko.Gstreamer : Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start_capture () {
|
public void start_capture () {
|
||||||
var file_dir = Environment.get_user_special_dir (camerabin_mode == CameraBinMode.VIDEO ? UserDirectory.VIDEOS : UserDirectory.PICTURES);
|
var file_dir = Environment.get_user_special_dir (camera_mode == CameraMode.VIDEO ? UserDirectory.VIDEOS : UserDirectory.PICTURES);
|
||||||
var dir = File.new_build_filename (file_dir, "EyeNeko");
|
var dir = File.new_build_filename (file_dir, "EyeNeko");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -79,7 +109,7 @@ public class EyeNeko.Gstreamer : Object {
|
||||||
|
|
||||||
var time = new DateTime.now ();
|
var time = new DateTime.now ();
|
||||||
var formated = time.format ("%Y-%m-%d-%H-%M-%S");
|
var formated = time.format ("%Y-%m-%d-%H-%M-%S");
|
||||||
var filename = "%s.%s".printf (formated, camerabin_mode == CameraBinMode.VIDEO ? "mp4" : "jpeg");
|
var filename = "%s.%s".printf (formated, camera_mode == CameraMode.VIDEO ? "mp4" : "jpeg");
|
||||||
var location = Path.build_filename (dir.get_path (), filename);
|
var location = Path.build_filename (dir.get_path (), filename);
|
||||||
message ("Capturing %s", location);
|
message ("Capturing %s", location);
|
||||||
camerabin.set_property ("location", location);
|
camerabin.set_property ("location", location);
|
||||||
|
@ -163,6 +193,10 @@ public class EyeNeko.Gstreamer : Object {
|
||||||
camerasrc_wrapper.set_property ("video-source-filter",
|
camerasrc_wrapper.set_property ("video-source-filter",
|
||||||
pipe_elements ("Camera Processing",
|
pipe_elements ("Camera Processing",
|
||||||
downscale,
|
downscale,
|
||||||
|
Gst.ElementFactory.make ("videoconvert"),
|
||||||
|
camera_convert_caps,
|
||||||
|
qr_scaner,
|
||||||
|
Gst.ElementFactory.make ("videoconvert"),
|
||||||
Gst.ElementFactory.make ("glupload"),
|
Gst.ElementFactory.make ("glupload"),
|
||||||
color_correction_matrix,
|
color_correction_matrix,
|
||||||
Gst.ElementFactory.make ("gldownload")
|
Gst.ElementFactory.make ("gldownload")
|
||||||
|
@ -181,15 +215,26 @@ public class EyeNeko.Gstreamer : Object {
|
||||||
)));
|
)));
|
||||||
|
|
||||||
camerabin.bind_property ("idle", this, "ready", BindingFlags.SYNC_CREATE);
|
camerabin.bind_property ("idle", this, "ready", BindingFlags.SYNC_CREATE);
|
||||||
this.bind_property ("camerabin-mode", camerabin, "mode");
|
this.bind_property ("camera-mode", camerabin, "mode", BindingFlags.SYNC_CREATE, (b, s, ref d) => {
|
||||||
add_enc_profile ();
|
d.set_enum (((CameraMode) s).to_camerabin_mode ());
|
||||||
|
return true;
|
||||||
notify["camerabin-mode"].connect (() => {
|
|
||||||
downscale.max_size = camerabin_mode == CameraBinMode.VIDEO ? downscale_video_to : downscale_photo_to;
|
|
||||||
if (current_camera != null) {
|
|
||||||
start_stream_from (current_camera);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
this.bind_property ("camera-mode", camera_convert_caps, "caps", BindingFlags.SYNC_CREATE, (b, s, ref d) => {
|
||||||
|
d = mode_caps[((CameraMode) s)];
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
this.bind_property ("camera-mode", downscale, "max-size", BindingFlags.SYNC_CREATE, (b, s, ref d) => {
|
||||||
|
d = mode_downscale[((CameraMode) s)];
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
this.bind_property ("camera-mode", qr_scaner, "enabled", BindingFlags.SYNC_CREATE, (b, s, ref d) => {
|
||||||
|
d = (CameraMode) s == CameraMode.QR;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
notify["camera-mode"].connect (() => start_stream_from (current_camera));
|
||||||
|
|
||||||
|
add_enc_profile ();
|
||||||
|
|
||||||
Gdk.GLContext context;
|
Gdk.GLContext context;
|
||||||
viewfinder_paintable.get ("gl-context", out context);
|
viewfinder_paintable.get ("gl-context", out context);
|
||||||
|
|
|
@ -74,6 +74,12 @@ Adw.ToolbarView toolbar {
|
||||||
label: "Video";
|
label: "Video";
|
||||||
name: "video";
|
name: "video";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Adw.Toggle {
|
||||||
|
icon-name: "qr-code-scanner-symbolic";
|
||||||
|
label: "QR";
|
||||||
|
name: "qr";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
|
|
|
@ -109,24 +109,16 @@ public class EyeNeko.Window : Adw.ApplicationWindow {
|
||||||
|
|
||||||
Gstreamer.instance.bind_property ("viewfinder-paintable", viewfinder, "paintable", BindingFlags.SYNC_CREATE);
|
Gstreamer.instance.bind_property ("viewfinder-paintable", viewfinder, "paintable", BindingFlags.SYNC_CREATE);
|
||||||
capture_btn.clicked.connect (() => {
|
capture_btn.clicked.connect (() => {
|
||||||
if (Gstreamer.instance.camerabin_mode == Gstreamer.CameraBinMode.VIDEO && !Gstreamer.instance.ready) {
|
if (Gstreamer.instance.camera_mode == Gstreamer.CameraMode.VIDEO && !Gstreamer.instance.ready) {
|
||||||
Gstreamer.instance.stop_capture ();
|
Gstreamer.instance.stop_capture ();
|
||||||
} else {
|
} else {
|
||||||
Gstreamer.instance.start_capture ();
|
Gstreamer.instance.start_capture ();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
camera_mode.notify["active"].connect (() => {
|
camera_mode.bind_property ("active-name", Gstreamer.instance, "camera-mode", BindingFlags.SYNC_CREATE, (b, s, ref d) => {
|
||||||
switch (camera_mode.active_name) {
|
d = Gstreamer.CameraMode.parse (s.get_string ());
|
||||||
case "photo":
|
return true;
|
||||||
Gstreamer.instance.camerabin_mode = Gstreamer.CameraBinMode.PHOTO;
|
|
||||||
break;
|
|
||||||
case "video":
|
|
||||||
Gstreamer.instance.camerabin_mode = Gstreamer.CameraBinMode.VIDEO;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
camera_mode.active_name = Env.get_variable_or ("CAM_M", "photo");
|
camera_mode.active_name = Env.get_variable_or ("CAM_M", "photo");
|
||||||
|
|
||||||
|
@ -136,5 +128,7 @@ public class EyeNeko.Window : Adw.ApplicationWindow {
|
||||||
Gstreamer.instance.start_stream_from (null);
|
Gstreamer.instance.start_stream_from (null);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Gstreamer.instance.qr_scaner.new_qr.connect ((qr) => message ("QR scaned %s", qr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue