Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions src/Library/demos/Camera/main.blp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Gtk 4.0;
using Adw 1;

Adw.StatusPage {
title: _("Camera");
description: _("Access the Camera");
margin-top: 48;

Box {
orientation: vertical;
halign: center;

Picture output {
margin-bottom: 30;
width-request: 360;
height-request: 240;
}

Button button {
label: _("Access Camera");
margin-bottom: 42;
halign: center;
styles ["suggested-action"]
}

LinkButton {
label: "API Reference";
uri: "https://libportal.org/method.Portal.access_camera.html";
}
}
}


98 changes: 98 additions & 0 deletions src/Library/demos/Camera/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import Gio from "gi://Gio";
import Xdp from "gi://Xdp";
import XdpGtk from "gi://XdpGtk4";
import GObject from "gi://GObject";
import Gst from "gi://Gst";

Gst.init(null);
Gio._promisify(Xdp.Portal.prototype, "access_camera", "access_camera_finish");

const portal = new Xdp.Portal();
const parent = XdpGtk.parent_new_gtk(workbench.window);

const output = workbench.builder.get_object("output");
const button = workbench.builder.get_object("button");

button.connect("clicked", () => {
accessCamera().catch(logError);
});

async function accessCamera() {
if (!portal.is_camera_present()) {
console.log("No Camera detected");
return;
}

const success = await portal.access_camera(
parent,
Xdp.CameraFlags.NONE,
null,
);

if (!success) {
console.log("Permission denied");
return;
}

await handleCamera();
}

async function handleCamera() {
const fd_pipewire_remote = portal.open_pipewire_remote_for_camera();
console.log("Pipewire remote opened for camera");

// Create the pipeline
const pipeline = new Gst.Pipeline();

// Create elements
const source = Gst.ElementFactory.make("pipewiresrc", "source");
const queue = Gst.ElementFactory.make("queue", "queue"); // add a queue element
const paintable_sink = Gst.ElementFactory.make(
"gtk4paintablesink",
"paintable_sink",
);
const glsinkbin = Gst.ElementFactory.make("glsinkbin", "glsinkbin");

// Set up and Link Pipeline
source.set_property("fd", fd_pipewire_remote); // fd_pipewire_remote is the file descriptor obtained from libportal
glsinkbin.set_property("sink", paintable_sink);

pipeline.add(source);
pipeline.add(queue);
pipeline.add(glsinkbin);
source.link(queue);
queue.link(glsinkbin);

const paintable = new GObject.Value();
paintable_sink.get_property("paintable", paintable);
output.paintable = paintable.get_object();

// Start the pipeline
pipeline.set_state(Gst.State.PLAYING);

// Handle cleanup on application exit
output.connect("destroy", () => {
pipeline.set_state(Gst.State.NULL);
});

// Set up the bus
const bus = pipeline.get_bus();
bus.add_signal_watch();
bus.connect("message", (self, message) => {
// Check the message type
const message_type = message.type;

// Handle different message types
switch (message_type) {
case Gst.MessageType.ERROR: {
const errorMessage = message.parse_error();
console.error(errorMessage[0].toString());
break;
}
case Gst.MessageType.EOS: {
console.log("End of stream");
break;
}
}
});
}
10 changes: 10 additions & 0 deletions src/Library/demos/Camera/main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "Camera",
"category": "platform",
"description": "Access the Camera",
"panels": [
"code",
"preview"
],
"autorun": true
}