Skip to content

Commit

Permalink
Add startTime support in src= mode (#2271)
Browse files Browse the repository at this point in the history
Closes #2267
  • Loading branch information
Álvaro Velad Galván authored and joeyparrish committed Jan 15, 2020
1 parent 51f8fe9 commit a23b6eb
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 26 deletions.
7 changes: 5 additions & 2 deletions lib/media/playhead.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,15 @@ shaka.media.SrcEqualsPlayhead = class {
this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
this.started_ = true;
});
this.mediaElement_.currentTime = this.startTime_;
const currentTime = this.mediaElement_.currentTime;
// Using the currentTime allows using a negative number in Live HLS
const newTime = Math.max(0, currentTime + this.startTime_);
this.mediaElement_.currentTime = newTime;
}
};
if (this.mediaElement_.readyState == 0) {
this.eventManager_.listenOnce(
this.mediaElement_, 'loadedmetadata', onLoaded);
this.mediaElement_, 'loadeddata', onLoaded);
} else {
// It's already loaded.
onLoaded();
Expand Down
55 changes: 31 additions & 24 deletions test/player_src_equals_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,39 +55,45 @@ describe('Player Src Equals', () => {
// This test verifies that we can successfully load content that requires us
// to use |src=|.
it('loads content', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
});

// This test verifys that we can successfully unload content that required
// |src=| to load.
it('unloads content', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
await player.unload(/* initMediaSource= */ false);
});

it('can get asset uri after loading', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(player.getAssetUri()).toBe(SMALL_MP4_CONTENT_URI);
});

// TODO: test an HLS live stream on platforms supporting native HLS
it('considers simple mp4 content to be VOD"', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(player.isLive()).toBeFalsy();
expect(player.isInProgress()).toBeFalsy();
});

// TODO: test an audio-only mp4
// TODO: test audio-only HLS on platforms with native HLS
it('considers audio-video mp4 content to be audio-video', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(player.isAudioOnly()).toBeFalsy();
});

it('allow load with startTime', async () => {
const startTime = 5;
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, startTime);
expect(video.currentTime).toBe(startTime);
});

// Since we don't have any manifest data, we must assume that we can seek
// anywhere in the presentation; end-time will come from the media element.
it('allows seeking throughout the presentation', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// For src=, the seekRange is based on video.seekable, so wait for this
// event before proceeding to check seekRange.
Expand Down Expand Up @@ -125,7 +131,7 @@ describe('Player Src Equals', () => {
// TODO: test HLS without DRM on platforms with native HLS
// TODO: test HLS with DRM on platforms with native HLS
it('considers simple content to be clear ', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

expect(player.keySystem()).toBe('');
expect(player.drmInfo()).toBe(null);
Expand All @@ -136,7 +142,7 @@ describe('Player Src Equals', () => {
// accurate information. However we can still report what the media element
// surfaces.
it('reports buffering information', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// For playback to begin so that we have some content buffered.
video.play();
Expand All @@ -160,7 +166,7 @@ describe('Player Src Equals', () => {
// When we load content via src=, can we use the trick play controls to
// control the playback rate.
it('can control trick play rate', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// Let playback run for a little.
video.play();
Expand All @@ -180,7 +186,7 @@ describe('Player Src Equals', () => {

// TODO: test audio-video mp4 content on platforms with audioTracks API
it('reports variant tracks for video-only mp4 content', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// On platforms with audioTracks, such as Safari, we get one track here.
if (video.audioTracks) {
Expand All @@ -192,7 +198,7 @@ describe('Player Src Equals', () => {

// TODO: test HLS on platforms with native HLS
it('allows selecting variant tracks', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// We can only get a variant track here on certain browsers.
const tracks = player.getVariantTracks();
Expand All @@ -206,13 +212,13 @@ describe('Player Src Equals', () => {

// TODO: test HLS with text tracks on platforms with native HLS
it('reports no text tracks for simple mp4 content', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(player.getTextTracks()).toEqual([]);
});

// TODO: test HLS on platforms with native HLS
it('allows selecting text tracks', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// We can only get a text track here on certain browsers.
const tracks = player.getTextTracks();
Expand All @@ -226,7 +232,7 @@ describe('Player Src Equals', () => {

// TODO: test HLS on platforms with native HLS
it('returns no languages or roles for simple mp4 content', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// On platforms with audioTracks, such as Safari, we get one track, with
// language set to whatever is in the mp4.
Expand All @@ -249,7 +255,7 @@ describe('Player Src Equals', () => {
// TODO: test language selection w/ HLS on platforms with native HLS
// This test is disabled until then.
xit('cannot select language or role', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

const language = 'en';
const role = 'main';
Expand All @@ -274,7 +280,7 @@ describe('Player Src Equals', () => {
// TODO: test text visibility w/ HLS on platforms with native HLS
// This test is disabled until then.
xit('persists the text visibility setting', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

expect(player.isTextTrackVisible()).toBe(false);

Expand All @@ -288,7 +294,7 @@ describe('Player Src Equals', () => {
// Even though we loaded content using |src=| we should still be able to get
// the playhead position as normal.
it('can get the playhead position', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(video.readyState).toBeGreaterThan(0);

expect(video.currentTime).toBeCloseTo(0);
Expand All @@ -307,7 +313,7 @@ describe('Player Src Equals', () => {
// Even though we are not using all the internals, we should still get some
// meaningful statistics.
it('can get stats', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

// Wait some time for playback to start so that we will have a load latency
// value.
Expand All @@ -322,7 +328,7 @@ describe('Player Src Equals', () => {

// Because we have no manifest, we can't add text tracks.
it('cannot add text tracks', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);

const pendingAdd = player.addTextTrack(
'test:need-a-uri-for-text',
Expand All @@ -339,28 +345,29 @@ describe('Player Src Equals', () => {
// Since we are not in-charge of streaming, calling |retryStreaming| should
// have no effect.
it('requesting streaming retry does nothing', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(player.retryStreaming()).toBeFalsy();
});

// Since we are not loading a manifest, we can't return a manifest.
// |getManifest| should return |null|.
it('has no manifest to return', async () => {
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI);
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime */ null);
expect(player.getManifest()).toBeFalsy();
});

/**
* @param {string} contentUri
* @param {?number} startTime
* @return {!Promise}
*/
async function loadWithSrcEquals(contentUri) {
async function loadWithSrcEquals(contentUri, startTime) {
const ready = new Promise((resolve) => {
eventManager.listenOnce(video, 'loadedmetadata', resolve);
eventManager.listenOnce(video, 'loadeddata', resolve);
});

await player.attach(video, /* initMediaSource= */ false);
await player.load(contentUri);
await player.load(contentUri, startTime);

// Wait until the media element is ready with content. Waiting until this
// point ensures it is safe to interact with the media element.
Expand Down

0 comments on commit a23b6eb

Please sign in to comment.