Skip to content

Commit

Permalink
📖 3p video player guide and explainer (ampproject#25287)
Browse files Browse the repository at this point in the history
- Includes guide for explaining when and how to build vendor-specific players.
- Updates some `VideoInterface` method descriptions.
  • Loading branch information
alanorozco authored Dec 5, 2019
1 parent 5161a02 commit d9015fe
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 16 deletions.
83 changes: 83 additions & 0 deletions spec/amp-3p-video.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Vendor-specific (3p) video player components

The generic [`amp-video`](https://go.amp.dev/c/amp-video) component plays videos directly on a page, much like a `<video>` tag.

Unfortunately, not all videos can be embedded this way. For these specialized cases, AMP provides vendor-optimized components like [`amp-youtube`](https://go.amp.dev/c/amp-youtube), [`amp-ima-video`](https://go.amp.dev/c/amp-ima-video), and [others](./amp-video-interface.md).
Internally these players load an iframe whose page communicates with the outer document to coordinate playback.

We would prefer to not have additional custom video implementations. Instead we believe, [`amp-video-iframe`](https://go.amp.dev/c/amp-video-iframe) should be used instead, since it is a generic component that can load any video document to coordinate playback.

## I want to contribute my vendor-specific player

You probaby do **_not_** need to build your own player.

The `amp-video-iframe` playback interface supports the following methods, which we believe cover the vast majority of cases AMP documents necessitate:

- `play`
- `pause`
- `mute`
- `unmute`
- `showcontrols`
- `hidecontrols`
- `fullscreenenter`
- `fullscreenexit`

`amp-video-iframe` can also [send custom namespaced analytics signals](<https://amp.dev/documentation/components/amp-video-iframe/#postanalyticsevent(eventtype[,-vars])>).

(If there is a feature missing from this list that a custom player requires, we are happy to work on extending it as necessary.)

If this is enough for your use case, you'd only need to build a playback document hosted on your server that's able to communicate with `amp-video-iframe`.
For guidance on accomplishing this, [refer to the component documentation.](https://go.amp.dev/c/amp-video-iframe)

If you _cannot_ support these playback methods, it's likely that a simple [`amp-iframe`](https://go.amp.dev/c/amp-iframe)
can embed a separate video document just fine.

### When _should_ I build a vendor-specific player?

If the API you require for communication includes methods that are **_not_** part of the `amp-video-iframe` playback interface, you might want to build your own player. (Please note that most custom analytics signals are not considered in this case,
since `amp-video-iframe` [also has plumbing for that.](<https://amp.dev/documentation/components/amp-video-iframe/#postanalyticsevent(eventtype[,-vars])>))

For example, these are some of the features that justify specific players:

- **`amp-youtube`** loads initial-frame placeholder images based on a URL scheme for perceived performance.
Its underlying implementation also loads with lower-than-default priority, since it's rather large in binary size.

- **`amp-ima-video`** embeds a generic host-less page that ships with the IMA SDK in order to transparently include standard IMA interface videos.

(Please note that most other 3p players historically exist before `amp-video-iframe` could fulfill their use case.)

## How can I build a player component?

### Understand the contribution process

You'll need to go through [the standard AMP contribution process](../CONTRIBUTING.md) when creating and submitting your component. For large features, such as a video player, you need to file an [I2I (intent-to-implement) issue](https://github.com/ampproject/amphtml/issues/new?assignees=&labels=INTENT+TO+IMPLEMENT&template=intent-to-implement--i2i-.md&title=I2I:%20%3Cyour%20change/update%3E) that describes the overall workings of your component.

Your player component is also shipped as an extension, so you should [become familiar with the process of building one.](https://github.com/ampproject/amphtml/blob/master/contributing/building-an-amp-extension.md)

### Write your implementation

#### The `VideoManager`

Every player component talks to a single [`VideoManager`](../src/service/video-manager-impl.js) via standard methods (`VideoInterface`) and events (`VideoEvents`) defined in the [AMP video interface](../src/video-interface.js).

This manager performs standard responsibilities for all videos, regardless of type:

- accessible, managed autoplay
- analytics tracking for playback
- coordination with [`amp-story`](https://go.amp.dev/c/amp-youtube)
- [docked video](https://amp.dev/documentation/components/amp-video-docking/)
- rotate-to-fullscreen

#### Interface support

Component classes should implement the [`VideoInterface`](../src/video-interface.js).
You can implement this interface entirely or partially, depending on [the video integration features you'd like to support](./amp-video-interface.md).

At the very least, you should implement `play()` and `pause()`. Likewise, playback
[actions](https://amp.dev/documentation/guides-and-tutorials/learn/amp-actions-and-events/) (like `my-element.play`) will not work when unimplemented.

Read through [the `VideoInterface` code to understand the individual effects of leaving each method unimplemented.](../src/video-interface.js)

#### Reference

Existing implementations for [`amp-youtube.js`](../extensions/amp-youtube/0.1/amp-youtube.js) and [`amp-video-iframe.js`](../extensions/amp-video-iframe/0.1/amp-video-iframe.js) are good starter examples for implementation details. They use several standard utilities for creating the video frame and communicating with the `VideoManager`.
50 changes: 34 additions & 16 deletions src/video-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,32 @@ export class VideoInterface {
isInteractive() {}

/**
* Current playback time in seconds at time of trigger
* Current playback time in seconds at time of trigger.
*
* This is used for analytics metadata.
*
* @return {number}
*/
getCurrentTime() {}

/**
* Total duration of the video in seconds
*
* This is used for analytics metadata.
*
* @return {number}
*/
getDuration() {}

/**
* Get a 2d array of start and stop times that the user has watched.
* This is used for analytics metadata.
* @return {!Array<Array<number>>}
*/
getPlayedRanges() {}

/**
* Plays the video..
* Plays the video.
*
* @param {boolean} unusedIsAutoplay Whether the call to the `play` method is
* triggered by the autoplay functionality. Video players can use this hint
Expand All @@ -84,55 +91,66 @@ export class VideoInterface {

/**
* Mutes the video.
* Implementation is required for autoplay and mute/unmute controls on docked
* video.
*/
mute() {}

/**
* Unmutes the video.
* Implementation is required for autoplay and mute/unmute controls on docked
* video.
*/
unmute() {}

/**
* Makes the video UI controls visible.
*
* AMP will not call this method if `controls` attribute is not set.
*
* Implementation is required for docked video.
*/
showControls() {}

/**
* Hides the video UI controls.
*
* AMP will not call this method if `controls` attribute is not set.
*
* Implementation is required for docked video.
*/
hideControls() {}

/**
* Returns video's meta data (artwork, title, artist, album, etc.) for use
* with the Media Session API
* artwork (Array): URL to the poster image (preferably a 512x512 PNG)
* title (string): Name of the video
* artist (string): Name of the video's author/artist
* album (string): Name of the video's album if it exists
* @return {!./mediasession-helper.MetadataDef|undefined} metadata
* - artwork (Array): URL to the poster image (preferably a 512x512 PNG)
* - title (string): Name of the video
* - artist (string): Name of the video's author/artist
* - album (string): Name of the video's album if it exists
*/
getMetadata() {}

/**
* If this returns true then it will be assumed that the player implements
* a feature to enter fullscreen on device rotation internally, so that the
* video manager does not override it. If not, the video manager will
* implement this feature automatically for videos with the attribute
* `rotate-to-fullscreen`.
* If returning true, it's assumed that the embedded video document internally
* implements a feature to enter fullscreen on device rotation, so that the
* VideoManager does not override it.
*
* Otherwise, the feature is implemented automatically when using the
* `rotate-to-fullscreen` attribute.
*
* @return {boolean}
*/
preimplementsAutoFullscreen() {}

/**
* If this returns true then it will be assumed that the player implements
* the MediaSession API internally so that the video manager does not override
* it. If not, the video manager will use the metadata variable as well as
* inferred meta-data to update the video's Media Session notification.
* If returning true, it's assumed that the embedded video document internally
* implements the MediaSession API internally so that the VideoManager won't
* replace it.
*
* Otherwise provided and inferred metadata are used to update the video's
* Media Session.
*
* @return {boolean}
*/
Expand All @@ -155,7 +173,7 @@ export class VideoInterface {
fullscreenExit() {}

/**
* Returns whether the video is currently in fullscreen mode or not
* Returns whether the video is currently in fullscreen mode or not.
* @return {boolean}
*/
isFullscreen() {}
Expand Down

0 comments on commit d9015fe

Please sign in to comment.