Commit 55a6f822 authored by Henrik Boström's avatar Henrik Boström Committed by Commit Bot

Define legacy getLocalStreams() on top of track-based API.

getLocalStreams() is redefined from a list of streams modified by
addStream() and removeStream() to "all streams of all senders".
When using the stream-based APIs, getLocalStreams() behave exactly
like it did before this CL. Because senders are added or removed by
addTrack() and removeTrack(), this CL makes it possible for
spec-compliant APIs to modify the legacy getLocalStreams(), which was
not possible before this CL.

In follow-up CLs addStream() and removeStream() will also be implemented
on top of the track-based APIs and RTCPeerConnectionHandler code will be
removed.

This CL adds LayoutTests for expected behavior of legacy stream-based
APIs when fully implemented on top of track-based APIs. This includes
the first tests for interaction between legacy and non-legacy APIs.

Design doc: https://docs.google.com/document/d/1Obbeg-B4_04twVctHdf7C7vkUQrGlLvOaV0C8J5_-Gs/edit?usp=sharing

Bug: 803021, 738929
Change-Id: Ibab3f8329809437b6058c3350cd11e747054efd2
Reviewed-on: https://chromium-review.googlesource.com/893385Reviewed-by: default avatarHarald Alvestrand <hta@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533656}
parent 9b477bca
......@@ -1798,7 +1798,12 @@ void RTCPeerConnectionHandler::RemoveStream(
break;
}
}
DCHECK(webrtc_stream.get());
// If the stream was added using addTrack() we might not find it here. When
// addStream() and removeStream() is implemented on top of addTrack() and
// removeTrack() this won't be a problem and this code will go away.
// https://crbug.com/738929
if (!webrtc_stream)
return;
// TODO(tommi): Make this async (PostTaskAndReply).
native_peer_connection_->RemoveStream(webrtc_stream.get());
......
......@@ -12,6 +12,6 @@ PASS removeTrack() makes stream.onremovetrack fire and the track to be removed f
PASS stream.onremovetrack fires before setRemoteDescription resolves.
PASS removeTrack() makes track.onmute fire and the track to be muted.
PASS track.onmute fires before setRemoteDescription resolves.
PASS removeTrack() twice is safe.
FAIL removeTrack() twice is safe. Failed to execute 'removeTrack' on 'RTCPeerConnection': The sender was not created by this peer connection.
Harness: the test ran to completion.
......@@ -27,10 +27,7 @@ promise_test(function() {
let track = stream.getAudioTracks()[0];
let sender = pc.addTrack(track, stream);
assert_equals(sender.track, track);
// TODO(hbos): |addTrack| does not add to the set of local streams. When
// |getLocalStreams| is updated to return the streams of all senders this
// would have an observable effect here. https://crbug.com/738918
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
});
}, 'addTrack() for a single track and its stream.');
......@@ -43,10 +40,7 @@ promise_test(function() {
let streamB = streams[1];
let sender = pc.addTrack(trackA, streamB);
assert_equals(sender.track, trackA);
// TODO(hbos): |addTrack| does not add to the set of local streams. When
// |getLocalStreams| is updated to return the streams of all senders this
// would have an observable effect here. https://crbug.com/738918
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getLocalStreams(), [ streamB ]);
});
}, 'addTrack() for a single track and a different stream.');
......@@ -120,11 +114,12 @@ promise_test(function() {
assert_equals(sender.track, track);
assert_array_equals(pc.getSenders(), [ sender ]);
assert_array_equals(pc.getLocalStreams(), []);
// This is a NO-OP because there already is a sender for this track.
pc.addStream(stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
// The interaction between |addStream| and |addTrack| is not standardized
// (|addStream| is non-standard). In our implementation, the existing
// sender is reused by |addStream|.
// The sender is not associated with the stream, so |addStream| has no
// effect in this case. Otherwise it would already have been part of
// getLocalStreams().
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), [ sender ]);
});
}, 'addTrack() before addStream() works.');
......
This is a testharness.js-based test.
PASS addStream() adds to local streams and senders.
PASS addTrack() adds to local streams and senders.
PASS addTrack() fails after addStream().
PASS addStream() after addTrack() adds the remaining track.
PASS Adding a track to an addStream()-stream adds it to the PC.
PASS Removing a track from an addStream()-stream removes it from the PC.
PASS The PC stops observing the stream after removeStream().
PASS removeStream() after addStream() removes from local streams and senders.
FAIL removeStream() after addTrack() removes from local streams and senders. assert_array_equals: lengths differ, expected 0 got 1
PASS removeStream() after removeTrack() removes remaining tracks.
PASS removeTrack() after addStream() removes from local streams and senders.
PASS removeTrack() after addTrack() removes from local streams and senders.
PASS createDTMFSender() with addStream()-track.
FAIL createDTMFSender() with addTrack()-track. promise_test: Unhandled rejection with value: object "SyntaxError: Failed to execute 'createDTMFSender' on 'RTCPeerConnection': No local stream is available for the track provided."
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
// This is not an external/wpt/webrtc/ test because it tests APIs and behaviors
// that are not in the spec.
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
pc.addStream(stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_equals(pc.getSenders().length, 1);
assert_equals(pc.getSenders()[0].track, stream.getTracks()[0]);
}, 'addStream() adds to local streams and senders.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
let sender = pc.addTrack(stream.getTracks()[0], stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_array_equals(pc.getSenders(), [ sender ]);
}, 'addTrack() adds to local streams and senders.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
pc.addStream(stream);
try {
pc.addTrack(stream.getTracks()[0])
assert_unreached('addTrack() should have failed.');
} catch (e) {
assert_equals(e.name, 'InvalidAccessError');
assert_array_equals(pc.getLocalStreams(), [ stream ]);
};
}, 'addTrack() fails after addStream().');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true,
video:true});
let sender = pc.addTrack(stream.getTracks()[0]);
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), [ sender ]);
pc.addStream(stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
let otherSender = pc.getSenders().find(s => {
return s.track == stream.getTracks()[1];
});
assert_true(otherSender != null);
assert_equals(pc.getSenders().length, 2);
}, 'addStream() after addTrack() adds the remaining track.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true,
video:true});
let videoTrack = stream.getVideoTracks()[0];
stream.removeTrack(videoTrack);
pc.addStream(stream);
assert_true(
pc.getSenders().find(s => { return s.track == videoTrack; }) == null,
'PC does not know about the videoTrack before stream.addTrack()');
stream.addTrack(videoTrack);
assert_true(
pc.getSenders().find(s => { return s.track == videoTrack; }) != null,
'PC knows about the videoTrack after stream.addTrack()');
}, 'Adding a track to an addStream()-stream adds it to the PC.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true,
video:true});
let videoTrack = stream.getVideoTracks()[0];
pc.addStream(stream);
assert_true(
pc.getSenders().find(s => { return s.track == videoTrack; }) != null,
'PC knows about the track before stream.removeTrack()');
stream.removeTrack(videoTrack);
assert_true(
pc.getSenders().find(s => { return s.track == videoTrack; }) == null,
'PC does not know about the track after stream.removeTrack()');
}, 'Removing a track from an addStream()-stream removes it from the PC.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true,
video:true});
let videoTrack = stream.getVideoTracks()[0];
stream.removeTrack(videoTrack);
pc.addStream(stream);
pc.removeStream(stream);
assert_true(
pc.getSenders().find(s => { return s.track == videoTrack; }) == null,
'PC does not know about the videoTrack before stream.addTrack()');
stream.addTrack(videoTrack);
assert_true(
pc.getSenders().find(s => { return s.track == videoTrack; }) == null,
'PC does not know about the videoTrack after stream.addTrack()');
}, 'The PC stops observing the stream after removeStream().');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
pc.addStream(stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_equals(pc.getSenders().length, 1);
pc.removeStream(stream);
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), []);
}, 'removeStream() after addStream() removes from local streams and senders.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
pc.addTrack(stream.getTracks()[0], stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_equals(pc.getSenders().length, 1);
pc.removeStream(stream);
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), []);
}, 'removeStream() after addTrack() removes from local streams and senders.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true,
video:true});
pc.addStream(stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
let senders = pc.getSenders();
assert_equals(senders.length, 2);
pc.removeTrack(senders[0]);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_array_equals(pc.getSenders(), [ senders[1] ]);
pc.removeStream(stream);
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), []);
}, 'removeStream() after removeTrack() removes remaining tracks.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
pc.addStream(stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_equals(pc.getSenders().length, 1);
pc.removeTrack(pc.getSenders()[0]);
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), []);
}, 'removeTrack() after addStream() removes from local streams and senders.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
let sender = pc.addTrack(stream.getTracks()[0], stream);
assert_array_equals(pc.getLocalStreams(), [ stream ]);
assert_array_equals(pc.getSenders(), [ sender ]);
pc.removeTrack(sender);
assert_array_equals(pc.getLocalStreams(), []);
assert_array_equals(pc.getSenders(), []);
}, 'removeTrack() after addTrack() removes from local streams and senders.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
pc.addStream(stream);
pc.createDTMFSender(stream.getTracks()[0]);
}, 'createDTMFSender() with addStream()-track.');
promise_test(async function() {
let pc = new RTCPeerConnection();
let stream = await navigator.mediaDevices.getUserMedia({audio:true});
let track = stream.getTracks()[0];
pc.addTrack(track);
pc.createDTMFSender(track);
}, 'createDTMFSender() with addTrack()-track.');
</script>
</body>
</html>
......@@ -1144,7 +1144,7 @@ void RTCPeerConnection::addStream(ScriptState* script_state,
return;
}
if (local_streams_.Contains(stream))
if (getLocalStreams().Contains(stream))
return;
MediaErrorState media_error_state;
......@@ -1156,7 +1156,6 @@ void RTCPeerConnection::addStream(ScriptState* script_state,
return;
}
local_streams_.push_back(stream);
stream->RegisterObserver(this);
for (auto& track : stream->getTracks()) {
DCHECK(track->Component());
......@@ -1164,14 +1163,12 @@ void RTCPeerConnection::addStream(ScriptState* script_state,
}
bool valid = peer_handler_->AddStream(stream->Descriptor(), constraints);
if (!valid)
if (!valid) {
exception_state.ThrowDOMException(kSyntaxError,
"Unable to add the provided stream.");
// Ensure |rtp_senders_| is up-to-date so that |addTrack| knows if a sender
// already exists for a track. When |addStream| and |removeStream| are
// are implemented using |addTrack| and |removeTrack| we can simply add and
// remove senders there instead. https://crbug.com/738929
getSenders();
return;
}
CreateMissingSendersForStream(stream);
}
void RTCPeerConnection::removeStream(MediaStream* stream,
......@@ -1186,21 +1183,24 @@ void RTCPeerConnection::removeStream(MediaStream* stream,
return;
}
size_t pos = local_streams_.Find(stream);
if (pos == kNotFound)
if (!getLocalStreams().Contains(stream))
return;
local_streams_.EraseAt(pos);
stream->UnregisterObserver(this);
peer_handler_->RemoveStream(stream->Descriptor());
RemoveUnusedSenders();
}
MediaStreamVector RTCPeerConnection::getLocalStreams() const {
// TODO(hbos): We should define this as "the streams of all senders" instead
// of a set that we add to and subtract from on |addStream| and
// |removeStream|. https://crbug.com/738918
return local_streams_;
MediaStreamVector local_streams;
for (const auto& rtp_sender : getSenders()) {
for (const auto& stream : rtp_sender->streams()) {
if (!local_streams.Contains(stream))
local_streams.push_back(stream);
}
}
return local_streams;
}
MediaStreamVector RTCPeerConnection::getRemoteStreams() const {
......@@ -1267,33 +1267,24 @@ ScriptPromise RTCPeerConnection::getStats(ScriptState* script_state) {
return promise;
}
HeapVector<Member<RTCRtpSender>> RTCPeerConnection::getSenders() {
HeapVector<Member<RTCRtpSender>> RTCPeerConnection::getSenders() const {
HeapVector<Member<RTCRtpSender>> rtp_senders;
// |rtp_senders_| and lower layer GetSenders() should match, but we still
// invoke GetSenders() to get a predictable list order.
// TODO(hbos): Change |rtp_senders_| from a map to a list and stop relying on
// GetSenders(). https://crbug.com/803021
WebVector<std::unique_ptr<WebRTCRtpSender>> web_rtp_senders =
peer_handler_->GetSenders();
HeapVector<Member<RTCRtpSender>> rtp_senders(web_rtp_senders.size());
for (size_t i = 0; i < web_rtp_senders.size(); ++i) {
uintptr_t id = web_rtp_senders[i]->Id();
const auto it = rtp_senders_.find(id);
if (it != rtp_senders_.end()) {
rtp_senders[i] = it->value;
} else {
// There does not exist an |RTCRtpSender| for this |WebRTCRtpSender|
// yet, create it.
MediaStreamTrack* track = nullptr;
if (web_rtp_senders[i]->Track()) {
track = GetTrack(web_rtp_senders[i]->Track());
DCHECK(track);
}
RTCRtpSender* rtp_sender =
new RTCRtpSender(this, std::move(web_rtp_senders[i]), track);
rtp_senders_.insert(id, rtp_sender);
rtp_senders[i] = rtp_sender;
}
DCHECK(it != rtp_senders_.end());
rtp_senders.push_back(it->value);
}
return rtp_senders;
}
HeapVector<Member<RTCRtpReceiver>> RTCPeerConnection::getReceivers() {
HeapVector<Member<RTCRtpReceiver>> RTCPeerConnection::getReceivers() const {
return rtp_receivers_;
}
......@@ -1336,7 +1327,7 @@ RTCRtpSender* RTCPeerConnection::addTrack(MediaStreamTrack* track,
uintptr_t id = web_rtp_sender->Id();
DCHECK(rtp_senders_.find(id) == rtp_senders_.end());
RTCRtpSender* rtp_sender =
new RTCRtpSender(this, std::move(web_rtp_sender), track);
new RTCRtpSender(this, std::move(web_rtp_sender), track, streams);
tracks_.insert(track->Component(), track);
rtp_senders_.insert(id, rtp_sender);
return rtp_sender;
......@@ -1366,9 +1357,7 @@ void RTCPeerConnection::removeTrack(RTCRtpSender* sender,
// being nulled.
DCHECK(!sender->web_sender()->Track());
sender->SetTrack(nullptr);
// TODO(hbos): When |addStream| and |removeStream| are implemented using
// |addTrack| and |removeTrack|, we should remove |sender| from |rtp_senders_|
// here. https://crbug.com/738929
rtp_senders_.erase(sender->web_sender()->Id());
}
RTCDataChannel* RTCPeerConnection::createDataChannel(
......@@ -1426,6 +1415,50 @@ HeapVector<Member<RTCRtpReceiver>>::iterator RTCPeerConnection::FindReceiver(
return rtp_receivers_.end();
}
void RTCPeerConnection::CreateMissingSendersForStream(MediaStream* stream) {
WebVector<std::unique_ptr<WebRTCRtpSender>> web_rtp_senders =
peer_handler_->GetSenders();
for (size_t i = 0; i < web_rtp_senders.size(); ++i) {
uintptr_t id = web_rtp_senders[i]->Id();
const auto it = rtp_senders_.find(id);
// Any senders created by other means such as addTrack() will already exist
// in |rtp_senders_| and not be examined further.
if (it != rtp_senders_.end())
continue;
// Any sender that exist in the lower layer but not in blink must have been
// created as a result of the operation that called
// CreateMissingSendersForStream(). We create a blink sender under the
// assumption that it is associated with |stream|.
DCHECK(web_rtp_senders[i]->Track());
MediaStreamTrack* track = GetTrack(web_rtp_senders[i]->Track());
DCHECK(track);
DCHECK(stream->getTracks().Contains(track));
HeapVector<Member<MediaStream>> streams;
streams.push_back(stream);
RTCRtpSender* rtp_sender =
new RTCRtpSender(this, std::move(web_rtp_senders[i]), track, streams);
rtp_senders_.insert(id, rtp_sender);
}
}
void RTCPeerConnection::RemoveUnusedSenders() {
std::set<uintptr_t> used_sender_ids;
for (const auto& web_rtp_sender : peer_handler_->GetSenders()) {
used_sender_ids.insert(web_rtp_sender->Id());
}
// HeapHashMap does not support remove-while-iterating so we save all the IDs
// of senders we want to erase.
std::set<uintptr_t> unused_sender_ids;
for (const auto& rtp_sender : rtp_senders_.Values()) {
uintptr_t id = rtp_sender->web_sender()->Id();
if (used_sender_ids.find(id) == used_sender_ids.end())
unused_sender_ids.insert(id);
}
for (auto sender_id : unused_sender_ids) {
rtp_senders_.erase(sender_id);
}
}
RTCDTMFSender* RTCPeerConnection::createDTMFSender(
MediaStreamTrack* track,
ExceptionState& exception_state) {
......@@ -1435,7 +1468,7 @@ RTCDTMFSender* RTCPeerConnection::createDTMFSender(
DCHECK(track);
bool is_local_stream_track = false;
for (const auto& local_stream : local_streams_) {
for (const auto& local_stream : getLocalStreams()) {
if (local_stream->getTracks().Contains(track)) {
is_local_stream_track = true;
break;
......@@ -1467,10 +1500,13 @@ void RTCPeerConnection::OnStreamAddTrack(MediaStream* stream,
DCHECK(track->Component());
// Insert if not already present.
tracks_.insert(track->Component(), track);
CreateMissingSendersForStream(stream);
}
void RTCPeerConnection::OnStreamRemoveTrack(MediaStream* stream,
MediaStreamTrack* track) {
// Remove any senders that was removed from the lower layers as a result.
RemoveUnusedSenders();
// Don't remove |track| from |tracks_|, it may be referenced by another
// component. |tracks_| uses weak members and will automatically have |track|
// removed if destroyed.
......@@ -1811,7 +1847,6 @@ void RTCPeerConnection::RecordRapporMetrics() {
}
void RTCPeerConnection::Trace(blink::Visitor* visitor) {
visitor->Trace(local_streams_);
visitor->Trace(tracks_);
visitor->Trace(rtp_senders_);
visitor->Trace(rtp_receivers_);
......
......@@ -159,8 +159,8 @@ class MODULES_EXPORT RTCPeerConnection final
MediaStreamTrack* selector = nullptr);
ScriptPromise getStats(ScriptState*);
HeapVector<Member<RTCRtpSender>> getSenders();
HeapVector<Member<RTCRtpReceiver>> getReceivers();
HeapVector<Member<RTCRtpSender>> getSenders() const;
HeapVector<Member<RTCRtpReceiver>> getReceivers() const;
RTCRtpSender* addTrack(MediaStreamTrack*, MediaStreamVector, ExceptionState&);
void removeTrack(RTCRtpSender*, ExceptionState&);
DEFINE_ATTRIBUTE_EVENT_LISTENER(track);
......@@ -261,6 +261,19 @@ class MODULES_EXPORT RTCPeerConnection final
MediaStreamTrack* GetTrack(const WebMediaStreamTrack&) const;
HeapVector<Member<RTCRtpReceiver>>::iterator FindReceiver(
const WebRTCRtpReceiver& web_receiver);
// addStream() and removeStream() result in senders being added or removed.
// When a legacy stream API has been invoked, these methods must be invoked to
// make blink layer |rtp_senders_| up-to-date. The assumption of
// CreateMissingSendersForStream() is that any lower layer senders found that
// don't exist in blink was just created for the specified stream.
// RemoveUnusedSenders() removes all blink senders that don't exist in the
// lower layers.
// TODO(hbos): Stop using legacy stream APIs and remove these methods. When
// addStream() and removeStream() are implemented on top of track-based APIs
// we don't have to do these tricks to keep |rtp_senders_| up-to-date.
// https://crbug.com/738929
void CreateMissingSendersForStream(MediaStream*);
void RemoveUnusedSenders();
// The "Change" methods set the state asynchronously and fire the
// corresponding event immediately after changing the state (if it was really
......@@ -302,10 +315,8 @@ class MODULES_EXPORT RTCPeerConnection final
ICEGatheringState ice_gathering_state_;
ICEConnectionState ice_connection_state_;
MediaStreamVector local_streams_;
// A map containing any track that is in use by the peer connection. This
// includes tracks of |local_streams_|, |remote_streams_|, |rtp_senders_| and
// |rtp_receivers_|.
// includes tracks of |rtp_senders_| and |rtp_receivers_|.
HeapHashMap<WeakMember<MediaStreamComponent>, WeakMember<MediaStreamTrack>>
tracks_;
HeapHashMap<uintptr_t, Member<RTCRtpSender>> rtp_senders_;
......
......@@ -51,8 +51,12 @@ class ReplaceTrackRequest : public RTCVoidRequest {
RTCRtpSender::RTCRtpSender(RTCPeerConnection* pc,
std::unique_ptr<WebRTCRtpSender> sender,
MediaStreamTrack* track)
: pc_(pc), sender_(std::move(sender)), track_(track) {
MediaStreamTrack* track,
MediaStreamVector streams)
: pc_(pc),
sender_(std::move(sender)),
track_(track),
streams_(std::move(streams)) {
DCHECK(pc_);
DCHECK(sender_);
DCHECK(track_);
......@@ -88,9 +92,14 @@ void RTCRtpSender::SetTrack(MediaStreamTrack* track) {
track_ = track;
}
MediaStreamVector RTCRtpSender::streams() const {
return streams_;
}
void RTCRtpSender::Trace(blink::Visitor* visitor) {
visitor->Trace(pc_);
visitor->Trace(track_);
visitor->Trace(streams_);
ScriptWrappable::Trace(visitor);
}
......
......@@ -6,6 +6,7 @@
#define RTCRtpSender_h
#include "bindings/core/v8/ScriptPromise.h"
#include "modules/mediastream/MediaStream.h"
#include "platform/bindings/ScriptWrappable.h"
#include "platform/heap/GarbageCollected.h"
#include "platform/heap/Member.h"
......@@ -27,7 +28,8 @@ class RTCRtpSender final : public ScriptWrappable {
// https://github.com/w3c/webrtc-pc/issues/1712
RTCRtpSender(RTCPeerConnection*,
std::unique_ptr<WebRTCRtpSender>,
MediaStreamTrack*);
MediaStreamTrack*,
MediaStreamVector streams);
MediaStreamTrack* track();
ScriptPromise replaceTrack(ScriptState*, MediaStreamTrack*);
......@@ -36,6 +38,7 @@ class RTCRtpSender final : public ScriptWrappable {
// Sets the track. This must be called when the |WebRTCRtpSender| has its
// track updated, and the |track| must match the |WebRTCRtpSender::Track|.
void SetTrack(MediaStreamTrack*);
MediaStreamVector streams() const;
virtual void Trace(blink::Visitor*);
......@@ -43,6 +46,7 @@ class RTCRtpSender final : public ScriptWrappable {
Member<RTCPeerConnection> pc_;
std::unique_ptr<WebRTCRtpSender> sender_;
Member<MediaStreamTrack> track_;
MediaStreamVector streams_;
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment