Skip to content

Commit

Permalink
Merge pull request #120 from clappr/feature/pip_listeners
Browse files Browse the repository at this point in the history
Add Picture in Picture events in HTML5Video
  • Loading branch information
jhonatangcavalcanti authored Nov 23, 2023
2 parents 66bd830 + 3544562 commit 362f5f0
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/base/events/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,16 @@ Events.PLAYER_VOLUMEUPDATE = 'volumeupdate'
Events.PLAYER_SUBTITLE_AVAILABLE = 'subtitleavailable'

// Playback Events
/** Fired when picture-in-picture mode is entered
*
* @event PLAYBACK_PIP_ENTER
*/
Events.PLAYBACK_PIP_ENTER = 'playback:picture-in-picture:enter'
/** Fired when picture-in-picture mode is exited
*
* @event PLAYBACK_PIP_EXIT
*/
Events.PLAYBACK_PIP_EXIT = 'playback:picture-in-picture:exit'
/**
* Fired when the playback is downloading the media
*
Expand Down Expand Up @@ -763,6 +773,17 @@ Events.CONTAINER_MOUSE_LEAVE = 'container:mouseleave'
Events.CONTAINER_MOUSE_UP = 'container:mouseup'
Events.CONTAINER_MOUSE_DOWN = 'container:mousedown'

/**
* Fired when the container enters on Picture-in-Picture mode
* @event CONTAINER_PIP_ENTER
*/
Events.CONTAINER_PIP_ENTER = 'container:picture-in-picture:enter'
/**
* Fired when the container exits from Picture-in-Picture mode
* @event CONTAINER_PIP_EXIT
*/
Events.CONTAINER_PIP_EXIT = 'container:picture-in-picture:exit'

/**
* Fired when the container seeks the video
*
Expand Down
21 changes: 21 additions & 0 deletions src/base/playback/playback.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ export default class Playback extends UIObject {
return null
}

/**
* checks if picture in picture mode is active.
* @property isPiPActive
* @type {Boolean} `true` if the current playback is in picture in picture mode, otherwise `false`
*/
get isPiPActive() {
return false
}

/**
* @method constructor
* @param {Object} options the options object
Expand Down Expand Up @@ -122,6 +131,18 @@ export default class Playback extends UIObject {
*/
stop() {}

/**
* enter in picture in picture mode
* @method enterPiP
*/
enterPiP() {}

/**
* exit from picture in picture mode
* @method exitPiP
*/
exitPiP() {}

/**
* seeks the playback to a given `time` in seconds
* @method seek
Expand Down
28 changes: 28 additions & 0 deletions src/components/container/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ export default class Container extends UIObject {
return this.playback.currentAudioTrack
}

/**
* returns the picture-in-picture state.
* @type {Boolean}
*/
get isPiPActive() {
return this.playback.isPiPActive
}

/**
* it builds a container
* @method constructor
Expand Down Expand Up @@ -174,6 +182,8 @@ export default class Container extends UIObject {
* | play |
* | pause |
* | error |
* | pip_enter |
* | pip_exit |
*
* ps: the events usually translate from PLABACK_x to CONTAINER_x, you can check all the events at `Event` class.
*
Expand Down Expand Up @@ -204,6 +214,8 @@ export default class Container extends UIObject {
this.listenTo(this.playback, Events.PLAYBACK_SUBTITLE_CHANGED, this.subtitleChanged)
this.listenTo(this.playback, Events.PLAYBACK_AUDIO_AVAILABLE, this.audioAvailable)
this.listenTo(this.playback, Events.PLAYBACK_AUDIO_CHANGED, this.audioChanged)
this.listenTo(this.playback, Events.PLAYBACK_PIP_ENTER, this.onEnterPiP)
this.listenTo(this.playback, Events.PLAYBACK_PIP_EXIT, this.onExitPiP)
}

subtitleAvailable() {
Expand Down Expand Up @@ -453,6 +465,14 @@ export default class Container extends UIObject {
this.trigger(Events.CONTAINER_STATE_BUFFERFULL, this.name)
}

onEnterPiP() {
this.trigger(Events.CONTAINER_PIP_ENTER, this.name)
}

onExitPiP() {
this.trigger(Events.CONTAINER_PIP_EXIT, this.name)
}

/**
* adds plugin to the container
* @method addPlugin
Expand Down Expand Up @@ -503,6 +523,14 @@ export default class Container extends UIObject {
this.trigger(Events.CONTAINER_MOUSE_DOWN)
}

enterPiP() {
this.playback.enterPiP()
}

exitPiP() {
this.playback.exitPiP()
}

settingsUpdate() {
this.settings = this.playback.settings
this.trigger(Events.CONTAINER_SETTINGSUPDATE)
Expand Down
33 changes: 32 additions & 1 deletion src/playbacks/html5_video/html5_video.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export default class HTML5Video extends Playback {
'seeked': '_onSeeked',
'stalled': '_handleBufferingEvents',
'timeupdate': '_onTimeUpdate',
'waiting': '_onWaiting'
'waiting': '_onWaiting',
'enterpictureinpicture': '_onEnterPiP',
'leavepictureinpicture': '_onExitPiP'
}
}

Expand All @@ -96,6 +98,10 @@ export default class HTML5Video extends Playback {
return this._isBuffering
}

get isPiPActive() {
return this.el === document.pictureInPictureElement
}

get isLive() {
return this.getPlaybackType() === Playback.LIVE
}
Expand Down Expand Up @@ -457,6 +463,30 @@ export default class HTML5Video extends Playback {
this.trigger(Events.PLAYBACK_ENDED, this.name)
}

_onEnterPiP() {
this.trigger(Events.PLAYBACK_PIP_ENTER, this.name)
}

_onExitPiP() {
this.trigger(Events.PLAYBACK_PIP_EXIT, this.name)
}

enterPiP() {
this.el.requestPictureInPicture().then(() => {
Log.info(this.name, 'enter PIP success')
}).catch(e => {
Log.warn(this.name, 'enter PIP failed', e)
})
}

exitPiP() {
document.exitPictureInPicture().then(() => {
Log.info(this.name, 'exit PIP success')
}).catch(e => {
Log.warn(this.name, 'exit PIP failed', e)
})
}

// The playback should be classed as buffering if the following are true:
// - the ready state is less then HAVE_FUTURE_DATA or the playhead isn't moving and it should be
// - the media hasn't "ended",
Expand Down Expand Up @@ -499,6 +529,7 @@ export default class HTML5Video extends Playback {
this._destroyed = true
this.handleTextTrackChange && this.el.textTracks.removeEventListener('change', this.handleTextTrackChange)
this.$el.off('contextmenu')
this.isPiPActive && this.exitPiP()
super.destroy()
this.el.removeAttribute('src')
this.el.load() // load with no src to stop loading of the previous source and avoid leaks
Expand Down

0 comments on commit 362f5f0

Please sign in to comment.