Skip to content

Commit

Permalink
fix(Cast): Don't pause local video until the cast connection is estab…
Browse files Browse the repository at this point in the history
…lished (#6359)

CastProxy's getInitState_() pauses the local video as part of a transfer
of state to the receiver. Calling getInitState_() (via CastSender's
onInitStateRequired_()) should be deferred until we establish a
connection. This keeps the local video from pausing during a connection
attempt.

Closes #6293
  • Loading branch information
avelad committed Apr 8, 2024
1 parent 5143ef4 commit e74d90a
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 86 deletions.
4 changes: 1 addition & 3 deletions lib/cast/cast_proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,10 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
* @export
*/
async cast() {
const initState = this.getInitState_();

// TODO: transfer manually-selected tracks?
// TODO: transfer side-loaded text tracks?

await this.sender_.cast(initState);
await this.sender_.cast();
if (!this.localPlayer_) {
// We've already been destroyed.
return;
Expand Down
19 changes: 8 additions & 11 deletions lib/cast/cast_sender.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,10 @@ shaka.cast.CastSender = class {


/**
* @param {shaka.cast.CastUtils.InitStateType} initState Video and player
* state to be sent to the receiver.
* @return {!Promise} Resolved when connected to a receiver. Rejected if the
* connection fails or is canceled by the user.
*/
async cast(initState) {
async cast() {
if (!this.apiReady_) {
throw new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
Expand All @@ -288,7 +286,7 @@ shaka.cast.CastSender = class {

this.castPromise_ = new shaka.util.PublicPromise();
chrome.cast.requestSession(
(session) => this.onSessionInitiated_(initState, session),
(session) => this.onSessionInitiated_(session),
(error) => this.onConnectionError_(error));
await this.castPromise_;
}
Expand All @@ -303,10 +301,9 @@ shaka.cast.CastSender = class {
if (!this.isCasting_) {
return;
}
const initState = this.onInitStateRequired_();

chrome.cast.requestSession(
(session) => this.onSessionInitiated_(initState, session),
(session) => this.onSessionInitiated_(session),
(error) => this.onConnectionError_(error));
}

Expand Down Expand Up @@ -401,12 +398,14 @@ shaka.cast.CastSender = class {


/**
* @param {shaka.cast.CastUtils.InitStateType} initState
* @param {chrome.cast.Session} session
* @private
*/
onSessionInitiated_(initState, session) {
onSessionInitiated_(session) {
shaka.log.debug('CastSender: onSessionInitiated');

const initState = this.onInitStateRequired_();

this.onSessionCreated_(session);

this.sendMessage_({
Expand Down Expand Up @@ -529,12 +528,10 @@ shaka.cast.CastSender = class {
onExistingSessionJoined_(session) {
shaka.log.debug('CastSender: onExistingSessionJoined');

const initState = this.onInitStateRequired_();

this.castPromise_ = new shaka.util.PublicPromise();
this.hasJoinedExistingSession_ = true;

this.onSessionInitiated_(initState, session);
this.onSessionInitiated_(session);
}


Expand Down
46 changes: 0 additions & 46 deletions test/cast/cast_proxy_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,52 +114,6 @@ describe('CastProxy', () => {
});

describe('cast', () => {
it('pauses the local video', () => {
proxy.cast();
expect(mockVideo.pause).toHaveBeenCalled();
});

it('passes initial state to sender', () => {
mockVideo.loop = true;
mockVideo.playbackRate = 3;
mockVideo.currentTime = 12;
const fakeConfig = {key: 'value'};
mockPlayer.getConfiguration.and.returnValue(fakeConfig);
mockPlayer.isTextTrackVisible.and.returnValue(false);
const fakeManifestUri = 'foo://bar';
mockPlayer.getAssetUri.and.returnValue(fakeManifestUri);

proxy.cast();
const calls = mockSender.cast.calls;
expect(calls.count()).toBe(1);
if (calls.count()) {
const state = calls.argsFor(0)[0];
// Video state goes directly:
expect(state.video.loop).toBe(mockVideo.loop);
expect(state.video.playbackRate).toBe(mockVideo.playbackRate);
// Player state uses corresponding setter names:
expect(state.player.configure).toEqual(fakeConfig);
expect(state['playerAfterLoad'].setTextTrackVisibility).toBe(false);
// Manifest URI:
expect(state.manifest).toBe(fakeManifestUri);
// Start time:
expect(state.startTime).toBe(mockVideo.currentTime);
}
});

it('does not provide a start time if the video has ended', () => {
mockVideo.ended = true;
mockVideo.currentTime = 12;

proxy.cast();
const calls = mockSender.cast.calls;
expect(calls.count()).toBe(1);
if (calls.count()) {
const state = calls.argsFor(0)[0];
expect(state.startTime).toBe(null);
}
});

it('unloads the local player after casting is complete', async () => {
/** @type {!shaka.util.PublicPromise} */
const p = new shaka.util.PublicPromise();
Expand Down
52 changes: 26 additions & 26 deletions test/cast/cast_sender_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe('CastSender', () => {
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.CAST,
shaka.util.Error.Code.CAST_API_UNAVAILABLE));
await expectAsync(sender.cast(fakeInitState)).toBeRejectedWith(expected);
await expectAsync(sender.cast()).toBeRejectedWith(expected);
});

it('fails when there are no receivers', async () => {
Expand All @@ -168,7 +168,7 @@ describe('CastSender', () => {
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.CAST,
shaka.util.Error.Code.NO_CAST_RECEIVERS));
await expectAsync(sender.cast(fakeInitState)).toBeRejectedWith(expected);
await expectAsync(sender.cast()).toBeRejectedWith(expected);
});

it('creates a session and sends an "init" message', async () => {
Expand All @@ -177,7 +177,7 @@ describe('CastSender', () => {
fakeReceiverAvailability(true);
expect(sender.hasReceivers()).toBe(true);

const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();

await p;
Expand Down Expand Up @@ -220,7 +220,7 @@ describe('CastSender', () => {
sender.init();
fakeReceiverAvailability(true);

const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnectionFailure(metadata.castErrorCode);

const expected = Util.jasmineError(new shaka.util.Error(
Expand All @@ -236,22 +236,22 @@ describe('CastSender', () => {
sender.init();
fakeReceiverAvailability(true);

const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();

await p;
const expected = Util.jasmineError(new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.CAST,
shaka.util.Error.Code.ALREADY_CASTING));
await expectAsync(sender.cast(fakeInitState)).toBeRejectedWith(expected);
await expectAsync(sender.cast()).toBeRejectedWith(expected);
});
});

it('re-uses old sessions', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
const oldMockSession = mockSession;
await p;
Expand Down Expand Up @@ -289,7 +289,7 @@ describe('CastSender', () => {
it('doesn\'t re-use stopped sessions', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;
await sender.destroy();
Expand Down Expand Up @@ -334,7 +334,7 @@ describe('CastSender', () => {
sender.init();
fakeReceiverAvailability(true);
sender.setAppData(fakeAppData);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();

await p;
Expand All @@ -347,7 +347,7 @@ describe('CastSender', () => {
it('sends a special "appData" message if casting', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand Down Expand Up @@ -390,7 +390,7 @@ describe('CastSender', () => {
it('is not triggered if making a new session', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand Down Expand Up @@ -437,7 +437,7 @@ describe('CastSender', () => {
it('is triggered by an "event" message', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -460,7 +460,7 @@ describe('CastSender', () => {
it('is triggered when casting ends', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -477,7 +477,7 @@ describe('CastSender', () => {
it('opens the dialog if we are casting', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -502,7 +502,7 @@ describe('CastSender', () => {
it('returns most recent properties from "update" messages', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand Down Expand Up @@ -545,7 +545,7 @@ describe('CastSender', () => {
it('simple methods trigger "call" messages', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -568,7 +568,7 @@ describe('CastSender', () => {
method = null;
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand Down Expand Up @@ -655,7 +655,7 @@ describe('CastSender', () => {
it('overrides any cached properties', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -675,7 +675,7 @@ describe('CastSender', () => {
it('causes a "set" message to be sent', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -691,7 +691,7 @@ describe('CastSender', () => {
it('can be used before we have an "update" message', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -705,7 +705,7 @@ describe('CastSender', () => {
it('is true only after we have an "update" message', async () => {
sender.init();
fakeReceiverAvailability(true);
const p = sender.cast(fakeInitState);
const p = sender.cast();
fakeSessionConnection();
await p;

Expand All @@ -723,7 +723,7 @@ describe('CastSender', () => {
it('disconnects and cancels all async operations', async () => {
sender.init();
fakeReceiverAvailability(true);
const cast = sender.cast(fakeInitState);
const cast = sender.cast();
fakeSessionConnection();
await cast;

Expand Down Expand Up @@ -756,7 +756,7 @@ describe('CastSender', () => {
it('transfers playback to local device', async () => {
sender.init();
fakeReceiverAvailability(true);
const cast = sender.cast(fakeInitState);
const cast = sender.cast();
fakeSessionConnection();
await cast;

Expand All @@ -772,7 +772,7 @@ describe('CastSender', () => {
it('succeeds even if session.stop() throws', async () => {
sender.init();
fakeReceiverAvailability(true);
const cast = sender.cast(fakeInitState);
const cast = sender.cast();
fakeSessionConnection();
await cast;

Expand All @@ -792,7 +792,7 @@ describe('CastSender', () => {
beforeEach(async () => {
sender.init();
fakeReceiverAvailability(true);
const cast = sender.cast(fakeInitState);
const cast = sender.cast();
fakeSessionConnection();
await cast;

Expand Down Expand Up @@ -837,7 +837,7 @@ describe('CastSender', () => {
it('cancels all async operations', async () => {
sender.init();
fakeReceiverAvailability(true);
const cast = sender.cast(fakeInitState);
const cast = sender.cast();
fakeSessionConnection();
await cast;

Expand Down

0 comments on commit e74d90a

Please sign in to comment.