Skip to content

Commit

Permalink
Merge pull request #55 from jitsi/stream-constructor
Browse files Browse the repository at this point in the history
api: add support for the MediaStream constructor
  • Loading branch information
saghul committed Jul 25, 2019
2 parents 1929018 + ccee251 commit 44ecfdd
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 211 deletions.
84 changes: 62 additions & 22 deletions MediaStream.js
Expand Up @@ -2,7 +2,7 @@

import {NativeModules} from 'react-native';
import EventTarget from 'event-target-shim';
import MediaStreamTrackEvent from './MediaStreamTrackEvent';
import uuid from 'uuid';

import type MediaStreamTrack from './MediaStreamTrack';

Expand Down Expand Up @@ -34,31 +34,71 @@ export default class MediaStream extends EventTarget(MEDIA_STREAM_EVENTS) {
* unambiguously differentiate it from a local MediaStream instance not added
* to an RTCPeerConnection.
*/
reactTag: string;

constructor(id, reactTag) {
super();
this.id = id;
// Local MediaStreams are created by WebRTCModule to have their id and
// reactTag equal because WebRTCModule follows the respective standard's
// recommendation for id generation i.e. uses UUID which is unique enough
// for the purposes of reactTag.
this.reactTag = (typeof reactTag === 'undefined') ? id : reactTag;
_reactTag: string;

/**
* A MediaStream can be constructed in several ways, depending on the paramters
* that are passed here.
*
* - undefined: just a new stream, with no tracks.
* - MediaStream instance: a new stream, with a copy of the tracks of the passed stream.
* - Array of MediaStreamTrack: a new stream with a copy of the tracks in the array.
* - object: a new stream instance, represented by the passed info object, this is always
* done internally, when the stream is first created in native and the JS wrapper is
* built afterwards.
*/
constructor(arg) {
super();

// Assigm a UUID to start with. It may get overridden for remote streams.
this.id = uuid.v4();
// Local MediaStreams are created by WebRTCModule to have their id and
// reactTag equal because WebRTCModule follows the respective standard's
// recommendation for id generation i.e. uses UUID which is unique enough
// for the purposes of reactTag.
this._reactTag = this.id;

if (typeof arg === 'undefined') {
WebRTCModule.mediaStreamCreate(this.id);
} else if (arg instanceof MediaStream) {
WebRTCModule.mediaStreamCreate(this.id);
for (const track of arg.getTracks()) {
this.addTrack(track);
}
} else if (Array.isArray(arg)) {
WebRTCModule.mediaStreamCreate(this.id);
for (const track of arg) {
this.addTrack(track);
}
} else if (typeof arg === 'object' && arg.streamId && arg.streamReactTag && arg.tracks) {
this.id = arg.streamId;
this._reactTag = arg.streamReactTag;
for (const trackInfo of arg.tracks) {
// We are not using addTrack here because the track is already part of the
// stream, so there is no need to add it on the native side.
this._tracks.push(new MediaStreamTrack(trackInfo));
}
} else {
throw new TypeError(`invalid type: ${typeof arg}`);
}
}

addTrack(track: MediaStreamTrack) {
this._tracks.push(track);
this.dispatchEvent(new MediaStreamTrackEvent('addtrack', {track}));
const index = this._tracks.indexOf(track);
if (index !== -1) {
return;
}
this._tracks.push(track);
WebRTCModule.mediaStreamAddTrack(this._reactTag, track.id);
}

removeTrack(track: MediaStreamTrack) {
let index = this._tracks.indexOf(track);
if (index === -1) {
return;
}
WebRTCModule.mediaStreamTrackRelease(this.reactTag, track.id);
this._tracks.splice(index, 1);
this.dispatchEvent(new MediaStreamTrackEvent('removetrack', {track}));
const index = this._tracks.indexOf(track);
if (index === -1) {
return;
}
this._tracks.splice(index, 1);
WebRTCModule.mediaStreamRemoveTrack(this._reactTag, track.id);
}

getTracks(): Array<MediaStreamTrack> {
Expand All @@ -82,10 +122,10 @@ export default class MediaStream extends EventTarget(MEDIA_STREAM_EVENTS) {
}

toURL() {
return this.reactTag;
return this._reactTag;
}

release() {
WebRTCModule.mediaStreamRelease(this.reactTag);
WebRTCModule.mediaStreamRelease(this._reactTag);
}
}
12 changes: 6 additions & 6 deletions MediaStreamTrack.js
Expand Up @@ -70,13 +70,9 @@ class MediaStreamTrack extends EventTarget(MEDIA_STREAM_TRACK_EVENTS) {
}

stop() {
if (this.remote) {
return;
}
WebRTCModule.mediaStreamTrackStop(this.id);
this._enabled = false;
WebRTCModule.mediaStreamTrackSetEnabled(this.id, false);
this.readyState = 'ended';
this.muted = !this._enabled;
// TODO: save some stopped flag?
}

/**
Expand Down Expand Up @@ -115,6 +111,10 @@ class MediaStreamTrack extends EventTarget(MEDIA_STREAM_TRACK_EVENTS) {
getSettings() {
throw new Error('Not implemented.');
}

release() {
WebRTCModule.mediaStreamTrackRelease(this.id);
}
}

export default MediaStreamTrack;
29 changes: 15 additions & 14 deletions RTCPeerConnection.js
Expand Up @@ -101,16 +101,21 @@ export default class RTCPeerConnection extends EventTarget(PEER_CONNECTION_EVENT
}

addStream(stream: MediaStream) {
WebRTCModule.peerConnectionAddStream(stream.reactTag, this._peerConnectionId);
this._localStreams.push(stream);
const index = this._localStreams.indexOf(stream);
if (index !== -1) {
return;
}
WebRTCModule.peerConnectionAddStream(stream._reactTag, this._peerConnectionId);
this._localStreams.push(stream);
}

removeStream(stream: MediaStream) {
WebRTCModule.peerConnectionRemoveStream(stream.reactTag, this._peerConnectionId);
let index = this._localStreams.indexOf(stream);
if (index !== -1) {
const index = this._localStreams.indexOf(stream);
if (index === -1) {
return;
}
this._localStreams.splice(index, 1);
}
WebRTCModule.peerConnectionRemoveStream(stream._reactTag, this._peerConnectionId);
}

createOffer(options = DEFAULT_OFFER_OPTIONS) {
Expand Down Expand Up @@ -240,7 +245,7 @@ export default class RTCPeerConnection extends EventTarget(PEER_CONNECTION_EVENT
_getTrack(streamReactTag, trackId): MediaStreamTrack {
const stream
= this._remoteStreams.find(
stream => stream.reactTag === streamReactTag);
stream => stream._reactTag === streamReactTag);

return stream && stream._tracks.find(track => track.id === trackId);
}
Expand Down Expand Up @@ -280,22 +285,18 @@ export default class RTCPeerConnection extends EventTarget(PEER_CONNECTION_EVENT
if (ev.id !== this._peerConnectionId) {
return;
}
const stream = new MediaStream(ev.streamId, ev.streamReactTag);
const tracks = ev.tracks;
for (let i = 0; i < tracks.length; i++) {
stream.addTrack(new MediaStreamTrack(tracks[i]));
}
const stream = new MediaStream(ev);
this._remoteStreams.push(stream);
this.dispatchEvent(new MediaStreamEvent('addstream', {stream}));
}),
DeviceEventEmitter.addListener('peerConnectionRemovedStream', ev => {
if (ev.id !== this._peerConnectionId) {
return;
}
const stream = this._remoteStreams.find(s => s.reactTag === ev.streamId);
const stream = this._remoteStreams.find(s => s._reactTag === ev.streamId);
if (stream) {
const index = this._remoteStreams.indexOf(stream);
if (index > -1) {
if (index !== -1) {
this._remoteStreams.splice(index, 1);
}
}
Expand Down
45 changes: 22 additions & 23 deletions android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java
Expand Up @@ -217,30 +217,10 @@ void mediaStreamTrackSetEnabled(String trackId, final boolean enabled) {
}
}

void mediaStreamTrackStop(String id) {
MediaStreamTrack track = getTrack(id);
if (track == null) {
Log.d(
TAG,
"mediaStreamTrackStop() No local MediaStreamTrack with id "
+ id);
return;
}
track.setEnabled(false);
removeTrack(id);
}

private void removeTrack(String id) {
void disposeTrack(String id) {
TrackPrivate track = tracks.remove(id);
if (track != null) {
VideoCaptureController videoCaptureController
= track.videoCaptureController;
if (videoCaptureController != null) {
if (videoCaptureController.stopCapture()) {
videoCaptureController.dispose();
}
}
track.mediaSource.dispose();
track.dispose();
}
}

Expand Down Expand Up @@ -269,13 +249,18 @@ private static class TrackPrivate {
*/
public final VideoCaptureController videoCaptureController;

/**
* Whether this object has been disposed or not.
*/
private boolean disposed;

/**
* Initializes a new {@code TrackPrivate} instance.
*
* @param track
* @param mediaSource the {@code MediaSource} from which the specified
* {@code code} was created
* @param videoCapturer the {@code VideoCapturer} from which the
* @param videoCaptureController the {@code VideoCaptureController} from which the
* specified {@code mediaSource} was created if the specified
* {@code track} is a {@link VideoTrack}
*/
Expand All @@ -286,6 +271,20 @@ public TrackPrivate(
this.track = track;
this.mediaSource = mediaSource;
this.videoCaptureController = videoCaptureController;
this.disposed = false;
}

public void dispose() {
if (!disposed) {
if (videoCaptureController != null) {
if (videoCaptureController.stopCapture()) {
videoCaptureController.dispose();
}
}
mediaSource.dispose();
track.dispose();
disposed = true;
}
}
}
}
Expand Up @@ -343,8 +343,7 @@ public void onAddStream(MediaStream mediaStream) {
// MediaStream instance with the label default that the implementation
// reuses.
if ("default".equals(streamId)) {
for (Map.Entry<String, MediaStream> e
: remoteStreams.entrySet()) {
for (Map.Entry<String, MediaStream> e : remoteStreams.entrySet()) {
if (e.getValue().equals(mediaStream)) {
streamReactTag = e.getKey();
break;
Expand Down

0 comments on commit 44ecfdd

Please sign in to comment.