Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CameraView: Allow to change the camera resolution and framerate #224

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
17 changes: 12 additions & 5 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ public class Camera.MainWindow : Hdy.ApplicationWindow {
public const string ACTION_FULLSCREEN = "fullscreen";
public const string ACTION_TAKE_PHOTO = "take_photo";
public const string ACTION_RECORD = "record";
public const string ACTION_CHANGE_CAPS = "change-caps";

private const GLib.ActionEntry[] ACTION_ENTRIES = {
{ACTION_FULLSCREEN, on_fullscreen},
{ACTION_TAKE_PHOTO, on_take_photo},
{ACTION_RECORD, on_record, null, "false", null},
{ACTION_CHANGE_CAPS, on_change_caps, "u", "uint32 0", null},
};

private uint configure_id;
Expand Down Expand Up @@ -108,14 +110,14 @@ public class Camera.MainWindow : Hdy.ApplicationWindow {
}
});

var window_handle = new Hdy.WindowHandle ();
window_handle.add (header_bar);

var grid = new Gtk.Grid ();
grid.attach (header_bar, 0, 0);
grid.attach (window_handle, 0, 0);
grid.attach (overlay, 0, 1);

var window_handle = new Hdy.WindowHandle ();
window_handle.add (grid);

add (window_handle);
add (grid);

timer_running = false;
camera_view.camera_added.connect (header_bar.add_camera_option);
Expand Down Expand Up @@ -164,6 +166,11 @@ public class Camera.MainWindow : Hdy.ApplicationWindow {
}
}

private void on_change_caps (GLib.SimpleAction action, GLib.Variant? parameter) {
camera_view.change_caps (parameter.get_uint32 ());
change_action_state (ACTION_CHANGE_CAPS, parameter);
}

public override bool configure_event (Gdk.EventConfigure event) {
if (configure_id != 0) {
GLib.Source.remove (configure_id);
Expand Down
80 changes: 79 additions & 1 deletion src/Widgets/CameraView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ public class Camera.Widgets.CameraView : Gtk.Stack {
Gtk.Widget gst_video_widget;

private Gst.Pipeline pipeline;
private dynamic Gst.Element capsfilter;
private Gst.Element tee;
private Gst.Video.ColorBalance color_balance;
private Gst.Video.Direction? hflip;
private Gst.Bin? record_bin;
private Gst.Device? current_device = null;
private GLib.Menu resolution_menu;

public uint n_cameras {
get {
Expand Down Expand Up @@ -90,6 +92,20 @@ public class Camera.Widgets.CameraView : Gtk.Stack {
""
);

resolution_menu = new GLib.Menu ();
var popover = new Gtk.Popover.from_model (this, resolution_menu);

events |= Gdk.EventMask.BUTTON_RELEASE_MASK;
button_release_event.connect ((event) => {
if (((Gdk.EventButton) event).button != Gdk.BUTTON_SECONDARY) {
return false;
}

popover.pointing_to = {(int)((Gdk.EventButton) event).x, (int)((Gdk.EventButton) event).y};
popover.popup ();
return true;
});

add (status_box);
add (no_device_view);
monitor.get_bus ().add_watch (GLib.Priority.DEFAULT, on_bus_message);
Expand Down Expand Up @@ -168,6 +184,37 @@ public class Camera.Widgets.CameraView : Gtk.Stack {
Gst.Debug.BIN_TO_DOT_FILE (pipeline, Gst.DebugGraphDetails.VERBOSE, "changing");
}

var caps = camera.get_caps ();
resolution_menu.remove_all ();
for (uint i = 0; i < caps.get_size (); i++) {
unowned var s = caps.get_structure (i);
int w, h, num = 0, den = 1;
if (s.get ("width", typeof (int), out w,
"height", typeof (int), out h)) {
unowned GLib.Value? fraction = s.get_value ("framerate");
if (fraction.holds (typeof (Gst.Fraction))) {
num = Gst.Value.get_fraction_numerator (fraction);
den = Gst.Value.get_fraction_denominator (fraction);
} else if (fraction.holds (typeof (Gst.FractionRange))) {
var range_max = Gst.Value.get_fraction_range_max (fraction);
num = Gst.Value.get_fraction_numerator (range_max);
den = Gst.Value.get_fraction_denominator (range_max);
} else if (fraction.holds (typeof (Gst.ValueList))) {
unowned GLib.Value? val = Gst.ValueList.get_value (fraction, 0);
num = Gst.Value.get_fraction_numerator (val);
den = Gst.Value.get_fraction_denominator (val);
} else {
debug ("Unknown fraction type: %s", fraction.type_name ());
continue;
}

resolution_menu.append (
"%d×%d (%0.f fps)".printf (w, h, (double)num / (double)den),
GLib.Action.print_detailed_name ("win.change-caps", new GLib.Variant.uint32 (i))
);
}
}

create_pipeline (camera);
current_device = camera;
}
Expand Down Expand Up @@ -195,6 +242,7 @@ public class Camera.Widgets.CameraView : Gtk.Stack {

var device_src = camera.create_element (VIDEO_SRC_NAME);
pipeline = (Gst.Pipeline) Gst.parse_launch (
"capsfilter name=capsfilter ! " +
"decodebin name=decodebin ! " +
"videoflip method=horizontal-flip name=hflip ! " +
"videobalance name=balance ! " +
Expand All @@ -206,7 +254,8 @@ public class Camera.Widgets.CameraView : Gtk.Stack {
);

pipeline.add (device_src);
device_src.link (pipeline.get_by_name ("decodebin"));
capsfilter = pipeline.get_by_name ("capsfilter");
device_src.link (capsfilter);
tee = pipeline.get_by_name ("tee");
hflip = (pipeline.get_by_name ("hflip") as Gst.Video.Direction);
color_balance = (pipeline.get_by_name ("balance") as Gst.Video.ColorBalance);
Expand All @@ -233,6 +282,23 @@ public class Camera.Widgets.CameraView : Gtk.Stack {

gst_video_widget = gtksink.widget;

capsfilter.get_static_pad ("src").add_probe (Gst.PadProbeType.EVENT_BOTH, (pad, info) => {
unowned Gst.Event? event = info.get_event ();
if (event.type == Gst.EventType.CAPS) {
unowned Gst.Caps new_caps;
event.parse_caps (out new_caps);
var camera_caps = camera.get_caps ();
for (uint i = 0; i < camera_caps.get_size (); i++) {
var sec_caps = camera_caps.copy_nth (i);
if (new_caps.is_subset (sec_caps)) {
get_action_group ("win").change_action_state (MainWindow.ACTION_CHANGE_CAPS, new GLib.Variant.uint32 (i));
return Gst.PadProbeReturn.REMOVE;
}
}
}
return Gst.PadProbeReturn.OK;
});

add (gst_video_widget);
gst_video_widget.show ();

Expand All @@ -252,6 +318,18 @@ public class Camera.Widgets.CameraView : Gtk.Stack {
color_balance.set_property ("contrast", contrast);
}

public void change_caps (uint caps_index) {
var caps = current_device.get_caps ();
if (caps_index >= caps.get_size ()) {
return;
}

unowned var s = caps.get_structure (caps_index);
var new_caps = new Gst.Caps.empty ();
new_caps.append_structure (s.copy ());
capsfilter.caps = new_caps;
}

public void take_photo () {
if (recording || pipeline == null) {
return;
Expand Down