Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/Eyevinn/media-event-filter

Interpreter for HTML5 media events
https://github.com/Eyevinn/media-event-filter

events html5 library mediasource mse typescript video

Last synced: about 1 month ago
JSON representation

Interpreter for HTML5 media events

Awesome Lists containing this project

README

        

# Media Event Filter

A dependecy free, tiny package for producing a sane default interpretation of playback events from a [HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement).

## Why

The HTML5 video element does not produce a consistent set of events that correspond to the updates required by trackers and user interfaces, like `loaded`, `buffering` or `seeking`.

Different player engines built on top of the Media Source Extension all have their own implementations for listening to events.

This filter aims to provide a single source of truth that can be used across player engines and browser native playback.

Adheres to the [Eyevinn Player Analytics Specification](https://github.com/Eyevinn/player-analytics-specification).

## Overview

```typescript
/** Loading of stream is complete, playback is ready to start */
/** It is now safe to let the user press a play button */
"loaded";
/** A seek has started */
"seeking";
/** A seek has ended */
"seeked";
/** Buffering has started */
/** Does not trigger during seek, cancelled by a seek */
"buffering";
/** Buffering has ended */
/** Does not trigger during seek */
"buffered";
/** A request to start playing again has been made */
"play";
/** The stream has started playing after loading completed
* OR the stream has started playing after the stream was previously paused */
"playing";
/** The stream has been paused */
"pause";
/** The end of the stream was reached */
"ended";
/** A timeupdate event */
"timeupdate";
```

## Limitations

Does not support native HTML5 MSE controls (``). It works, but event sequence will not strictly follow EPAS in all browsers.

## Usage

`npm install @eyevinn/media-event-filter`

`yarn add @eyevinn/media-event-filter`

Example of creating and listening to the event filter.

```typescript
import {
getMediaEventFilter,
FilteredMediaEvent,
} from "@eyevinn/media-event-filter";

const videoElement = document.createElement("video");

// Using a switch statement

const mediaEventFilter = getMediaEventFilter({
mediaElement: videoElement,
callback: (event: FilteredMediaEvent) => {
switch (event) {
case FilteredMediaEvent.LOADED:
// handle loaded
break;
case FilteredMediaEvent.BUFFERING:
// handle buffering
break;
case FilteredMediaEvent.BUFFERED:
// handle buffered
// ...
default:
break;
}
},
});

// Call when done
mediaEventFilter.teardown();
```

```typescript
// Object notation can also be used

const handlers = {
[FilteredMediaEvent.LOADED]: () => {
/* handle loaded */
},
[FilteredMediaEvent.BUFFERING]: () => {
/* handle buffering */
},
[FilteredMediaEvent.BUFFERED]: () => {
/* handle buffered */
},
// ...
};

const mediaEventFilter = getMediaEventFilter({
mediaElement: videoElement,
callback: (event: FilteredMediaEvent) => handlers[event]?.(),
});
```

```typescript
// It is safe to use destructuring

const { teardown } = getMediaEventFilter({
/* ... */
});

teardown();
```

### Sample Shaka + React Component

The filter can be used to easily build a React UI on top of Shaka.

A barebones sample integration (see it on [codepen](https://codepen.io/atlimar/pen/wvEmpXM?editors=1010)):

```javascript
import { useCallback, useEffect, useMemo, useRef, useState } from "React";
import shaka from "shaka-player";
import {
FilteredMediaEvent,
getMediaEventFilter,
} from "@eyevinn/media-event-filter";

const PlayerComponent = ({ videoUrl }) => {
const videoRef = useRef(null);
const [playing, setPlaying] = useState(false);
const [loading, setLoading] = useState(true);
const [blocked, setBlocked] = useState(false);

useEffect(() => {
if (!videoUrl || !videoRef.current) return () => {};

const eventFilter = getMediaEventFilter({
mediaElement: videoRef.current,
// add your state handlers here
callback: (event) => {
switch (event) {
case FilteredMediaEvent.LOADED:
setLoading(false);

// attempt autoplay
videoRef.current.play().catch((e) => {
// catch autoplay block
if (e.name.indexOf("NotAllowedError") > -1) {
setBlocked(true);
}
});
break;
case FilteredMediaEvent.PLAYING:
// reset autplay blocked
setBlocked(false);
// we're playing!
setPlaying(true);
break;
case FilteredMediaEvent.ENDED:
case FilteredMediaEvent.PAUSE:
setPlaying(false);
break;
default:
break;
}
},
});

const player = new shaka.Player(videoRef.current);

// Add configuration if needed
// player.configure()

player
// start loading the stream
.load(videoUrl)
// catch errors during load
.catch(console.error);

// Kill player when unmounted
return () => {
player.destroy();
eventFilter.teardown();
};
}, [videoUrl, videoRef]);

const play = useCallback(() => {
if (!videoRef.current) return;

videoRef.current.play();
}, [videoRef]);

const pause = useCallback(() => {
if (!videoRef.current) return;

videoRef.current.pause();
}, [videoRef]);

return (


{loading &&

Video is Loading

}

{!loading &&
(playing ? (

Pause

) : (

Play

))}

{blocked &&

Autoplay blocked, please start playback manually

}



);
};
```

```javascript
// Use it:

```

## Benefits

Get a single source of truth for playback events regardless of engine (Shaka, Hls.js, DashJS, native) or browser (Chrome, Firefox, Safari).

Pipe the events directly into your UI state management for a reliable source of truth of playback state updates.

The event sequence map directly to popular tracking providers and playback SDKs without further filtering, like Youbora, Comscore, Yospace, or Nielsen. _If yours is also supported we welcome PRs to update this list!_

Compatible with [EPAS](https://github.com/Eyevinn/player-analytics-specification).

## Events

A description of events and their sequencing.

### Example Sequences

```typescript
LOADED; // playback is ready to start
PLAYING; // playback started
SEEKING; // seek requested
PAUSED; // manual pause
PLAY; // manual play
SEEKED; // seek finished
PLAYING; // video is rolling again
BUFFERING; // unable to continue playing due to missing buffer
BUFFERED; // buffer ended by incoming seek request
SEEKING; // seek requested
SEEKED; // seek finished
PAUSED; // manual pause
PLAY; // manual play
PLAYING; // video is rolling again
ENDED; // video reached the end
```

### loaded

The initial load of the video has completed, and it is ready to start playing.

No other event can trigger before loaded.

### seeking

Seeking has started.

If buffering is ongoing buffered will be triggered prior to seeking.

Buffer events can not trigger during a seek.

Can not trigger before loaded.

### seeked

Seeking has ended.

Can not trigger before loaded.

Can not trigger without a preceding seeking event.

### buffering

Buffering has started.

Can not trigger during a seek.

Can not trigger before loaded.

### buffered

Buffering has ended, or was interrupted by a seek.

Can not trigger during a seek.

Can not trigger without a preceding buffering event.

### play

Playback has been requested.

Can not trigger before loaded.

Can not trigger if video was not previously paused.

### playing

Playback has started.

Can not trigger before loaded.

Can not trigger during seeking.

Can not trigger during buffering.

Can not trigger if video was not previously paused.

A play requested during seeking or buffering will trigger playing after the seek or buffer has finished.

### pause

Playback has been paused.

Seeking or buffering do not count as pausing.

Can not trigger before loaded.

### timeupdate

A timeupdate event

Can not trigger before loaded.

### ended

The player has reached the end of a stream.

To allow restarting the stream after the end of stream has been reached set `allowResumeAfterEnded` to `true`.

## Contributing

Contributions to improve compatibility with or add support for different engines, tracking solutions, and browsers are welcome.

### Git Ways of Working

The project uses feature branches, and a [rebase merge strategy](https://www.atlassian.com/git/tutorials/merging-vs-rebasing).

Make sure you have `git pull` set to rebase mode:

`git config pull.rebase true`

Optionally, you can add the `--global` flag to the above command.

To start working on a new feature: `git checkout `.

As the project uses semantic-release to **automatically generate release notes** based on commits, it is important to follow some rules when committing.

This project uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary).

Read [Using Git with Discipline](https://drewdevault.com/2019/02/25/Using-git-with-discipline.html).

Read [How to Write a Commit Message](https://chris.beams.io/posts/git-commit/).

A commit should:

- contain a single change set (smaller commits are better)
- pass tests, linting, and typescript checks
- not be broken

Along with enabling time saving automation, it enables extremely powerful debug workflows via [git bisect](https://git-scm.com/docs/git-bisect), making bug hunting a matter of minutes instead of days. There are a number of articles out there on the magic of bisecting.

Basic structure of a commit message:

```
[optional scope]:

[optional body]

[optional footer]
```

For automated release notes to work well, try to describe what was added or changed, instead of describing what the code does. Example:

`fix(seek): rewrite calculation in seek module` `// bad, the consumer does not know what issue this fixes`

`fix(seek): stop player from freezing after seek` `// good, the consumer understands what is now working again`

### Develop

To start a dev server: `pnpm dev`, check `demo` and `public/index.html` for details.

Familiarity with the HTML5 video standard, shaka, hlsjs, or other engines
is recommended before contributing.

https://www.w3.org/TR/2011/WD-html5-20110113/video.html

https://html.spec.whatwg.org/multipage/media.html

https://html.spec.whatwg.org/multipage/media.html#mediaevents

Tested with shaka 2.5.X - 4.X.X

Tested with native video in Safari

Tested with hls.js

Tested on Safari 13.1+, Firefox, Chrome, Edge

Tested on OSX, Windows, Linux

### Releasing

Releases are triggered via a github action that will automatically increment the version and write a changelog based on commits.

Manual releases can be made by running `pnpm release`.

# Support

Join our [community on Slack](http://slack.streamingtech.se) where you can post any questions regarding any of our open source projects. Eyevinn's consulting business can also offer you:

- Further development of this component
- Customization and integration of this component into your platform
- Support and maintenance agreement

Contact [[email protected]](mailto:[email protected]) if you are interested.

# About Eyevinn Technology

[Eyevinn Technology](https://www.eyevinntechnology.se) is an independent consultant firm specialized in video and streaming. Independent in a way that we are not commercially tied to any platform or technology vendor. As our way to innovate and push the industry forward we develop proof-of-concepts and tools. The things we learn and the code we write we share with the industry in [blogs](https://dev.to/video) and by open sourcing the code we have written.

Want to know more about Eyevinn and how it is to work here. Contact us at [email protected]!