I’m trying to add basic video playback to a Gnome app. This is my first Gnome app, and I’m using GJS + GTK4.
Ultimately there’ll be about 10 video files (local mp4 files) throughout the app. I have the GTK video component (GTKvideo) wrapped with my own widget (“FbrVideoWidget”). For now, I have one test instance of the widget on the initial view of the app. As far as I can tell, the widget renders (I just see black, which I believe is the video player without controls or a file).
PROBLEM
When attempting to apply the file to the video player instance, I don’t see any log warnings about success/failure, and when logging the video instance, it still appears like an empty object (before and after). So I don’t know if I’m not accessing the video element correctly or not passing a file correctly.
Side note, after the file attempt, I’m also trying to start playback programmatically, and this does produce a log error:
(org.example.filebrowser:2): Gtk-CRITICAL **: 13:04:54.116: Media stream of type 'GtkNoMediaFile' does not implement GtkMediaStream::play
This actually seems like a good sign to me because I interpret this as the video element is actually loaded but it doesn’t have a proper video source.
QUESTION Given my requirement (playing local mp4 files), where should the mp4 files be kept?
QUESTION Regardless of where the file lives, do I absolutely need to register them somewhere? I suppose this depends on how I’m trying to get the files in the app.
QUESTION How do you get-and-pass a local file into the video player, via a widget wrapping?
I see different methods and no clear answer.
// Get path to mp4 file in my OS home dir
const filepath = GLib.build_filenamev([GLib.get_home_dir(), value]);
log('filepath is: ', filepath);
// 1 - Apply video file: new_for_uri
this._videoActual.file = Gio.File.new_for_uri(filepath);
// 2 - Apply video file: new_for_path
//this._videoActual.file = Gio.File.new_for_path(file);
/// --- or ---
// 3 - Apply video file: set_resource
// Tried accessing a resource defined in data/ui/
// this._videoActual.set_resource('/org/example/filebrowser/ui/file_example_MP4_640_3MG.mp4')
RELEVANT CODE
I’m excluding registry files.
/src/VideoWidget.js
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
import Gio from "gi://Gio";
import GLib from 'gi://GLib';
export const VideoWidget = GObject.registerClass({
GTypeName: 'FbrVideoWidget',
CssName: 'video',
Template: 'resource:///org/example/filebrowser/ui/VideoWidget.ui',
InternalChildren: ['videoActual'],
Properties: {
VideoFile: GObject.ParamSpec.string(
'video-file', // name
'Video File', // nick
'The text displayed by the widget', // blurb
GObject.ParamFlags.READWRITE, // flags
'' // default value
),
}
}, class extends Gtk.Widget {
constructor(params={}) {
super(params);
log('Video mounted');
}
get videoFile() {
log('get videoFile(): ', this._videoFile);
return this._videoFile;
}
set videoFile(value) {
// Setting local stuff
log('set videoFile() value: ', value);
if (this._videoFile === value) return;
this._videoFile = value;
// Getting filepath of requested video file
const filepath = GLib.build_filenamev([GLib.get_home_dir(), value]);
log('filepath is: ', filepath);
// Apply file to video player using new_for_uri
this._videoActual.file = Gio.File.new_for_uri(filepath);
// Apply file to video player using new_for_path
//this._videoActual.file = Gio.File.new_for_path(file);
// Or trying to use declared app resource (declared in `org.example.filebrowser.data.gresource.xml`):
this._videoActual.set_resource('/org/example/filebrowser/ui/file_example_MP4_640_3MG.mp4')
//this._videoActual.set_autoplay(true);
// LOG ERROR OCCURS HERE
this._videoActual.media_stream.play();
this.notify('video-file');
}
});
/data/ui/VideoWidget.ui
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="FbrVideoWidget">
<property name="layout-manager">
<object class="GtkBoxLayout">
<property name="orientation">vertical</property>
</object>
</property>
<object class="GtkBox">
<property name="orientation">1</property>
<property name="halign">3</property>
<child>
<object class="GtkVideo" id="videoActual">
<property name="file" />
<property name="autoplay">true</property>
<property name="loop">true</property>
<property name="width-request">540</property>
<property name="height-request">540</property>
</object>
</child>
</object>
</template>
</interface>
Test instance of the video widget (initial view in the app)
<property name="child">
<object class="FbrVideoWidget" id="videoTest">
<property name="video-file">file_example_MP4_640_3MG.mp4</property>
</object>
</property>
App resources declared in org.example.filebrowser.data.gresource.xml
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/example/filebrowser">
<file>css/style.css</file>
<file>ui/VideoWidget.ui</file>
<file>ui/WelcomeWidget.ui</file>
<file>ui/Window.ui</file>
<file>ui/file_example_MP4_640_3MG.mp4</file>
</gresource>
<gresource prefix="/org/example/filebrowser/icons/scalable/actions">
<file alias="filebrowser-welcome-symbolic.svg">icons/welcome-symbolic.svg</file>
</gresource>
</gresources>