Bug 1032839 - replaceTrack API. r=smaug, r=jesup

This commit is contained in:
Jan-Ivar Bruaroey 2014-08-15 01:33:09 -04:00
parent 7b12f55430
commit 838b08d7a0
10 changed files with 182 additions and 3 deletions

View File

@ -303,6 +303,9 @@ function RTCPeerConnection() {
this._onCreateAnswerFailure = null;
this._onGetStatsSuccess = null;
this._onGetStatsFailure = null;
this._onReplaceTrackSender = null;
this._onReplaceTrackSuccess = null;
this._onReplaceTrackFailure = null;
this._pendingType = null;
this._localType = null;
@ -812,7 +815,8 @@ RTCPeerConnection.prototype = {
this._checkClosed();
this._impl.addTrack(track, stream);
let sender = this._win.RTCRtpSender._create(this._win,
new RTCRtpSender(this, track));
new RTCRtpSender(this, track,
stream));
this._senders.push({ sender: sender, stream: stream });
return sender;
},
@ -822,6 +826,24 @@ RTCPeerConnection.prototype = {
throw new this._win.DOMError("", "removeTrack not yet implemented");
},
_replaceTrack: function(sender, withTrack, onSuccess, onError) {
// TODO: Do a (sender._stream.getTracks().indexOf(track) == -1) check
// on both track args someday.
//
// The proposed API will be that both tracks must already be in the same
// stream. However, since our MediaStreams currently are limited to one
// track per type, we allow replacement with an outside track not already
// in the same stream.
//
// Since a track may be replaced more than once, the track being replaced
// may not be in the stream either, so we check neither arg right now.
this._onReplaceTrackSender = sender;
this._onReplaceTrackSuccess = onSuccess;
this._onReplaceTrackFailure = onError;
this._impl.replaceTrack(sender.track, withTrack, sender._stream);
},
close: function() {
if (this._closed) {
return;
@ -1306,6 +1328,15 @@ PeerConnectionObserver.prototype = {
{ track: track }));
},
onReplaceTrackSuccess: function() {
this._dompc.callCB(this._dompc._onReplaceTrackSuccess);
},
onReplaceTrackError: function(code, message) {
this._dompc.callCB(this._dompc._onReplaceTrackError,
new RTCError(code, message));
},
foundIceCandidate: function(cand) {
this.dispatchEvent(new this._dompc._win.RTCPeerConnectionIceEvent("icecandidate",
{ candidate: cand } ));
@ -1337,15 +1368,25 @@ RTCPeerConnectionStatic.prototype = {
},
};
function RTCRtpSender(pc, track) {
this.pc = pc;
function RTCRtpSender(pc, track, stream) {
this._pc = pc;
this.track = track;
this._stream = stream;
}
RTCRtpSender.prototype = {
classDescription: "RTCRtpSender",
classID: PC_SENDER_CID,
contractID: PC_SENDER_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
replaceTrack: function(withTrack, onSuccess, onError) {
this._pc._checkClosed();
this._pc._queueOrRun({
func: this._pc._replaceTrack,
args: [this, withTrack, onSuccess, onError],
wait: false
});
}
};
function RTCRtpReceiver(pc, track) {

View File

@ -87,6 +87,8 @@ skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabl
skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
[test_peerConnection_offerRequiresReceiveVideoAudio.html]
skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
[test_peerConnection_replaceTrack.html]
skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
[test_peerConnection_setLocalAnswerInHaveLocalOffer.html]
[test_peerConnection_setLocalAnswerInStable.html]
[test_peerConnection_setLocalOfferInHaveRemoteOffer.html]

View File

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="templates.js"></script>
<script type="application/javascript" src="turnConfig.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript;version=1.8">
createHTML({
bug: "1032839",
title: "Replace video track"
});
function isSenderOfTrack(sender) {
return sender.track == this;
}
// Test basically just verifies that success callback is called at this point
var test;
runNetworkTest(function () {
test = new PeerConnectionTest();
test.setMediaConstraints([{video: true}], [{video: true}]);
test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
test.chain.insertBefore("PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT", [[
"PC_LOCAL_REPLACE_VIDEOTRACK",
function (test) {
var stream = test.pcLocal._pc.getLocalStreams()[0];
var track = stream.getVideoTracks()[0];
var sender = test.pcLocal._pc.getSenders().find(isSenderOfTrack, track);
ok(sender, "track has a sender");
navigator.mozGetUserMedia({video:true, fake: true}, function(newStream) {
sender.replaceTrack(newStream.getVideoTracks()[0],
function() {
ok(true, "replaceTrack success callback is called");
test.next();
},
function(err) {
ok(false, "replaceTrack failed with error = " + err);
test.next();
});
},
function(err) {
ok(false, "mozGetUserMedia failed. error = " + err);
test.next();
});
}
]]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -44,6 +44,9 @@ interface PeerConnectionImpl {
[Throws]
void removeTrack(MediaStreamTrack track);
[Throws]
void replaceTrack(MediaStreamTrack thisTrack, MediaStreamTrack withTrack,
MediaStream stream);
[Throws]
void closeStreams();
sequence<MediaStream> getLocalStreams();

View File

@ -28,6 +28,10 @@ interface PeerConnectionObserver
void onGetStatsSuccess(optional RTCStatsReportInternal report);
void onGetStatsError(unsigned long name, DOMString message);
/* replaceTrack callbacks */
void onReplaceTrackSuccess();
void onReplaceTrackError(unsigned long name, DOMString message);
/* Data channel callbacks */
void notifyDataChannel(DataChannel channel);

View File

@ -11,4 +11,8 @@
JSImplementation="@mozilla.org/dom/rtpsender;1"]
interface RTCRtpSender {
readonly attribute MediaStreamTrack track;
void replaceTrack(MediaStreamTrack track,
VoidFunction successCallback,
RTCPeerConnectionErrorCallback failureCallback);
};

View File

@ -1568,6 +1568,46 @@ PeerConnectionImpl::RemoveTrack(MediaStreamTrack& aTrack) {
return NS_OK;
}
NS_IMETHODIMP
PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack,
MediaStreamTrack& aWithTrack,
DOMMediaStream& aStream) {
PC_AUTO_ENTER_API_CALL(true);
// TODO: Do an aStream.HasTrack() check on both track args someday.
//
// The proposed API will be that both tracks must already be in the same
// stream. However, since our MediaStreams currently are limited to one
// track per type, we allow replacement with an outside track not already
// in the same stream. This works because sync happens receiver-side and
// timestamps are tied to capture.
//
// Since a track may be replaced more than once, the track being replaced
// may not be in the stream either, so we check neither arg right now.
// Insert magic here.
bool success = true;
nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
if (!pco) {
return NS_ERROR_UNEXPECTED;
}
JSErrorResult rv;
if (success) {
pco->OnReplaceTrackSuccess(rv);
} else {
pco->OnReplaceTrackError(kInternalError,
ObString("Failed to replace track"),
rv);
}
if (rv.Failed()) {
CSFLogError(logTag, "Error firing replaceTrack callback");
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
/*
NS_IMETHODIMP
PeerConnectionImpl::SetRemoteFingerprint(const char* hash, const char* fingerprint)

View File

@ -384,6 +384,14 @@ public:
nsresult
AddTrack(mozilla::dom::MediaStreamTrack& aTrack, DOMMediaStream& aStream);
NS_IMETHODIMP_TO_ERRORRESULT(ReplaceTrack, ErrorResult &rv,
mozilla::dom::MediaStreamTrack& aThisTrack,
mozilla::dom::MediaStreamTrack& aWithTrack,
DOMMediaStream& aStream)
{
rv = ReplaceTrack(aThisTrack, aWithTrack, aStream);
}
nsresult GetPeerIdentity(nsAString& peerIdentity)
{
#ifdef MOZILLA_INTERNAL_API

View File

@ -84,6 +84,8 @@ public:
virtual NS_IMETHODIMP OnRemoveStream(ER&) = 0;
virtual NS_IMETHODIMP OnAddTrack(ER&) = 0;
virtual NS_IMETHODIMP OnRemoveTrack(ER&) = 0;
virtual NS_IMETHODIMP OnReplaceTrackSuccess(ER&) = 0;
virtual NS_IMETHODIMP OnReplaceTrackError(uint32_t code, const char *msg, ER&) = 0;
virtual NS_IMETHODIMP OnAddIceCandidateSuccess(ER&) = 0;
virtual NS_IMETHODIMP OnAddIceCandidateError(uint32_t code, const char *msg, ER&) = 0;
virtual NS_IMETHODIMP OnIceCandidate(uint16_t level, const char *mid,

View File

@ -273,6 +273,8 @@ public:
NS_IMETHODIMP OnRemoveStream(ER&);
NS_IMETHODIMP OnAddTrack(ER&);
NS_IMETHODIMP OnRemoveTrack(ER&);
NS_IMETHODIMP OnReplaceTrackSuccess(ER&);
NS_IMETHODIMP OnReplaceTrackError(uint32_t code, const char *msg, ER&);
NS_IMETHODIMP OnAddIceCandidateSuccess(ER&);
NS_IMETHODIMP OnAddIceCandidateError(uint32_t code, const char *msg, ER&);
NS_IMETHODIMP OnIceCandidate(uint16_t level, const char *mid, const char *cand, ER&);
@ -472,6 +474,18 @@ TestObserver::OnRemoveTrack(ER&)
return NS_OK;
}
NS_IMETHODIMP
TestObserver::OnReplaceTrackSuccess(ER&)
{
return NS_OK;
}
NS_IMETHODIMP
TestObserver::OnReplaceTrackError(uint32_t code, const char *message, ER&)
{
return NS_OK;
}
NS_IMETHODIMP
TestObserver::OnIceCandidate(uint16_t level,
const char * mid,