Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
Vasiliy Doylov 2025-06-11 18:06:41 +03:00
parent 06406bb455
commit ed2e965f9c
Signed by: NekoCWD
GPG key ID: B7BE22D44474A582
12 changed files with 298 additions and 204 deletions

View file

@ -1,44 +0,0 @@
{
"id": "io.gitlab.nekocwd.eyeneko",
"runtime": "org.gnome.Platform",
"runtime-version": "master",
"sdk": "org.gnome.Sdk",
"sdk-extensions": ["org.freedesktop.Sdk.Extension.vala"],
"command": "eyeneko",
"finish-args": [
"--share=network",
"--share=ipc",
"--socket=fallback-x11",
"--device=dri",
"--socket=wayland"
],
"build-options": {
"append-path": "/usr/lib/sdk/vala/bin",
"prepend-ld-library-path": "/usr/lib/sdk/vala/lib"
},
"cleanup": [
"/include",
"/lib/pkgconfig",
"/man",
"/share/doc",
"/share/gtk-doc",
"/share/man",
"/share/pkgconfig",
"/share/vala",
"*.la",
"*.a"
],
"modules": [
{
"name": "eyeneko",
"builddir": true,
"buildsystem": "meson",
"sources": [
{
"type": "git",
"url": "file:///home/neko/Projects"
}
]
}
]
}

View file

@ -29,7 +29,7 @@ public class AutoFocus : Object {
public void adjust_step (double prev_score) { public void adjust_step (double prev_score) {
// message ("%lf < %lf < %lf : %lf %lf / %lf", best_lens_pos, prev_pos, max_pos, step, prev_score, best_score); // message ("%lf < %lf < %lf : %lf %lf / %lf", best_lens_pos, prev_pos, max_pos, step, prev_score, best_score);
if (fc < Environment.get_variable ("FRAME_DELAY").to_int ()) { if (fc < EyeNeko.Env.get_variable_or ("FRAME_DELAY", "3").to_int ()) {
fc++; fc++;
return; return;
} else } else
@ -51,10 +51,16 @@ public class AutoFocus : Object {
break; break;
case 20: case 20:
message ("meow on 2"); message ("meow on 2");
step = 0.1; step = 0.5;
set_diff (2); set_diff (2);
set_camfocus (prev_pos); set_camfocus (prev_pos);
break; break;
case 5:
message ("meow on 0.5");
step = 0.1;
set_diff (1);
set_camfocus (prev_pos);
break;
case 1: case 1:
message ("meow on 0.1"); message ("meow on 0.1");
step = 10; step = 10;
@ -84,4 +90,13 @@ public class AutoFocus : Object {
prev_pos += step; prev_pos += step;
set_camfocus (prev_pos); set_camfocus (prev_pos);
} }
private static AutoFocus _instance = null;
public static AutoFocus instance {
get {
if (_instance == null)
_instance = new AutoFocus ();
return _instance;
}
}
} }

View file

@ -39,10 +39,14 @@ public class EyeNeko.Elements.BinBase : Gst.Bin {
}); });
} }
protected void add_pads(Gst.Element sink, Gst.Element src) { protected void add_pads(Gst.Element? sink, Gst.Element? src) {
sinkpad = new Gst.GhostPad("sink", sink.get_static_pad("sink")); if (sink != null) {
add_pad(sinkpad); sinkpad = new Gst.GhostPad("sink", sink.get_static_pad("sink"));
srcpad = new Gst.GhostPad("src", src.get_static_pad("src")); add_pad(sinkpad);
add_pad(srcpad); }
if (src != null) {
srcpad = new Gst.GhostPad("src", src.get_static_pad("src"));
add_pad(srcpad);
}
} }
} }

View file

@ -0,0 +1,46 @@
/* downscale.vala
*
* Copyright 2025 Vasiliy Doylov <nekocwd@mainlining.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
public class EyeNeko.Elements.CameraSrc : BinBase {
public Gst.Element source = null;
public Gst.Element capsfilter = Gst.ElementFactory.make ("capsfilter", "capsfilter");
public Gst.Element decodebin = Gst.ElementFactory.make ("decodebin3", "decoder");
public Gst.Element identity = Gst.ElementFactory.make ("identity");
static construct {
set_static_metadata ("camerasrc",
"Source",
"Camera source",
"nekocwd@mainlining.org");
}
public CameraSrc (Gst.Element src, Gst.Caps caps = new Gst.Caps.any ()) {
source = src;
add_many (source, capsfilter, decodebin, identity);
source.link_many (capsfilter, decodebin);
capsfilter.set_property ("caps", caps);
decodebin.pad_added.connect ((pad) => {
if (pad.get_stream () != null && pad.get_stream ().stream_type == Gst.StreamType.VIDEO) {
pad.link (identity.get_static_pad ("sink"));
}
});
add_pads (null, identity);
}
}

View file

@ -1,6 +1,8 @@
public class EyeNeko.Elements.FocusAnalyze : BinBase { public class EyeNeko.Elements.FocusAnalyze : BinBase {
public Gst.Element tee = Gst.ElementFactory.make ("tee"); public Gst.Element tee = Gst.ElementFactory.make ("tee");
public Gst.Element queue_analyze = Gst.ElementFactory.make ("queue"); public Gst.Element queue_analyze = Gst.ElementFactory.make ("queue");
public Gst.Element videocrop = Gst.ElementFactory.make ("videocrop");
public Gst.Element glupload = Gst.ElementFactory.make ("glupload");
public Gst.Element laplacian = Gst.ElementFactory.make ("gleffects_laplacian"); public Gst.Element laplacian = Gst.ElementFactory.make ("gleffects_laplacian");
public Gst.Element gldownload = Gst.ElementFactory.make ("gldownload"); public Gst.Element gldownload = Gst.ElementFactory.make ("gldownload");
public Gst.Element videoconvert = Gst.ElementFactory.make ("videoconvert"); public Gst.Element videoconvert = Gst.ElementFactory.make ("videoconvert");
@ -10,10 +12,23 @@ public class EyeNeko.Elements.FocusAnalyze : BinBase {
public Gst.Element queue_passthrough = Gst.ElementFactory.make ("queue"); public Gst.Element queue_passthrough = Gst.ElementFactory.make ("queue");
public double focus_mark { get; private set; } public double focus_mark { get; private set; }
public const int window_h = 256;
public const int window_w = 256;
public double window_wpos { get; set; default = 0.1; }
public double window_hpos { get; set; default = 0.1; }
public int width = 0;
public int height = 0;
public enum Orientation {
ROTATE0,
ROTATE90,
ROTATE180,
ROTATE270
}
Orientation orientation = Orientation.ROTATE0;
public Gdk.Paintable paintable { public Gdk.Paintable paintable {
owned get { owned get {
Gdk.Paintable paintable; Gdk.Paintable paintable = null;
gtk4paintablesink.get ("paintable", out paintable); gtk4paintablesink.get ("paintable", out paintable);
return paintable; return paintable;
} }
@ -26,6 +41,7 @@ public class EyeNeko.Elements.FocusAnalyze : BinBase {
double val; double val;
message.get_structure ().get_double ("luma-average", out val); message.get_structure ().get_double ("luma-average", out val);
focus_mark = val; focus_mark = val;
AutoFocus.instance.adjust_step (focus_mark);
} }
break; break;
default: default:
@ -41,14 +57,105 @@ public class EyeNeko.Elements.FocusAnalyze : BinBase {
"Focus analyze video filter.", "Focus analyze video filter.",
"nekocwd@mainlining.org"); "nekocwd@mainlining.org");
} }
public void set_window () {
var wpos = window_wpos;
var hpos = window_hpos;
switch (orientation) {
case Orientation.ROTATE90:
wpos = window_hpos;
hpos = 1 - window_wpos;
break;
case Orientation.ROTATE180:
wpos = 1 - window_wpos;
hpos = 1 - window_hpos;
break;
case Orientation.ROTATE270:
wpos = 1 - window_hpos;
hpos = window_wpos;
break;
default:
break;
}
int left = (int) (width * wpos - window_w / 2);
left = int.min (width - window_w, int.max (left, 0));
left = int.max (0, left);
int right = width - left - window_w;
right = int.max (0, right);
int top = (int) (height * hpos - window_h / 2);
top = int.min (height - window_h, int.max (top, 0));
top = int.max (0, top);
int bottom = height - top - window_h;
bottom = int.max (0, bottom);
videocrop.set_property ("left", left);
videocrop.set_property ("right", right);
videocrop.set_property ("top", top);
videocrop.set_property ("bottom", bottom);
message ("l=%d r=%d t=%d b=%d (%dx%d)", left, right, top, bottom, width - left - right, height - top - bottom);
}
static bool sink_event (Gst.Pad pad, Gst.Object? parent, owned Gst.Event event) {
var filter = (FocusAnalyze) parent;
if (event.type == Gst.EventType.TAG) {
Gst.TagList taglist;
event.parse_tag (out taglist);
string orientation;
taglist.get_string ("image-orientation", out orientation);
if (orientation != null) {
switch (orientation) {
case "rotate-0" :
filter.orientation = Orientation.ROTATE0;
break;
case "rotate-90":
filter.orientation = Orientation.ROTATE90;
break;
case "rotate-180":
filter.orientation = Orientation.ROTATE180;
break;
case "rotate-270":
filter.orientation = Orientation.ROTATE270;
break;
default:
break;
}
message ("%s", orientation);
}
}
if (event.type == Gst.EventType.CAPS) {
message ("Caps");
Gst.Caps caps;
event.parse_caps (out caps);
var info = new Gst.Video.Info.with_caps (caps);
filter.width = info.width;
filter.height = info.height;
filter.set_window ();
}
if (event.type == Gst.EventType.STREAM_START) {
message ("Stream-Collection");
filter.width = 0;
filter.height = 0;
filter.set_window ();
}
return pad.event_default (parent, event);
}
construct { construct {
add_many (tee, queue_analyze, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink, queue_passthrough); add_many (tee, queue_analyze, videocrop, glupload, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink, queue_passthrough);
tee.link_many (queue_analyze, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink);
tee.link_many (queue_passthrough); tee.link_many (queue_passthrough);
add_pads (tee, queue_passthrough); add_pads (tee, queue_passthrough);
tee.link_many (queue_analyze, videocrop, glupload, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink);
sinkpad.set_event_function (sink_event, null, null);
var bus = new Gst.Bus (); var bus = new Gst.Bus ();
videoanalyze.set_bus (bus); videoanalyze.set_bus (bus);
bus.add_watch (0, bus_callback); bus.add_watch (0, bus_callback);
notify["window-wpos"].connect (set_window);
notify["window-hpos"].connect (set_window);
} }
} }

View file

@ -1,16 +1,41 @@
public class EyeNeko.Gstreamer : Object { public class EyeNeko.Gstreamer : Object {
public Gst.Element viewfinder;
private Gst.Bin camerabin;
public enum CameraBinMode { public enum CameraBinMode {
VIDEO = 2, VIDEO = 2,
PHOTO = 1, PHOTO = 1,
} }
// 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 CameraBinMode camerabin_mode { get; set; default = CameraBinMode.PHOTO; }
public Gdk.Paintable viewfinder_paintable {
owned get {
Gdk.Paintable paintable;
viewfinder.get ("paintable", out paintable);
return paintable;
}
}
public Gdk.Paintable focus_paintable {
owned get {
return focus_analyze.paintable;
}
}
// Private elements
private Gst.Bin camerabin = (Gst.Bin) Gst.ElementFactory.make ("camerabin", "camerabin");
private Gst.Element viewfinder = Gst.ElementFactory.make ("gtk4paintablesink", "paintablesink");
private Gst.Bin camerasrc_wrapper = (Gst.Bin) Gst.ElementFactory.make ("wrappercamerabinsrc");
public Elements.FocusAnalyze focus_analyze = new Elements.FocusAnalyze ();
private Elements.ColorCorrectionMatrix color_correction_matrix = new Elements.ColorCorrectionMatrix ();
private Elements.Downscale downscale = new Elements.Downscale ();
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_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")); public int downscale_photo_to = int.parse (Env.get_variable_or ("DOWNSCALE_PHOTO", "0"));
public delegate int SelectStreamCB (Gst.Element decodebin, Gst.StreamCollection collection, Gst.Stream stream, void* userdata);
Gst.Caps get_best_caps (Gst.Device device) { Gst.Caps get_best_caps (Gst.Device device) {
@ -23,7 +48,7 @@ public class EyeNeko.Gstreamer : Object {
var caps = device.get_caps (); var caps = device.get_caps ();
for (uint i = 0; i < caps.get_size (); i++) { for (uint i = 0; i < caps.get_size (); i++) {
var format = caps.get_structure (i).get_name (); var format = caps.get_structure (i).get_name ();
int num, denom, width, height; int num = 1, denom = 1, width, height;
if (caps.get_structure (i).get_field_type ("width") != typeof (int) || caps.get_structure (i).get_field_type ("height") != typeof (int)) { if (caps.get_structure (i).get_field_type ("width") != typeof (int) || caps.get_structure (i).get_field_type ("height") != typeof (int)) {
warning ("Can't find best caps :(. I hate arrays!"); warning ("Can't find best caps :(. I hate arrays!");
} }
@ -86,13 +111,6 @@ public class EyeNeko.Gstreamer : Object {
message ("Capturing %s", location); message ("Capturing %s", location);
camerabin.set_property ("location", location); camerabin.set_property ("location", location);
Signal.emit_by_name (camerabin, "start-capture"); Signal.emit_by_name (camerabin, "start-capture");
var t = new TimeoutSource.seconds (5);
t.set_callback (() => {
Gst.Debug.bin_to_dot_file ((Gst.Bin) camerabin, Gst.DebugGraphDetails.ALL, "Capture");
return false;
});
t.attach ();
} }
public void stop_capture () { public void stop_capture () {
@ -114,37 +132,13 @@ public class EyeNeko.Gstreamer : Object {
} }
public void start_stream_from (Camera camera) { public void start_stream_from (Camera camera) {
current_camera = camera;
var camerasrc = new Gst.Bin ("CameraSRC");
var src = camera.device.create_element ("camerasrc");
var caps = get_best_caps (camera.device);
var capsfilt = Gst.ElementFactory.make ("capsfilter");
capsfilt.set_property ("caps", caps);
var db = Gst.ElementFactory.make ("decodebin3");
var identity = Gst.ElementFactory.make ("identity");
camerasrc.add_many (src, capsfilt, db, identity);
src.link_many (capsfilt, db);
db.pad_added.connect ((pad) => {
if (pad.get_stream () != null && pad.get_stream ().stream_type == Gst.StreamType.VIDEO) {
pad.link (identity.get_static_pad ("sink"));
}
});
camerasrc.add_pad (new Gst.GhostPad ("src", identity.get_static_pad ("src")));
var camerasrc_wrapper = Gst.ElementFactory.make ("wrappercamerabinsrc");
camerasrc_wrapper.set_property ("video-source", camerasrc);
camerasrc_wrapper.set_property ("video-source-filter",
pipe_elements ("Camera filter",
new Elements.Downscale.to (camerabin_mode == CameraBinMode.VIDEO ? downscale_video_to : downscale_photo_to),
new Logic.Filters.CCM ("CCM", true, true).element
));
camerabin.set_property ("camera-source", camerasrc_wrapper);
camerabin.set_state (Gst.State.NULL); camerabin.set_state (Gst.State.NULL);
current_camera = camera;
camerasrc_wrapper.set_property
("video-source",
new Elements.CameraSrc (camera.device.create_element (null),
get_best_caps (camera.device)));
camerabin.set_state (Gst.State.PLAYING); camerabin.set_state (Gst.State.PLAYING);
} }
@ -159,35 +153,53 @@ public class EyeNeko.Gstreamer : Object {
public void init (ref weak string[] args) { public void init (ref weak string[] args) {
Gst.init (ref args); Gst.init (ref args);
camerabin.set_property ("camera-source", camerasrc_wrapper);
camerabin.set_property ("viewfinder-sink", viewfinder);
color_correction_matrix.red_in_red = 1f;
color_correction_matrix.red_in_blue = 0.1f;
color_correction_matrix.red_in_green = 0.1f;
color_correction_matrix.blue_in_red = 0.1f;
color_correction_matrix.blue_in_blue = 1f;
color_correction_matrix.blue_in_green = 0.1f;
color_correction_matrix.green_in_green = 0.8f;
camerasrc_wrapper.set_property ("video-source-filter",
pipe_elements ("Camera Processing",
focus_analyze,
Gst.ElementFactory.make ("glupload"),
color_correction_matrix,
downscale
));
camerabin.set_property ("image-filter",
pipe_elements ("Image Processing",
Gst.ElementFactory.make ("gldownload")
));
camerabin = (Gst.Bin) Gst.ElementFactory.make ("camerabin", "camerabin");
camerabin.set_property ("video-filter", camerabin.set_property ("video-filter",
pipe_elements ("Video Pipeline", pipe_elements ("Video Processing",
Gst.ElementFactory.make ("gldownload"),
Gst.ElementFactory.make ("queue"), Gst.ElementFactory.make ("queue"),
Gst.ElementFactory.make ("videoconvert"), Gst.ElementFactory.make ("videoconvert"),
Gst.parse_launch (Env.get_variable_or Gst.parse_launch (Env.get_variable_or
( (
"VIDEO_ENCODE", "VIDEO_ENCODE",
"x264enc tune=zerolatency speed-preset=ultrafast bitrate=8192" "x264enc tune=zerolatency speed-preset=ultrafast bitrate=8192"
)))); )
)));
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 ("camerabin-mode", camerabin, "mode");
add_enc_profile (); add_enc_profile ();
notify["camerabin-mode"].connect (() => { notify["camerabin-mode"].connect (() => {
downscale.max_size = camerabin_mode == CameraBinMode.VIDEO ? downscale_video_to : downscale_photo_to;
start_stream_from (current_camera); start_stream_from (current_camera);
}); });
camerabin.bus.add_watch (0, bus_callback); camerabin.bus.add_watch (0, bus_callback);
} }
public Gdk.Paintable init_viewfinder () {
Gdk.Paintable paintable;
viewfinder = Gst.ElementFactory.make ("gtk4paintablesink", "paintablesink");
viewfinder.get ("paintable", out paintable);
camerabin.set_property ("viewfinder-sink", viewfinder);
return paintable;
}
public static void add_pads_to_bin (ref Gst.Bin bin, bool src = true, bool sink = true) { public static void add_pads_to_bin (ref Gst.Bin bin, bool src = true, bool sink = true) {
if (src) if (src)
bin.add_pad (new Gst.GhostPad ("src", ((Gst.Element) bin.get_child_by_index (0)).get_static_pad ("src"))); bin.add_pad (new Gst.GhostPad ("src", ((Gst.Element) bin.get_child_by_index (0)).get_static_pad ("src")));
@ -201,10 +213,10 @@ public class EyeNeko.Gstreamer : Object {
GLib.Error err; GLib.Error err;
string debug; string debug;
message.parse_error (out err, out debug); message.parse_error (out err, out debug);
GLib.warning ("Error: %s", err.message); GLib.warning ("Error: %s %s", err.message, err.domain.to_string ());
break; break;
case Gst.MessageType.EOS: case Gst.MessageType.EOS:
GLib.warning ("end of stream\n"); GLib.warning ("End of stream\n");
break; break;
case Gst.MessageType.STATE_CHANGED: case Gst.MessageType.STATE_CHANGED:
Gst.State oldstate; Gst.State oldstate;
@ -212,21 +224,13 @@ public class EyeNeko.Gstreamer : Object {
Gst.State pending; Gst.State pending;
message.parse_state_changed (out oldstate, out newstate, message.parse_state_changed (out oldstate, out newstate,
out pending); out pending);
// GLib.message ("state changed: %s->%s:%s\n", /*
// oldstate.to_string (), newstate.to_string (), GLib.message ("state changed: %s->%s:%s\n",
// pending.to_string ()); oldstate.to_string (), newstate.to_string (),
break; pending.to_string ());
case Gst.MessageType.TAG: */
Gst.TagList tag_list;
stdout.printf ("taglist found\n");
message.parse_tag (out tag_list);
break; break;
case Gst.MessageType.ELEMENT: case Gst.MessageType.ELEMENT:
if (message.get_structure ().get_name () == "GstVideoAnalyse") {
double val;
message.get_structure ().get_double ("luma-average", out val);
// af.adjust_step (val);
}
break; break;
default: default:
break; break;
@ -246,4 +250,9 @@ public class EyeNeko.Gstreamer : Object {
return _instance; return _instance;
} }
} }
static construct {
unowned string[] ? gst_args = null;
Gst.init (ref gst_args);
}
} }

View file

@ -1,34 +0,0 @@
public class EyeNeko.Logic.Filters.CCM : GLFilter {
public float red_in_red { get; set; default = 1.0f; }
public float red_in_green { get; set; default = 0.1f; }
public float red_in_blue { get; set; default = 0.1f; }
public float green_in_red { get; set; default = 0.0f; }
public float green_in_green { get; set; default = 0.8f; }
public float green_in_blue { get; set; default = 0.0f; }
public float blue_in_red { get; set; default = 0.1f; }
public float blue_in_green { get; set; default = 0.1f; }
public float blue_in_blue { get; set; default = 1.0f; }
public CCM (string name = "CCM", bool glupload = false, bool gldownload = false) {
base ("ccm", name, glupload, gldownload);
update_props ();
notify.connect (update_props);
}
private void update_props () {
Gst.Structure uniforms = new Gst.Structure.empty ("uniforms");
uniforms.set ("rr", typeof (float), red_in_red);
uniforms.set ("rg", typeof (float), red_in_green);
uniforms.set ("rb", typeof (float), red_in_blue);
uniforms.set ("gr", typeof (float), green_in_red);
uniforms.set ("gg", typeof (float), green_in_green);
uniforms.set ("gb", typeof (float), green_in_blue);
uniforms.set ("br", typeof (float), blue_in_red);
uniforms.set ("bg", typeof (float), blue_in_green);
uniforms.set ("bb", typeof (float), blue_in_blue);
gl_shader.set_property ("uniforms", uniforms);
}
}

View file

@ -1,43 +0,0 @@
namespace EyeNeko.Logic.Filters {
public interface Filter : Object {
public abstract Gst.Element element { get; }
}
public class GLFilter : Filter, Object {
public static string get_shader_source (string name) {
try {
return (string) resources_lookup_data ("/io/gitlab/nekocwd/eyeneko/shaders/" + name + ".glsl", GLib.ResourceLookupFlags.NONE).get_data ();
} catch (Error err) {
warning ("Error during shader lookup. %s", err.message);
return "";
}
}
public Gst.Element element { get; }
public Gst.Element gl_shader { get; }
public GLFilter (string shader_name, string elem_name = "GLShader", bool glupload = false, bool gldownload = false) {
var bin = new Gst.Bin (elem_name);
var gl_upload = Gst.ElementFactory.make ("glupload", @"$(elem_name)-upload");
var gl_download = Gst.ElementFactory.make ("gldownload", @"$(elem_name)-download");
_gl_shader = Gst.ElementFactory.make ("glshader", @"$(elem_name)-shader");
gl_shader.set_property ("fragment", GLFilter.get_shader_source (shader_name));
if (glupload) {
bin.add (gl_upload);
}
bin.add (gl_shader);
if (gldownload) {
bin.add (gl_download);
gl_shader.link (gl_download);
}
if (glupload) {
gl_upload.link (gl_shader);
}
EyeNeko.Gstreamer.add_pads_to_bin (ref bin);
_element = bin;
}
}
}

View file

@ -6,14 +6,14 @@ eyeneko_sources = [
'gst.vala', 'gst.vala',
'pipetap_proxy.vala', 'pipetap_proxy.vala',
'auto_focus.vala', 'auto_focus.vala',
'logic/color_correction_filter.vala',
'logic/filter.vala',
'logic/helpers.vala', 'logic/helpers.vala',
'elements/bin_base.vala', 'elements/bin_base.vala',
'elements/camerasrc.vala',
'elements/downscale.vala', 'elements/downscale.vala',
'elements/focus_analyze.vala', 'elements/focus_analyze.vala',
'elements/gl_filter.vala', 'elements/gl_filter.vala',
'elements/color_correction_matrix.vala', 'elements/color_correction_matrix.vala',
'ui/preferences.vala',
] ]
vapi_dir = meson.current_source_dir() / 'vapi' vapi_dir = meson.current_source_dir() / 'vapi'

6
src/ui/preferences.vala Normal file
View file

@ -0,0 +1,6 @@
public class EyeNeko.Ui.Settings : Adw.PreferencesDialog {
private Adw.PreferencesPage cameras = new Adw.PreferencesPage ();
construct {
cameras.title = _("Cameras");
}
}

View file

@ -13,13 +13,19 @@ template $EyeNekoWindow: Adw.ApplicationWindow {
content: Gtk.Overlay overlay { content: Gtk.Overlay overlay {
child: Gtk.Picture viewfinder { child: Gtk.Picture viewfinder {
halign: fill; halign: fill;
valign: fill; valign: start;
hexpand: true; hexpand: true;
vexpand: true; vexpand: true;
}; };
}; };
} }
Gtk.Picture focus_overlay {
opacity: 0.5;
halign: start;
valign: start;
}
Adw.ToolbarView toolbar { Adw.ToolbarView toolbar {
styles [ styles [
"flat", "flat",
@ -64,7 +70,7 @@ Adw.ToolbarView toolbar {
valign: center; valign: center;
} }
Button { Button focus_btn {
valign: center; valign: center;
halign: end; halign: end;
width-request: 60; width-request: 60;

View file

@ -27,12 +27,19 @@ public class EyeNeko.Window : Adw.ApplicationWindow {
[GtkChild] [GtkChild]
private unowned Gtk.Picture viewfinder; private unowned Gtk.Picture viewfinder;
[GtkChild] [GtkChild]
private unowned Gtk.Picture focus_overlay;
[GtkChild]
private unowned Gtk.MenuButton video_source_btn; private unowned Gtk.MenuButton video_source_btn;
[GtkChild] [GtkChild]
private unowned Gtk.Button capture_btn; private unowned Gtk.Button capture_btn;
[GtkChild] [GtkChild]
private unowned Adw.ToggleGroup camera_mode; private unowned Adw.ToggleGroup camera_mode;
[GtkChild]
private unowned Gtk.Button focus_btn;
[GtkChild]
private unowned Gtk.Label focus_label;
public string camera_path { get; set; default = "Unknown"; } public string camera_path { get; set; default = "Unknown"; }
private void setup_video_source_changer () { private void setup_video_source_changer () {
notify["camera-path"].connect (() => { notify["camera-path"].connect (() => {
@ -74,16 +81,31 @@ public class EyeNeko.Window : Adw.ApplicationWindow {
public Window (Gtk.Application app) { public Window (Gtk.Application app) {
Object (application: app); Object (application: app);
overlay.add_overlay (toolbar); overlay.add_overlay (toolbar);
overlay.add_overlay (focus_overlay);
setup_video_source_changer (); setup_video_source_changer ();
Gstreamer.instance.bind_property ("focus-paintable", focus_overlay, "paintable", BindingFlags.SYNC_CREATE);
viewfinder.set_paintable (Gstreamer.instance.init_viewfinder ()); viewfinder.set_paintable (Gstreamer.instance.viewfinder_paintable);
Gstreamer.instance.start_stream_from (Gstreamer.Camera.get_all ()[0]); Gstreamer.instance.start_stream_from (Gstreamer.Camera.get_all ()[0]);
var ec = new Gtk.GestureClick ();
overlay.add_controller (ec);
ec.pressed.connect ((n, x, y) => {
if (!viewfinder.contains (x, y))
return;
var width = x / viewfinder.get_width ();
var height = y / viewfinder.get_height ();
Gstreamer.instance.focus_analyze.window_hpos = height;
Gstreamer.instance.focus_analyze.window_wpos = width;
});
ec.button = 0;
// focus.clicked.connect (Gstreamer.af.reset_focus); focus_btn.clicked.connect (AutoFocus.instance.reset_focus);
// Gstreamer.af.bind_property ("prev_pos", focus_label, "label", GLib.BindingFlags.SYNC_CREATE, (b, src, ref tgt) => { tgt = "%.1f".printf (src.get_double ()); return true; }); AutoFocus.instance.bind_property ("prev_pos", focus_label, "label", GLib.BindingFlags.SYNC_CREATE, (b, src, ref tgt) => { tgt = "%.1f".printf (src.get_double ()); return true; });
capture_btn.clicked.connect (() => { capture_btn.clicked.connect (() => {
message ("%d %d", Gstreamer.instance.camerabin_mode, (int) Gstreamer.instance.ready); message ("%d %d", Gstreamer.instance.camerabin_mode, (int) Gstreamer.instance.ready);