Skip to content

Commit

Permalink
Merge pull request #1948 from twilio/prep-2.26.0
Browse files Browse the repository at this point in the history
Prep for 2.26.0 release
  • Loading branch information
PikaJoyce authored Dec 14, 2022
2 parents 666a972 + 7f2a40c commit 96dfb80
Show file tree
Hide file tree
Showing 30 changed files with 1,467 additions and 1,238 deletions.
113 changes: 112 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,117 @@ The Twilio Programmable Video SDKs use [Semantic Versioning](http://www.semver.o

**Version 1.x reached End of Life on September 8th, 2021.** See the changelog entry [here](https://www.twilio.com/changelog/end-of-life-complete-for-unsupported-versions-of-the-programmable-video-sdk). Support for the 1.x version ended on December 4th, 2020.

2.26.0 (December 14, 2022)
==========================

New Features
------------

- The [`LocalAudioTrack`](https://sdk.twilio.com/js/video/releases/2.26.0/docs/LocalAudioTrack.html) and
[`LocalVideoTrack`](https://sdk.twilio.com/js/video/releases/2.26.0/docs/LocalVideoTrack.html) classes now provide a
new boolean property called `isMuted`, which lets you know if the audio or video source is currently providing raw media
samples. The classes also emit `muted` and `unmuted` events if the value of `isMuted` toggles. The application can use
these APIs to detect temporary loss of microphone or camera to other applications (ex: an incoming phone call on an iOS device),
and update the user interface accordingly. (VIDEO-11360)

- The `Room` class provides a new method called [refreshInactiveMedia](https://sdk.twilio.com/js/video/releases/2.26.0/docs/Room.html#refreshInactiveMedia),
which restarts any muted local media Tracks, and plays any inadvertently paused HTMLMediaElements that are attached to
local and remote media Tracks. This is useful especially on iOS devices, where sometimes your application's media may
not recover after an incoming phone call. You can use this method in conjunction with the local media Track's `isMuted`
property described previously to recover local and remote media after an incoming phone call as shown below. (VIDEO-11360)

### Vanilla JS

#### html

```html
<button id="refresh-inactive-media" disabled>Refresh Inactive Media</button>
```

#### js

```js
const { connect } = require('twilio-video');

const room = await connect('token', { name: 'my-cool-room' });

const $refreshInactiveMedia = document.getElementById('refresh-inactive-media');
$refreshInactiveMedia.onclick = () => room.refreshInactiveMedia();

const [{ track: localAudioTrack }] = [...room.localParticipant.audioTracks.values()];
const [{ track: localVideoTrack }] = [...room.localParticipant.videoTracks.values()];

const isLocalAudioOrVideoMuted = () => {
return localAudioTrack.isMuted || localVideoTrack.isMuted;
}

const onLocalMediaMutedChanged = () => {
$refreshInactiveMedia.disabled = !isLocalAudioOrVideoMuted();
};

[localAudioTrack, localVideoTrack].forEach(localMediaTrack => {
['muted', 'unmuted'].forEach(event => {
localMediaTrack.on(event, onLocalMediaMutedChanged);
});
});
```

### React

#### src/hooks/useLocalMediaMuted.js

```js
import { useEffect, useState } from 'react';

export default function useLocalMediaMuted(localMediaTrack) {
const [isMuted, setIsMuted] = useState(localMediaTrack?.isMuted ?? false);

useEffect(() => {
const updateMuted = () => setIsMuted(localMediaTrack?.isMuted ?? false);
updateMuted();

localMediaTrack?.on('muted', updateMuted);
localMediaTrack?.on('unmuted', updateMuted);

return () => {
localMediaTrack?.off('muted', updateMuted);
localMediaTrack?.off('unmuted', updateMuted);
};
}, [localMediaTrack]);

return isMuted;
}
```
#### src/components/room.js
```jsx
import useLocalMediaMuted from '../hooks/useLocalMediaMuted';

export default function Room({ room }) {
const [{ track: localAudioTrack }] = [...room.localParticipant.audioTracks.values()];
const [{ track: localVideoTrack }] = [...room.localParticipant.videoTracks.values()];

const isLocalAudioMuted = useLocalMediaMuted(localAudioTrack);
const isLocalVideoMuted = useLocalMediaMuted(localVideoTrack);
const isLocalMediaMuted = isLocalAudioMuted || isLocalVideoMuted;

const refreshInactiveMedia = () => {
room.refreshInactiveMedia();
};

return (
<>
...
{isLocalMediaMuted && <Button onClick={refreshInactiveMedia}>
Refresh Inactive Media
</Button>}
...
</>
);
}
```
2.25.0 (November 14, 2022)
==========================
Expand All @@ -27,7 +138,7 @@ The application can decide to capture audio from a specific audio input device b
- using the MediaTrackConstraints `{ audio: { deviceId: 'foo' } }`, and "foo" is available, or
- using the MediaTrackConstraints `{ audio: { deviceId: { ideal: 'foo' } } }` and "foo" is available, or
- using the MediaTrackConstraints `{ audio: { deviceId: { exact: 'foo' } } }` and "foo" is available
- using the MediaTrackConstraints `{ audio: { deviceId: { exact: 'foo' } } }` and "foo" is available
In this case, the LocalAudioTrack DOES NOT switch to another audio input device if the current audio input device is no
longer available. See below for the behavior of this property based on how the LocalAudioTrack is created. (VIDEO-11701)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Releases of twilio-video.js are hosted on a CDN, and you can include these
directly in your web app using a &lt;script&gt; tag.

```html
<script src="//sdk.twilio.com/js/video/releases/2.25.0/twilio-video.min.js"></script>
<script src="//sdk.twilio.com/js/video/releases/2.26.0/twilio-video.min.js"></script>
```

Using this method, twilio-video.js will set a browser global:
Expand Down
38 changes: 30 additions & 8 deletions lib/media/track/localaudiotrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ const LocalMediaAudioTrack = mixinLocalMediaTrack(AudioTrack);
* {@link LocalAudioTrack#stop}.
* @extends AudioTrack
* @property {Track.ID} id - The {@link LocalAudioTrack}'s ID
* @property {boolean} isMuted - Whether or not the audio source has stopped sending samples to the
* {@link LocalAudioTrack}; This can happen when the microphone is taken over by another application,
* mainly on mobile devices; When this property toggles, then <code>muted</code> and <code>unmuted</code>
* events are fired appropriately
* @property {boolean} isStopped - Whether or not the {@link LocalAudioTrack} is
* stopped
* @property {NoiseCancellation?} noiseCancellation - When a LocalAudioTrack is created
* with {@link NoiseCancellationOptions}, this property provides interface
* to enable or disable the noise cancellation at runtime.
* @emits LocalAudioTrack#disabled
* @emits LocalAudioTrack#enabled
* @emits LocalAudioTrack#muted
* @emits LocalAudioTrack#started
* @emits LocalAudioTrack#stopped
* @emits LocalAudioTrack#unmuted
*/
class LocalAudioTrack extends LocalMediaAudioTrack {
/**
Expand Down Expand Up @@ -183,7 +189,7 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
}

/**
* Disable the {@link LocalAudioTrack}. This is effectively "mute".
* Disable the {@link LocalAudioTrack}. This is equivalent to muting the audio source.
* @returns {this}
* @fires LocalAudioTrack#disabled
*/
Expand All @@ -192,13 +198,13 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
}

/**
* Enable the {@link LocalAudioTrack}. This is effectively "unmute".
* Enable the {@link LocalAudioTrack}. This is equivalent to unmuting the audio source.
* @returns {this}
* @fires LocalAudioTrack#enabled
*//**
* Enable or disable the {@link LocalAudioTrack}. This is effectively "unmute"
* or "mute".
* @param {boolean} [enabled] - Specify false to mute the
* Enable or disable the {@link LocalAudioTrack}. This is equivalent to unmuting or muting
* the audio source respectively.
* @param {boolean} [enabled] - Specify false to disable the
* {@link LocalAudioTrack}
* @returns {this}
* @fires LocalAudioTrack#disabled
Expand Down Expand Up @@ -264,18 +270,25 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
}

/**
* The {@link LocalAudioTrack} was disabled, i.e. "muted".
* The {@link LocalAudioTrack} was disabled, i.e. the audio source was muted by the user.
* @param {LocalAudioTrack} track - The {@link LocalAudioTrack} that was
* disabled
* @event LocalAudioTrack#disabled
*/

/**
* The {@link LocalAudioTrack} was enabled, i.e. "unmuted".
* The {@link LocalAudioTrack} was enabled, i.e. the audio source was unmuted by the user.
* @param {LocalAudioTrack} track - The {@link LocalAudioTrack} that was enabled
* @event LocalAudioTrack#enabled
*/

/**
* The {@link LocalAudioTrack} was muted because the audio source stopped sending samples, most
* likely due to another application taking said audio source, especially on mobile devices.
* @param {LocalAudioTrack} track - The {@link LocalAudioTrack} that was muted
* @event LocalAudioTrack#muted
*/

/**
* The {@link LocalAudioTrack} started. This means there is enough audio data to
* begin playback.
Expand All @@ -291,5 +304,14 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
* @event LocalAudioTrack#stopped
*/

module.exports = LocalAudioTrack;
/**
* The {@link LocalAudioTrack} was unmuted because the audio source resumed sending samples,
* most likely due to the application that took over the said audio source has released it
* back to the application, especially on mobile devices. This event is also fired when
* {@link LocalAudioTrack#restart} is called on a muted {@link LocalAudioTrack} with a
* new audio source.
* @param {LocalAudioTrack} track - The {@link LocalAudioTrack} that was unmuted
* @event LocalAudioTrack#unmuted
*/

module.exports = LocalAudioTrack;
29 changes: 29 additions & 0 deletions lib/media/track/localmediatrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
* enabled and disabled with {@link LocalMediaTrack#enable} and
* {@link LocalMediaTrack#disable} or stopped completely with
* {@link LocalMediaTrack#stop}.
* @emits LocalMediaTrack#muted
* @emits LocalMediaTrack#stopped
* @emits LocalMediaTrack#unmuted
*/
return class LocalMediaTrack extends AudioOrVideoTrack {
/**
Expand Down Expand Up @@ -60,6 +62,12 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
_gUMSilentTrackWorkaround: {
value: options.gUMSilentTrackWorkaround
},
_eventsToReemitters: {
value: new Map([
['muted', () => this.emit('muted', this)],
['unmuted', () => this.emit('unmuted', this)]
])
},
_workaroundWebKitBug1208516: {
value: options.workaroundWebKitBug1208516
},
Expand Down Expand Up @@ -87,6 +95,12 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
return mediaTrackSender.enabled;
}
},
isMuted: {
enumerable: true,
get() {
return mediaTrackSender.muted;
}
},
isStopped: {
enumerable: true,
get() {
Expand All @@ -100,6 +114,8 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
if (this._workaroundWebKitBug1208516) {
this._workaroundWebKitBug1208516Cleanup = restartWhenInadvertentlyStopped(this);
}

this._reemitTrackSenderEvents();
}

/**
Expand All @@ -111,6 +127,7 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
}
super._end.call(this);
this._didCallEnd = true;
this._eventsToReemitters.forEach((reemitter, event) => this._trackSender.removeListener(event, reemitter));
this.emit('stopped', this);
}

Expand All @@ -121,6 +138,9 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
if (this._didCallEnd) {
this._didCallEnd = false;
}
if (this._eventsToReemitters) {
this._reemitTrackSenderEvents();
}
super._initialize.call(this);
}

Expand Down Expand Up @@ -152,6 +172,15 @@ function mixinLocalMediaTrack(AudioOrVideoTrack) {
});
}

/**
* @private
*/
_reemitTrackSenderEvents() {
this._eventsToReemitters.forEach((reemitter, event) => this._trackSender.on(event, reemitter));
this._trackSender.dequeue('muted');
this._trackSender.dequeue('unmuted');
}

/**
* @private
*/
Expand Down
Loading

0 comments on commit 96dfb80

Please sign in to comment.