Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load text tracks from dash into video.js #135

Merged
merged 4 commits into from
May 11, 2017

Conversation

justinanastos
Copy link
Contributor

@justinanastos justinanastos commented Jan 17, 2017

As of video.js v5.14, video.js no longer supports native subtitles on anything besides Safari. For Chrome, this means there is no subtitle support. This will wait for dash.js to load all the text tracks and then add them to video.js if we're using a version that does not attempt to use native subtitles. Audio tracks will also be added.


Todo


Wait for videojs/video.js#4131 to be merged. Add that version as an explicit dependency (maybe change it to a peerDependency and devDependency since we don't actually need it here?

This fix depends on videojs@^v5.18

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from 7f3cfc8 to d774648 Compare January 17, 2017 22:39
@chemoish
Copy link
Member

chemoish commented Jan 18, 2017

I might look into generating cues before calling addRemoteTextTrack, not sure if this will effect anything, but last time I was in TextTrack I did notice a few setTimeout and Events that caused race conditions (just to be aware).


Somethings to consider in this implementation:

  • How to manage the selected track? (haven't done a deep dive with managing initial track selection modes, but I did notice that embedded captions were being defaulted by the dash.js reference player)
  • Apparently there are a few types of dash.js tracks that can be embedded, they can also have different roles, I am not sure how that would effect this implementation (since this looks like it only manages captions)—fyi, there isn't a 1:1 list of TextTracks from dash.js to HTML5 spec.

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch 2 times, most recently from c74b11b to cfe82ae Compare January 18, 2017 20:21
@justinanastos justinanastos changed the title Use emulated text tracks Load text and audio tracks from dash into video.js Jan 18, 2017
@justinanastos
Copy link
Contributor Author

@chemoish

I might look into generating cues before calling addRemoteTextTrack, not sure if this will effect anything, but last time I was in TextTrack I did notice a few setTimeout and Events that caused race conditions (just to be aware).

I'm new to working with video.js's implementation, but the addRemoteTextTrack function appears to create the HTMLTrackElement element and I need that to add the cues.

Stepping through the Html5 tech implementation related to addRemoteTextTrack, (https://github.com/videojs/video.js/blob/master/src/js/tech/html5.js#L712-L738), I didn't see any Events or setTimeouts (at least not in v15.1). addRemoteTextTrack accepts an options object that is fed directly to the native HTMLTrackElement which will optionally applykind, label, srclang, default, and src; if any of those exist.


How to manage the selected track? (haven't done a deep dive with managing initial track selection modes, but I did notice that embedded captions were being defaulted by the dash.js reference player)

I updated how I handle this. If I apply the default option, then both the defaulted track and "captions off" will be selected. I will loop through all the tracks after they have been generated and set the mode attribute to "showing" for default: true and "hidden" otherwise.


Apparently there are a few types of dash.js tracks that can be embedded, they can also have different roles, I am not sure how that would effect this implementation (since this looks like it only manages captions)—fyi, there isn't a 1:1 list of TextTracks from dash.js to HTML5 spec.

Agreed, the source for TextSourceBugger.js appears to show that dash.js only supports captions and subtitles. Using the kind property that comes from the dash parsing and feeding that kind into video.js using this implementation will show two separate (and functional) captions and subtitles menus.

@justinanastos justinanastos changed the title Load text and audio tracks from dash into video.js WIP: Load text and audio tracks from dash into video.js Jan 19, 2017
@justinanastos
Copy link
Contributor Author

I've reviewed all the samples in #103. This PR in it's current state does not match the functionality of the dash reference player. I'll keep working on it.

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from cfe82ae to be9aa24 Compare January 20, 2017 20:02
@justinanastos justinanastos changed the title WIP: Load text and audio tracks from dash into video.js Load text and audio tracks from dash into video.js Jan 20, 2017
@justinanastos
Copy link
Contributor Author

With this update, it's worked with everything I can throw at it @chemoish .

@justinanastos justinanastos changed the title Load text and audio tracks from dash into video.js WIP: Load text and audio tracks from dash into video.js Jan 23, 2017
@justinanastos
Copy link
Contributor Author

I found what I perceive to be a flaw in the text track selection logic. This assumes a 1:1 relationship between dash text tracks and videojs's DOM track list. This is not guaranteed because the user can manipulate the videojs text tracks however they like (or not even use them), so this needs to be modified.

// Example:
// https://storage.googleapis.com/shaka-demo-assets/sintel-mp4-wvtt/dash.mpd

// Return `track` so we can continue chaning.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chaining + does this require chaining still?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chemoish No, it doesn't :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly update to .each and remove comment/return.


// When `dashjs` finishes loading metadata, create audio tracks for
// `video.js`.
this.mediaPlayer_.on(
Copy link
Member

@chemoish chemoish Jan 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor, any reason why the event handler is bound inline rather than within the setup (different implementation than textTrack)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No good reason @chemoish , I will update it to make them consistent.

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch 2 times, most recently from 5067eb4 to 10fc47d Compare January 24, 2017 21:53
@justinanastos
Copy link
Contributor Author

Updates pushed with the following changes:

  • Text track handling is no longer async because I am passing tech into setupTextTracks and it can then check for tech.featuresNativeTextTracks.
  • setupTextTracks is called in the same fashion as setupAudioTracks
  • Text track handling is no longer dependent on the dash text track list being a 1:1 match for the videojs track list because users can destroy or create text tracks at will. This uses a WeakMap as a dictionary.

@justinanastos justinanastos changed the title WIP: Load text and audio tracks from dash into video.js Load text and audio tracks from dash into video.js Jan 24, 2017
@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from 10fc47d to f0a38b5 Compare January 24, 2017 22:18
function attachDashTextTracksToVideojs(player, tracks) {
const mediaPlayer = player.dash.mediaPlayer;

const trackDictionaryKeyedByTextTrack = new WeakMap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's a good idea to use WeakMap yet, still. To support the widest field of browsers possible, we can't use WeakMap and WeakMap isn't really polyfillable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll replace it with a Map()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might not want to either Map either depending on which browsers we're targeting for this project. We wouldn't want to require es6-shim for Map() support if we expect to be able to run this package in a browser that supports MSE+EME but not Map().

@forbesjo do need to worry about this?

Copy link
Contributor Author

@justinanastos justinanastos Jan 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake, I didn't check to see how the babel transforms were handled. Map can't be polyfilled so I agree it's unacceptable. I'll replace it.

tracks
// Map input data to match HTMLTrackElement spec
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLTrackElement
.map((track) => Object.assign(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to assign on top of the dash.js track rather than just create a new object containing only the properties needed by the video.js track?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No good reason, I can axe the track reference.

const textTracks = this.textTracks();
let activeTextTrackIndex = -1;

function findMatchingTrack(dashTrack) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is only used in once spot, it may be easier to read if it is just inlined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original plan, but jshint didn't like me creating functions within a loop.

// Iterate through the tracks and find the one marked as showing.
// If none are showing, `activeTextTrackIndex` will be set to
// `-1`, disabling text tracks.
for (let i = 0; i < textTracks.length; i += 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i++ just for consistent style

// Add track to videojs track list
.map((track) => {
const remoteTextTrack = player.addRemoteTextTrack(track, true);
trackDictionaryKeyedByTextTrack.set(remoteTextTrack.track, track);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to match the original dash.js track (instead of the new reference) to make it easier down below (can skip trying to find the reference based on index matching, since we have the reference).

Copy link
Contributor Author

@justinanastos justinanastos Jan 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand. Later on I use index matching because I have to loop through player.textTracks() to find the active track. After that, I use that track to figure out the dash track. dash.setTextTrack() expects an index so I have to first find it, then figure out it's index. I must be missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. When we're using remote time text files .getTracksFor('text') will return an empty array so we have no choice but to use the tracks passed in the event handler.

*/
const textTracks = player.textTracks();

for (let i = 0; i < textTracks.length; i += 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i++ just for consistent style

mediaPlayer.updateDashTextTrack();

// Update dash when videojs's selected text track changes. Use an arrow function so
// `mediaPlayer.updateDashTextTrack` can be overriden exterally.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we are ready yet to provide an external function that people can override. If we do though, we may want to add it onto player.dash, since mediaPlayer is technically a dash.js object that we don't have control over.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a mistake, I had used this logic elsewhere and forgot to remove it. I'll remove it here.

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from f0a38b5 to 82eba76 Compare January 27, 2017 23:07
@justinanastos
Copy link
Contributor Author

Updated @gesinger , @gkatsev , and @chemoish .

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from 82eba76 to 91ae1b5 Compare January 28, 2017 16:44
@justinanastos
Copy link
Contributor Author

Use of Map has been removed.

@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from 91ae1b5 to 981cdb2 Compare January 28, 2017 17:50
@justinanastos
Copy link
Contributor Author

justinanastos commented Jan 28, 2017

The audio track manipulation breaks dash playback in Safari 10.0.3. I am investigating.

@@ -89,6 +91,12 @@ class Html5DashJS {
this.mediaPlayer_.setProtectionData(this.keySystemOptions_);
this.mediaPlayer_.attachSource(manifestSource);

// Setup text tracks
setupTextTracks.call(null, this.player, tech);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be called before attachSource()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that dashjs.MediaPlayer.events.TEXT_TRACKS_ADDED/dashjs.MediaPlayer.events.PLAYBACK_METADATA_LOADED may fire between attachSource() and the event listener setup functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. If the user can fetch a source and attach it, this might become a sync call and the events might get missed. I've moved the calls to be before attachSource().

@justinanastos
Copy link
Contributor Author

justinanastos commented Jan 30, 2017

I have a question and could use some advice. I'm having trouble bypassing native captions in Safari because of this line:

https://github.com/videojs/video.js/blob/master/src/js/tech/html5.js#L798

I have been unsuccessful in finding a smooth way to disable native text tracks other than globally disabling it in the Html5 tech when videojs-contrib-dash is included, but this might break other techs/plugins. The only solution I have found that seems to work is to force the user to pass in {html: {nativeCaptions: false}} in their options when instantiating videojs.

There has to be a better way, can anyone give me some advice on where to look or what to do?

As of video.js v5.14, video.js no longer supports native
subtitles on anything besides Safari. For Chrome, this means
there is *no* subtitle support with mpeg-dash. This will wait for
dash.js to load all the text tracks and then add them to video.js
if we're using a version that does not attempt to use native
subtitles. When the video.js menu is used to change captions,
we'll set the corresponding active text track in dash.

We *cannot* dynamically add text track cues to video.js beacuse
fragmented text tracks are loaded on the fly and there's no event
fired when they are loaded. Therefore, we must has dash to
natively display captions.
The v5.18.0 release of video.js includes a fix for videojs/video.js#4093 with videojs/video.js#4131. This is neccessary for text tracks to work in Chrome.
- Only set `textTrack.mode` for `captions` and `subtitles.
- Don't call the initial `updateActiveDashTextTrack` until after
  the `textTrack.mode`s have been set.
@justinanastos justinanastos force-pushed the use-emulated-text-tracks branch from e815d1e to f69fab8 Compare April 14, 2017 20:36
@justinanastos justinanastos changed the title WIP: Load text tracks from dash into video.js Load text tracks from dash into video.js Apr 14, 2017
@justinanastos
Copy link
Contributor Author

Any updates on this @forbesjo ?

@squarebracket
Copy link
Contributor

FYI, I downloaded the patch of this PR, and noticed that changing the caption display settings doesn't actually change them.

@justinanastos
Copy link
Contributor Author

@squarebracket

FYI, I downloaded the patch of this PR, and noticed that changing the caption display settings doesn't actually change them.

That's correct. The captions will be displayed natively by dash.js.

@forbesjo
Copy link
Contributor

forbesjo commented May 8, 2017

@justinanastos testing this now

@squarebracket
Copy link
Contributor

@justinanastos Why would that preclude video.js from setting the caption style? Aren't the dash.js captions being spit out as VTTCues?

@gkatsev
Copy link
Member

gkatsev commented May 8, 2017

Dash.js renders the captions natively rather than using Video.js's rendering capabilities. That means that the styling that Video.js can provide via the caption settings menu isn't (and potentially can't be) applied.
We probably should disable that menu item in this case.

@squarebracket
Copy link
Contributor

Can't videojs just set CSS classes or something though?

@forbesjo
Copy link
Contributor

forbesjo commented May 8, 2017

@justinanastos your PR works for me but as has been discussed the captions style cannot be changed since dash.js is doing the rendering and not video.js. Looking into this

@forbesjo
Copy link
Contributor

forbesjo commented May 8, 2017

It doesn't look like there's a mechanism to disable the dash.js display so I agree with @gkatsev that the caption settings menu item should be disabled.

@squarebracket
Copy link
Contributor

squarebracket commented May 9, 2017

If they're going to be left completely to dash.js, then may I suggest doing something this in Html5DashJS:

    // Attach the source with any protection data
    this.mediaPlayer_.setProtectionData(this.keySystemOptions_);
    this.mediaPlayer_.attachSource(manifestSource);
    // Use the normal text track display to render TTMLs into
    this.mediaPlayer_.attachTTMLRenderingDiv($('.vjs-text-track-display')[0]);

    this.tech_.triggerReady();

The only addition there is calling attachTTMLRenderingDiv.

Their TTML rendering is much better than their VTTCue rendering. For instance, it can do this:
screenshot from 2017-05-02 21-52-30
And also supports vertical placement which is important for things like news shows:
screenshot from 2017-05-09 10-38-49

@squarebracket
Copy link
Contributor

In case anyone was testing media with CEA608 captions, I just had some fixes merged into dash.js. They should now work properly. (For reference, here here and here.)

@forbesjo
Copy link
Contributor

@squarebracket I'm not sure if it's a great idea to reuse video.js's text track display for TTML rendering. Instead this script can add a new component div to the player specifically for TTML. That can be done in a separate PR.

@gkatsev what's the recommended way of disabling menu items like the captions settings item? Thinking about this a little more we would still want to display caption settings if there are side-loaded captions (captions not coming from the source manifest). Though when a mix of in-manifest and external captions are present in the player a view might be confused when some captions can be customized while others cannot.

@forbesjo
Copy link
Contributor

I think we should leave the captions settings alone, merge this PR in and add other enhancements later

@forbesjo forbesjo merged commit 5bf6c6a into videojs:master May 11, 2017
@justinanastos justinanastos deleted the use-emulated-text-tracks branch May 12, 2017 19:58
@justinanastos justinanastos restored the use-emulated-text-tracks branch May 12, 2017 19:58
@justinanastos
Copy link
Contributor Author

@squarebracket I believe native cue rendering can do the same thing you're showing, right? There can be multiple cues on the screen at once and their position is defined by the vtt file.

@squarebracket
Copy link
Contributor

squarebracket commented May 15, 2017

@justinanastos that is correct, however that information is dropped in the dash.js VTTCue rendering library, but is rendered for the TTML rendering library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants