WebRTC: Add support for RTCRtpSender.replaceTrack()
authoradam.bergkvist@ericsson.com <adam.bergkvist@ericsson.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 Jan 2016 19:22:28 +0000 (19:22 +0000)
committeradam.bergkvist@ericsson.com <adam.bergkvist@ericsson.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 Jan 2016 19:22:28 +0000 (19:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153063

Reviewed by Eric Carlson.

Source/WebCore:

Add the API and infrastructure to support RTCRtpSender.replaceTrack(). The platform is
reached through the RTCPeerConnection object that created the RTCRtpSender via a client
interface.

Test: fast/mediastream/RTCRtpSender-replaceTrack.html

* Modules/mediastream/MediaEndpointPeerConnection.cpp:
(WebCore::MediaEndpointPeerConnection::replaceTrack):
* Modules/mediastream/MediaEndpointPeerConnection.h:
* Modules/mediastream/PeerConnectionBackend.h:
* Modules/mediastream/RTCPeerConnection.cpp:
(WebCore::RTCPeerConnection::addTrack):
(WebCore::RTCPeerConnection::removeTrack):
(WebCore::RTCPeerConnection::replaceTrack):
* Modules/mediastream/RTCPeerConnection.h:
* Modules/mediastream/RTCRtpSender.cpp:
(WebCore::RTCRtpSender::RTCRtpSender):
(WebCore::RTCRtpSender::replaceTrack):
* Modules/mediastream/RTCRtpSender.h:
(WebCore::RTCRtpSenderClient::~RTCRtpSenderClient):
(WebCore::RTCRtpSender::create):
(WebCore::RTCRtpSender::trackId):
(WebCore::RTCRtpSender::stop):
* Modules/mediastream/RTCRtpSender.idl:

LayoutTests:

Add API test for RTCRtpSender.replaceTrack(). A successful call is still
rejected (promise) until proper support is available in the WebRTC backend.

* fast/mediastream/RTCRtpSender-replaceTrack-expected.txt: Added.
* fast/mediastream/RTCRtpSender-replaceTrack.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@194968 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/mediastream/RTCRtpSender-replaceTrack-expected.txt [new file with mode: 0644]
LayoutTests/fast/mediastream/RTCRtpSender-replaceTrack.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.cpp
Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.h
Source/WebCore/Modules/mediastream/PeerConnectionBackend.h
Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp
Source/WebCore/Modules/mediastream/RTCPeerConnection.h
Source/WebCore/Modules/mediastream/RTCRtpSender.cpp
Source/WebCore/Modules/mediastream/RTCRtpSender.h
Source/WebCore/Modules/mediastream/RTCRtpSender.idl

index d17974f..db82457 100644 (file)
@@ -1,3 +1,16 @@
+2016-01-13  Adam Bergkvist  <adam.bergkvist@ericsson.com>
+
+        WebRTC: Add support for RTCRtpSender.replaceTrack()
+        https://bugs.webkit.org/show_bug.cgi?id=153063
+
+        Reviewed by Eric Carlson.
+
+        Add API test for RTCRtpSender.replaceTrack(). A successful call is still
+        rejected (promise) until proper support is available in the WebRTC backend.
+
+        * fast/mediastream/RTCRtpSender-replaceTrack-expected.txt: Added.
+        * fast/mediastream/RTCRtpSender-replaceTrack.html: Added.
+
 2016-01-13  Mario Sanchez Prada  <mario@webkit.org>
 
         [GTK] Unreviewed gardening.
diff --git a/LayoutTests/fast/mediastream/RTCRtpSender-replaceTrack-expected.txt b/LayoutTests/fast/mediastream/RTCRtpSender-replaceTrack-expected.txt
new file mode 100644 (file)
index 0000000..2766952
--- /dev/null
@@ -0,0 +1,23 @@
+Test basic behavior of RTCRtpSender.replaceTrack()
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS pc.getSenders().length is 0
+PASS sender = pc.addTrack(audioTrack, stream) did not throw exception.
+PASS sender is an instance of RTCRtpSender
+PASS sender.track is audioTrack
+PASS promise sender.replaceTrack() rejected with TypeError: Not enough arguments
+PASS promise sender.replaceTrack(null) rejected with TypeError: Type error
+PASS promise sender.replaceTrack({}) rejected with TypeError: Argument 1 ('withTrack') to RTCRtpSender.replaceTrack must be an instance of MediaStreamTrack
+Test mismatching track kind
+PASS promise sender.replaceTrack(videoTrack) rejected with TypeError: Type error
+This is a valid call but will be rejected until the PeerConnectionBackend implementation has proper support
+PASS promise sender.replaceTrack(audioTrack2) rejected with [object DOMError]
+Stop sender, and try replacing the track
+PASS promise sender.replaceTrack(audioTrack2) rejected with [object DOMError]
+End of promise chain
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/mediastream/RTCRtpSender-replaceTrack.html b/LayoutTests/fast/mediastream/RTCRtpSender-replaceTrack.html
new file mode 100644 (file)
index 0000000..c013ccb
--- /dev/null
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+    <head>
+        <script src="../../resources/js-test-pre.js"></script>
+    </head>
+    <body>
+        <script>
+            var stream;
+            var audioTrack;
+            var audioTrack2;
+            var videoTrack;
+            var sender;
+
+            description("Test basic behavior of RTCRtpSender.replaceTrack()");
+
+            if (window.testRunner)
+                testRunner.setUserMediaPermission(true);
+            else {
+                debug("This test can not be run without the testRunner");
+                finishJSTest();
+            }
+
+            var pc = new webkitRTCPeerConnection({iceServers:[{urls:'stun:foo.com'}]});
+
+            navigator.mediaDevices.getUserMedia({ "audio": true, "video": true })
+            .then(function (s) {
+                stream = s;
+                audioTrack = stream.getAudioTracks()[0];
+                videoTrack = stream.getVideoTracks()[0];
+
+                audioTrack2 = audioTrack.clone();
+
+                shouldBe("pc.getSenders().length", "0");
+
+                shouldNotThrow("sender = pc.addTrack(audioTrack, stream)");
+                shouldBeType("sender", "RTCRtpSender");
+                shouldBe("sender.track", "audioTrack");
+
+                promiseShouldReject("sender.replaceTrack()")
+                .catch(function () {
+                    return promiseShouldReject("sender.replaceTrack(null)");
+                })
+                .catch(function () {
+                    return promiseShouldReject("sender.replaceTrack({})");
+                })
+                .catch(function () {
+                    debug("Test mismatching track kind");
+                    return promiseShouldReject("sender.replaceTrack(videoTrack)");
+                })
+                .catch(function () {
+                    debug("This is a valid call but will be rejected until the PeerConnectionBackend implementation has proper support");
+                    return promiseShouldReject("sender.replaceTrack(audioTrack2)");
+                })
+                .catch(function () {
+                    debug("Stop sender, and try replacing the track");
+                    pc.removeTrack(sender);
+                    return promiseShouldReject("sender.replaceTrack(audioTrack2)");
+                })
+                .catch(function () {
+                    debug("End of promise chain");
+                    finishJSTest();
+                });
+            })
+            .catch(function (error) {
+                testFailed("getUserMedia error: " + error);
+                finishJSTest();
+            });
+
+            function promiseShouldReject(expr) {
+                var p;
+                try {
+                    p = eval(expr);
+                } catch (e) {
+                    testFailed("evaluating " + expr + " threw exception " + e);
+                    return Promise.reject();
+                }
+
+                if (!(p instanceof Promise)) {
+                    testFailed(expr + " does not evaluate to a promise.");
+                    return Promise.reject();
+                }
+
+                p.then(function () {
+                    testFailed("promise " + expr + " fulfilled unexpectedly.");
+                });
+
+                p.catch(function (reason) {
+                    testPassed("promise " + expr + " rejected with " + reason);
+                });
+
+                return p;
+            }
+
+            window.jsTestIsAsync = true;
+            window.successfullyParsed = true;
+
+        </script>
+        <script src="../../resources/js-test-post.js"></script>
+    </body>
+</html>
index 83e5335..56cc765 100644 (file)
@@ -1,3 +1,35 @@
+2016-01-13  Adam Bergkvist  <adam.bergkvist@ericsson.com>
+
+        WebRTC: Add support for RTCRtpSender.replaceTrack()
+        https://bugs.webkit.org/show_bug.cgi?id=153063
+
+        Reviewed by Eric Carlson.
+
+        Add the API and infrastructure to support RTCRtpSender.replaceTrack(). The platform is
+        reached through the RTCPeerConnection object that created the RTCRtpSender via a client
+        interface.
+
+        Test: fast/mediastream/RTCRtpSender-replaceTrack.html
+
+        * Modules/mediastream/MediaEndpointPeerConnection.cpp:
+        (WebCore::MediaEndpointPeerConnection::replaceTrack):
+        * Modules/mediastream/MediaEndpointPeerConnection.h:
+        * Modules/mediastream/PeerConnectionBackend.h:
+        * Modules/mediastream/RTCPeerConnection.cpp:
+        (WebCore::RTCPeerConnection::addTrack):
+        (WebCore::RTCPeerConnection::removeTrack):
+        (WebCore::RTCPeerConnection::replaceTrack):
+        * Modules/mediastream/RTCPeerConnection.h:
+        * Modules/mediastream/RTCRtpSender.cpp:
+        (WebCore::RTCRtpSender::RTCRtpSender):
+        (WebCore::RTCRtpSender::replaceTrack):
+        * Modules/mediastream/RTCRtpSender.h:
+        (WebCore::RTCRtpSenderClient::~RTCRtpSenderClient):
+        (WebCore::RTCRtpSender::create):
+        (WebCore::RTCRtpSender::trackId):
+        (WebCore::RTCRtpSender::stop):
+        * Modules/mediastream/RTCRtpSender.idl:
+
 2016-01-13  Brady Eidson  <beidson@apple.com>
 
         Modern IDB: A few cursor tests are flaky because JS wrappers are GC'ed.
index ea25eb2..c34c376 100644 (file)
@@ -153,6 +153,17 @@ void MediaEndpointPeerConnection::getStats(MediaStreamTrack*, PeerConnection::St
     promise.reject(DOMError::create("NotSupportedError"));
 }
 
+void MediaEndpointPeerConnection::replaceTrack(RTCRtpSender& sender, MediaStreamTrack& withTrack, PeerConnection::VoidPromise&& promise)
+{
+    UNUSED_PARAM(sender);
+    UNUSED_PARAM(withTrack);
+    UNUSED_PARAM(promise);
+
+    notImplemented();
+
+    promise.reject(DOMError::create("NotSupportedError"));
+}
+
 void MediaEndpointPeerConnection::stop()
 {
     notImplemented();
index 40ce632..6cd3b18 100644 (file)
@@ -64,6 +64,8 @@ public:
 
     void getStats(MediaStreamTrack*, PeerConnection::StatsPromise&&) override;
 
+    void replaceTrack(RTCRtpSender&, MediaStreamTrack&, PeerConnection::VoidPromise&&) override;
+
     void stop() override;
 
     bool isNegotiationNeeded() const override { return false; };
index b95bd22..898d784 100644 (file)
@@ -101,6 +101,8 @@ public:
 
     virtual void getStats(MediaStreamTrack*, PeerConnection::StatsPromise&&) = 0;
 
+    virtual void replaceTrack(RTCRtpSender&, MediaStreamTrack&, PeerConnection::VoidPromise&&) = 0;
+
     virtual void stop() = 0;
 
     virtual bool isNegotiationNeeded() const = 0;
index 1dd65b2..f4f8899 100644 (file)
@@ -121,7 +121,7 @@ RefPtr<RTCRtpSender> RTCPeerConnection::addTrack(RefPtr<MediaStreamTrack>&& trac
     }
 
     for (auto& sender : m_senderSet) {
-        if (sender->track()->id() == track->id()) {
+        if (sender->trackId() == track->id()) {
             // FIXME: Spec says InvalidParameter
             ec = INVALID_MODIFICATION_ERR;
             return nullptr;
@@ -132,7 +132,7 @@ RefPtr<RTCRtpSender> RTCPeerConnection::addTrack(RefPtr<MediaStreamTrack>&& trac
     for (auto stream : streams)
         mediaStreamIds.append(stream->id());
 
-    RefPtr<RTCRtpSender> sender = RTCRtpSender::create(WTFMove(track), WTFMove(mediaStreamIds));
+    RefPtr<RTCRtpSender> sender = RTCRtpSender::create(WTFMove(track), WTFMove(mediaStreamIds), *this);
     m_senderSet.append(sender);
 
     m_backend->markAsNeedingNegotiation();
@@ -155,6 +155,8 @@ void RTCPeerConnection::removeTrack(RTCRtpSender* sender, ExceptionCode& ec)
     if (!m_senderSet.contains(sender))
         return;
 
+    sender->stop();
+
     m_backend->markAsNeedingNegotiation();
 }
 
@@ -427,6 +429,11 @@ void RTCPeerConnection::fireEvent(Event& event)
     dispatchEvent(event);
 }
 
+void RTCPeerConnection::replaceTrack(RTCRtpSender& sender, MediaStreamTrack& withTrack, PeerConnection::VoidPromise&& promise)
+{
+    m_backend->replaceTrack(sender, withTrack, WTFMove(promise));
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 9f9e875..28e91f6 100644 (file)
@@ -59,7 +59,7 @@ class RTCPeerConnectionErrorCallback;
 class RTCSessionDescription;
 class RTCStatsCallback;
 
-class RTCPeerConnection final : public RefCounted<RTCPeerConnection>, public ScriptWrappable, public PeerConnectionBackendClient, public EventTargetWithInlineData, public ActiveDOMObject {
+class RTCPeerConnection final : public RefCounted<RTCPeerConnection>, public ScriptWrappable, public PeerConnectionBackendClient, public RTCRtpSenderClient, public EventTargetWithInlineData, public ActiveDOMObject {
 public:
     static RefPtr<RTCPeerConnection> create(ScriptExecutionContext&, const Dictionary& rtcConfiguration, ExceptionCode&);
     ~RTCPeerConnection();
@@ -131,6 +131,9 @@ private:
     PeerConnectionStates::IceGatheringState internalIceGatheringState() const override { return m_iceGatheringState; }
     PeerConnectionStates::IceConnectionState internalIceConnectionState() const override { return m_iceConnectionState; }
 
+    // RTCRtpSenderClient
+    void replaceTrack(RTCRtpSender&, MediaStreamTrack&, PeerConnection::VoidPromise&&) override;
+
     PeerConnectionStates::SignalingState m_signalingState;
     PeerConnectionStates::IceGatheringState m_iceGatheringState;
     PeerConnectionStates::IceConnectionState m_iceConnectionState;
index 66d3740..f4aadc5 100644 (file)
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "DOMError.h"
+#include "ExceptionCode.h"
+#include "JSDOMError.h"
+
 namespace WebCore {
 
-RTCRtpSender::RTCRtpSender(RefPtr<MediaStreamTrack>&& track, Vector<String>&& mediaStreamIds)
+RTCRtpSender::RTCRtpSender(RefPtr<MediaStreamTrack>&& track, Vector<String>&& mediaStreamIds, RTCRtpSenderClient& client)
     : RTCRtpSenderReceiverBase(WTFMove(track))
     , m_mediaStreamIds(WTFMove(mediaStreamIds))
+    , m_client(&client)
+{
+    // The original track id is always used in negotiation even if the track is replaced.
+    m_trackId = m_track->id();
+}
+
+void RTCRtpSender::replaceTrack(MediaStreamTrack* withTrack, PeerConnection::VoidPromise&& promise, ExceptionCode& ec)
 {
+    if (!withTrack) {
+        ec = TypeError;
+        return;
+    }
+
+    if (!m_client) {
+        promise.reject(DOMError::create("InvalidStateError"));
+        return;
+    }
+
+    if (m_track->kind() != withTrack->kind()) {
+        ec = TypeError;
+        return;
+    }
+
+    m_client->replaceTrack(*this, *withTrack, WTFMove(promise));
 }
 
 } // namespace WebCore
index 257d326..9d9d0d4 100644 (file)
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "PeerConnectionBackend.h"
 #include "RTCRtpSenderReceiverBase.h"
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
+class RTCRtpSenderClient {
+public:
+    virtual void replaceTrack(RTCRtpSender&, MediaStreamTrack&, PeerConnection::VoidPromise&&) = 0;
+
+    virtual ~RTCRtpSenderClient() { }
+};
+
 class RTCRtpSender : public RTCRtpSenderReceiverBase {
 public:
-    static Ref<RTCRtpSender> create(RefPtr<MediaStreamTrack>&& track, Vector<String>&& mediaStreamIds)
+    static Ref<RTCRtpSender> create(RefPtr<MediaStreamTrack>&& track, Vector<String>&& mediaStreamIds, RTCRtpSenderClient& client)
     {
-        return adoptRef(*new RTCRtpSender(WTFMove(track), WTFMove(mediaStreamIds)));
+        return adoptRef(*new RTCRtpSender(WTFMove(track), WTFMove(mediaStreamIds), client));
     }
 
+    const String& trackId() { return m_trackId; }
     const Vector<String>& mediaStreamIds() const { return m_mediaStreamIds; }
 
+    void stop() { m_client = nullptr; }
+
+    void replaceTrack(MediaStreamTrack*, PeerConnection::VoidPromise&&, ExceptionCode&);
+
 private:
-    RTCRtpSender(RefPtr<MediaStreamTrack>&&, Vector<String>&& mediaStreamIds);
+    RTCRtpSender(RefPtr<MediaStreamTrack>&&, Vector<String>&& mediaStreamIds, RTCRtpSenderClient&);
 
+    String m_trackId;
     Vector<String> m_mediaStreamIds;
+    RTCRtpSenderClient* m_client;
 };
 
 } // namespace WebCore
index 8cb2755..30272df 100644 (file)
@@ -32,4 +32,6 @@
     Conditional=MEDIA_STREAM
 ] interface RTCRtpSender {
     readonly attribute MediaStreamTrack track;
+
+    [StrictTypeChecking, RaisesException] Promise replaceTrack(MediaStreamTrack withTrack);
 };