Add video, audio and subtitle track selection

This commit is contained in:
Rafostar
2020-09-04 23:43:51 +02:00
parent 24e84a397b
commit e76d1c9e6e
2 changed files with 191 additions and 1 deletions

View File

@@ -5,6 +5,9 @@ var Controls = GObject.registerClass({
'position-seeking-changed': { 'position-seeking-changed': {
param_types: [GObject.TYPE_BOOLEAN] param_types: [GObject.TYPE_BOOLEAN]
}, },
'track-change-requested': {
param_types: [GObject.TYPE_STRING, GObject.TYPE_INT]
},
} }
}, class ClapperControls extends Gtk.HBox }, class ClapperControls extends Gtk.HBox
{ {
@@ -44,6 +47,16 @@ var Controls = GObject.registerClass({
this.positionAdjustment = this.positionScale.get_adjustment(); this.positionAdjustment = this.positionScale.get_adjustment();
this.pack_start(this.positionScale, true, true, 0); this.pack_start(this.positionScale, true, true, 0);
this.videoTracksButton = this.addPopoverButton(
'emblem-videos-symbolic'
);
this.audioTracksButton = this.addPopoverButton(
'emblem-music-symbolic'
);
this.subtitleTracksButton = this.addPopoverButton(
'media-view-subtitles-symbolic'
);
this.volumeButton = new Gtk.ScaleButton({ this.volumeButton = new Gtk.ScaleButton({
icons: [ icons: [
'audio-volume-muted-symbolic', 'audio-volume-muted-symbolic',
@@ -98,6 +111,48 @@ var Controls = GObject.registerClass({
return button; return button;
} }
addPopoverButton(iconName, size)
{
let button = this.addButton(iconName, size);
button.popover = new Gtk.Popover({
relative_to: button
});
button.popoverBox = new Gtk.VBox({
margin_top: 4,
margin_bottom: 4,
});
button.popover.add(button.popoverBox);
button.connect('clicked', () => button.popover.popup());
return button;
}
addRadioButtons(box, array, activeId)
{
let group = null;
for(let el of array) {
let radioButton = new Gtk.RadioButton({
label: el.label,
group: group,
});
radioButton.trackType = el.type;
radioButton.trackId = el.value;
if(radioButton.trackId === activeId)
radioButton.set_active(true);
if(!group)
group = radioButton;
radioButton.connect(
'toggled', this._onTrackRadioButtonToggled.bind(this, radioButton)
);
box.add(radioButton);
}
box.show_all();
}
setDefaultWidgetBehaviour(widget) setDefaultWidgetBehaviour(widget)
{ {
widget.can_focus = false; widget.can_focus = false;
@@ -139,6 +194,18 @@ var Controls = GObject.registerClass({
} }
} }
_onTrackRadioButtonToggled(self, radioButton)
{
if(!radioButton.get_active())
return;
this.emit(
'track-change-requested',
radioButton.trackType,
radioButton.trackId
);
}
_onPositionScaleButtonPressEvent() _onPositionScaleButtonPressEvent()
{ {
this.isPositionSeeking = true; this.isPositionSeeking = true;

View File

@@ -21,6 +21,7 @@ class ClapperInterface extends Gtk.Grid
this.controlsInVideo = false; this.controlsInVideo = false;
this.lastVolumeValue = null; this.lastVolumeValue = null;
this.lastPositionValue = 0; this.lastPositionValue = 0;
this.needsTracksUpdate = true;
this.revealTime = 800; this.revealTime = 800;
this.overlay = new Gtk.Overlay(); this.overlay = new Gtk.Overlay();
@@ -60,6 +61,9 @@ class ClapperInterface extends Gtk.Grid
this.controls.connect( this.controls.connect(
'position-seeking-changed', this._onPositionSeekingChanged.bind(this) 'position-seeking-changed', this._onPositionSeekingChanged.bind(this)
); );
this.controls.connect(
'track-change-requested', this._onTrackChangeRequested.bind(this)
);
this.overlay.add(this._player.widget); this.overlay.add(this._player.widget);
} }
@@ -98,17 +102,136 @@ class ClapperInterface extends Gtk.Grid
debug(`placed controls in overlay: ${isOnVideo}`); debug(`placed controls in overlay: ${isOnVideo}`);
} }
updateMediaTracks()
{
let mediaInfo = this._player.get_media_info();
// titlebar from video title should be set from this (not implemented yet)
//let title = mediaInfo.get_title();
// we can also check if video is "live" or "seekable" (right now unused)
// it might be a good idea to hide position seek bar and disable seeking
// when playing not seekable media (not implemented yet)
//let isLive = mediaInfo.is_live();
//let isSeekable = mediaInfo.is_seekable();
let streamList = mediaInfo.get_stream_list();
let parsedInfo = {
videoTracks: [],
audioTracks: [],
subtitleTracks: []
};
for(let info of streamList) {
let type, text;
switch(info.constructor) {
case GstPlayer.PlayerVideoInfo:
type = 'video';
let fps = info.get_framerate();
text = info.get_codec() + ', ' +
+ info.get_width() + 'x'
+ info.get_height() + '@'
+ Number((fps[0] / fps[1]).toFixed(2));
break;
case GstPlayer.PlayerAudioInfo:
type = 'audio';
let codec = info.get_codec();
// This one is too long to fit nicely in UI
if(codec.startsWith('Free Lossless Audio Codec'))
codec = 'FLAC';
text = info.get_language() + ', '
+ codec + ', '
+ info.get_channels() + ' Channels';
break;
case GstPlayer.PlayerSubtitleInfo:
type = 'subtitle';
text = info.get_language();
break;
default:
debug(`unrecognized media info type: ${info.constructor}`);
break;
}
let tracksArr = parsedInfo[`${type}Tracks`];
if(!tracksArr.length)
{
tracksArr[0] = {
label: 'Disabled',
type: type,
value: -1
};
}
tracksArr.push({
label: text,
type: type,
value: info.get_index(),
});
}
for(let type of ['video', 'audio', 'subtitle']) {
let currStream = this._player[`get_current_${type}_track`]();
let activeId = (currStream) ? currStream.get_index() : -1;
if(currStream && type !== 'subtitle') {
let caps = currStream.get_caps();
debug(`${type} caps: ${caps.to_string()}`, 'LEVEL_INFO');
}
this.controls.addRadioButtons(
this.controls[`${type}TracksButton`].popoverBox,
parsedInfo[`${type}Tracks`],
activeId
);
}
}
_onTrackChangeRequested(self, trackType, trackId)
{
// reenabling audio is slow (as expected),
// so it is better to toggle mute instead
if(trackType === 'audio') {
if(trackId < 0)
return this._player.set_mute(true);
if(this._player.get_mute())
this._player.set_mute(false);
return this._player[`set_${trackType}_track`](trackId);
}
if(trackId < 0) {
// disabling video leaves last frame frozen,
// so we also hide the widget
if(trackType === 'video')
this._player.widget.hide();
return this._player[`set_${trackType}_track_enabled`](false);
}
this._player[`set_${trackType}_track`](trackId);
this._player[`set_${trackType}_track_enabled`](true);
if(trackType === 'video' && !this._player.widget.get_visible()) {
this._player.widget.show();
this._player.renderer.expose();
}
}
_onPlayerStateChanged(player, state) _onPlayerStateChanged(player, state)
{ {
switch(state) { switch(state) {
case GstPlayer.PlayerState.BUFFERING: case GstPlayer.PlayerState.BUFFERING:
break; break;
case GstPlayer.PlayerState.PAUSED:
case GstPlayer.PlayerState.STOPPED: case GstPlayer.PlayerState.STOPPED:
this.needsTracksUpdate = true;
case GstPlayer.PlayerState.PAUSED:
this.controls.togglePlayButton.image = this.controls.playImage; this.controls.togglePlayButton.image = this.controls.playImage;
break; break;
case GstPlayer.PlayerState.PLAYING: case GstPlayer.PlayerState.PLAYING:
this.controls.togglePlayButton.image = this.controls.pauseImage; this.controls.togglePlayButton.image = this.controls.pauseImage;
if(this.needsTracksUpdate) {
this.needsTracksUpdate = false;
this.updateMediaTracks();
}
break; break;
default: default:
break; break;