WIP
Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
parent
06406bb455
commit
ed2e965f9c
12 changed files with 298 additions and 204 deletions
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -29,7 +29,7 @@ public class AutoFocus : Object {
|
|||
|
||||
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);
|
||||
if (fc < Environment.get_variable ("FRAME_DELAY").to_int ()) {
|
||||
if (fc < EyeNeko.Env.get_variable_or ("FRAME_DELAY", "3").to_int ()) {
|
||||
fc++;
|
||||
return;
|
||||
} else
|
||||
|
@ -51,10 +51,16 @@ public class AutoFocus : Object {
|
|||
break;
|
||||
case 20:
|
||||
message ("meow on 2");
|
||||
step = 0.1;
|
||||
step = 0.5;
|
||||
set_diff (2);
|
||||
set_camfocus (prev_pos);
|
||||
break;
|
||||
case 5:
|
||||
message ("meow on 0.5");
|
||||
step = 0.1;
|
||||
set_diff (1);
|
||||
set_camfocus (prev_pos);
|
||||
break;
|
||||
case 1:
|
||||
message ("meow on 0.1");
|
||||
step = 10;
|
||||
|
@ -84,4 +90,13 @@ public class AutoFocus : Object {
|
|||
prev_pos += step;
|
||||
set_camfocus (prev_pos);
|
||||
}
|
||||
|
||||
private static AutoFocus _instance = null;
|
||||
public static AutoFocus instance {
|
||||
get {
|
||||
if (_instance == null)
|
||||
_instance = new AutoFocus ();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,14 @@ public class EyeNeko.Elements.BinBase : Gst.Bin {
|
|||
});
|
||||
}
|
||||
|
||||
protected void add_pads(Gst.Element sink, Gst.Element src) {
|
||||
sinkpad = new Gst.GhostPad("sink", sink.get_static_pad("sink"));
|
||||
add_pad(sinkpad);
|
||||
srcpad = new Gst.GhostPad("src", src.get_static_pad("src"));
|
||||
add_pad(srcpad);
|
||||
protected void add_pads(Gst.Element? sink, Gst.Element? src) {
|
||||
if (sink != null) {
|
||||
sinkpad = new Gst.GhostPad("sink", sink.get_static_pad("sink"));
|
||||
add_pad(sinkpad);
|
||||
}
|
||||
if (src != null) {
|
||||
srcpad = new Gst.GhostPad("src", src.get_static_pad("src"));
|
||||
add_pad(srcpad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
46
src/elements/camerasrc.vala
Normal file
46
src/elements/camerasrc.vala
Normal 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);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
public class EyeNeko.Elements.FocusAnalyze : BinBase {
|
||||
public Gst.Element tee = Gst.ElementFactory.make ("tee");
|
||||
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 gldownload = Gst.ElementFactory.make ("gldownload");
|
||||
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 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 {
|
||||
owned get {
|
||||
Gdk.Paintable paintable;
|
||||
Gdk.Paintable paintable = null;
|
||||
gtk4paintablesink.get ("paintable", out paintable);
|
||||
return paintable;
|
||||
}
|
||||
|
@ -26,6 +41,7 @@ public class EyeNeko.Elements.FocusAnalyze : BinBase {
|
|||
double val;
|
||||
message.get_structure ().get_double ("luma-average", out val);
|
||||
focus_mark = val;
|
||||
AutoFocus.instance.adjust_step (focus_mark);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -41,14 +57,105 @@ public class EyeNeko.Elements.FocusAnalyze : BinBase {
|
|||
"Focus analyze video filter.",
|
||||
"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 {
|
||||
add_many (tee, queue_analyze, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink, queue_passthrough);
|
||||
tee.link_many (queue_analyze, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink);
|
||||
add_many (tee, queue_analyze, videocrop, glupload, laplacian, gldownload, videoconvert, videoanalyze, videoconvert2, gtk4paintablesink, queue_passthrough);
|
||||
tee.link_many (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 ();
|
||||
videoanalyze.set_bus (bus);
|
||||
bus.add_watch (0, bus_callback);
|
||||
notify["window-wpos"].connect (set_window);
|
||||
notify["window-hpos"].connect (set_window);
|
||||
}
|
||||
}
|
||||
|
|
141
src/gst.vala
141
src/gst.vala
|
@ -1,16 +1,41 @@
|
|||
|
||||
public class EyeNeko.Gstreamer : Object {
|
||||
public Gst.Element viewfinder;
|
||||
private Gst.Bin camerabin;
|
||||
public enum CameraBinMode {
|
||||
VIDEO = 2,
|
||||
PHOTO = 1,
|
||||
}
|
||||
// Public properties
|
||||
public bool ready { get; set; default = false; }
|
||||
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 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 delegate int SelectStreamCB (Gst.Element decodebin, Gst.StreamCollection collection, Gst.Stream stream, void* userdata);
|
||||
|
||||
|
||||
Gst.Caps get_best_caps (Gst.Device device) {
|
||||
|
@ -23,7 +48,7 @@ public class EyeNeko.Gstreamer : Object {
|
|||
var caps = device.get_caps ();
|
||||
for (uint i = 0; i < caps.get_size (); i++) {
|
||||
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)) {
|
||||
warning ("Can't find best caps :(. I hate arrays!");
|
||||
}
|
||||
|
@ -86,13 +111,6 @@ public class EyeNeko.Gstreamer : Object {
|
|||
message ("Capturing %s", location);
|
||||
camerabin.set_property ("location", location);
|
||||
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 () {
|
||||
|
@ -114,37 +132,13 @@ public class EyeNeko.Gstreamer : Object {
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -159,35 +153,53 @@ public class EyeNeko.Gstreamer : Object {
|
|||
|
||||
public void init (ref weak string[] 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",
|
||||
pipe_elements ("Video Pipeline",
|
||||
pipe_elements ("Video Processing",
|
||||
Gst.ElementFactory.make ("gldownload"),
|
||||
Gst.ElementFactory.make ("queue"),
|
||||
Gst.ElementFactory.make ("videoconvert"),
|
||||
Gst.parse_launch (Env.get_variable_or
|
||||
(
|
||||
"VIDEO_ENCODE",
|
||||
"x264enc tune=zerolatency speed-preset=ultrafast bitrate=8192"
|
||||
))));
|
||||
)
|
||||
)));
|
||||
|
||||
camerabin.bind_property ("idle", this, "ready", BindingFlags.SYNC_CREATE);
|
||||
this.bind_property ("camerabin-mode", camerabin, "mode");
|
||||
add_enc_profile ();
|
||||
|
||||
notify["camerabin-mode"].connect (() => {
|
||||
downscale.max_size = camerabin_mode == CameraBinMode.VIDEO ? downscale_video_to : downscale_photo_to;
|
||||
start_stream_from (current_camera);
|
||||
});
|
||||
|
||||
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) {
|
||||
if (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;
|
||||
string 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;
|
||||
case Gst.MessageType.EOS:
|
||||
GLib.warning ("end of stream\n");
|
||||
GLib.warning ("End of stream\n");
|
||||
break;
|
||||
case Gst.MessageType.STATE_CHANGED:
|
||||
Gst.State oldstate;
|
||||
|
@ -212,21 +224,13 @@ public class EyeNeko.Gstreamer : Object {
|
|||
Gst.State pending;
|
||||
message.parse_state_changed (out oldstate, out newstate,
|
||||
out pending);
|
||||
// GLib.message ("state changed: %s->%s:%s\n",
|
||||
// oldstate.to_string (), newstate.to_string (),
|
||||
// pending.to_string ());
|
||||
break;
|
||||
case Gst.MessageType.TAG:
|
||||
Gst.TagList tag_list;
|
||||
stdout.printf ("taglist found\n");
|
||||
message.parse_tag (out tag_list);
|
||||
/*
|
||||
GLib.message ("state changed: %s->%s:%s\n",
|
||||
oldstate.to_string (), newstate.to_string (),
|
||||
pending.to_string ());
|
||||
*/
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
|
@ -246,4 +250,9 @@ public class EyeNeko.Gstreamer : Object {
|
|||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
static construct {
|
||||
unowned string[] ? gst_args = null;
|
||||
Gst.init (ref gst_args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,14 +6,14 @@ eyeneko_sources = [
|
|||
'gst.vala',
|
||||
'pipetap_proxy.vala',
|
||||
'auto_focus.vala',
|
||||
'logic/color_correction_filter.vala',
|
||||
'logic/filter.vala',
|
||||
'logic/helpers.vala',
|
||||
'elements/bin_base.vala',
|
||||
'elements/camerasrc.vala',
|
||||
'elements/downscale.vala',
|
||||
'elements/focus_analyze.vala',
|
||||
'elements/gl_filter.vala',
|
||||
'elements/color_correction_matrix.vala',
|
||||
'ui/preferences.vala',
|
||||
]
|
||||
|
||||
vapi_dir = meson.current_source_dir() / 'vapi'
|
||||
|
|
6
src/ui/preferences.vala
Normal file
6
src/ui/preferences.vala
Normal file
|
@ -0,0 +1,6 @@
|
|||
public class EyeNeko.Ui.Settings : Adw.PreferencesDialog {
|
||||
private Adw.PreferencesPage cameras = new Adw.PreferencesPage ();
|
||||
construct {
|
||||
cameras.title = _("Cameras");
|
||||
}
|
||||
}
|
|
@ -13,13 +13,19 @@ template $EyeNekoWindow: Adw.ApplicationWindow {
|
|||
content: Gtk.Overlay overlay {
|
||||
child: Gtk.Picture viewfinder {
|
||||
halign: fill;
|
||||
valign: fill;
|
||||
valign: start;
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Gtk.Picture focus_overlay {
|
||||
opacity: 0.5;
|
||||
halign: start;
|
||||
valign: start;
|
||||
}
|
||||
|
||||
Adw.ToolbarView toolbar {
|
||||
styles [
|
||||
"flat",
|
||||
|
@ -64,7 +70,7 @@ Adw.ToolbarView toolbar {
|
|||
valign: center;
|
||||
}
|
||||
|
||||
Button {
|
||||
Button focus_btn {
|
||||
valign: center;
|
||||
halign: end;
|
||||
width-request: 60;
|
||||
|
|
|
@ -27,12 +27,19 @@ public class EyeNeko.Window : Adw.ApplicationWindow {
|
|||
[GtkChild]
|
||||
private unowned Gtk.Picture viewfinder;
|
||||
[GtkChild]
|
||||
private unowned Gtk.Picture focus_overlay;
|
||||
[GtkChild]
|
||||
private unowned Gtk.MenuButton video_source_btn;
|
||||
[GtkChild]
|
||||
private unowned Gtk.Button capture_btn;
|
||||
[GtkChild]
|
||||
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"; }
|
||||
private void setup_video_source_changer () {
|
||||
notify["camera-path"].connect (() => {
|
||||
|
@ -74,16 +81,31 @@ public class EyeNeko.Window : Adw.ApplicationWindow {
|
|||
public Window (Gtk.Application app) {
|
||||
Object (application: app);
|
||||
overlay.add_overlay (toolbar);
|
||||
overlay.add_overlay (focus_overlay);
|
||||
|
||||
|
||||
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]);
|
||||
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);
|
||||
// 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; });
|
||||
focus_btn.clicked.connect (AutoFocus.instance.reset_focus);
|
||||
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 (() => {
|
||||
message ("%d %d", Gstreamer.instance.camerabin_mode, (int) Gstreamer.instance.ready);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue