FrameTransformerProxy.cpp (9267B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #include "libwebrtcglue/FrameTransformerProxy.h" 8 9 #include <memory> 10 #include <string> 11 #include <utility> 12 13 #include "ErrorList.h" 14 #include "api/frame_transformer_interface.h" 15 #include "jsapi/RTCRtpScriptTransformer.h" 16 #include "libwebrtcglue/FrameTransformer.h" 17 #include "mozilla/Assertions.h" 18 #include "mozilla/Logging.h" 19 #include "mozilla/Maybe.h" 20 #include "mozilla/Mutex.h" 21 #include "mozilla/RefPtr.h" 22 #include "mozilla/dom/RTCRtpReceiver.h" 23 #include "mozilla/dom/RTCRtpSender.h" 24 #include "nsDebug.h" 25 #include "nsIEventTarget.h" 26 #include "nsIRunnable.h" 27 #include "nsISupports.h" 28 #include "nsThreadUtils.h" 29 #include "nscore.h" 30 31 namespace mozilla { 32 33 LazyLogModule gFrameTransformerProxyLog("FrameTransformerProxy"); 34 35 FrameTransformerProxy::FrameTransformerProxy() 36 : mMutex("FrameTransformerProxy::mMutex") {} 37 38 FrameTransformerProxy::~FrameTransformerProxy() = default; 39 40 void FrameTransformerProxy::SetScriptTransformer( 41 dom::RTCRtpScriptTransformer& aTransformer) { 42 MutexAutoLock lock(mMutex); 43 if (mReleaseScriptTransformerCalled) { 44 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Warning, 45 ("RTCRtpScriptTransformer is ready, but ReleaseScriptTransformer " 46 "has already been called.")); 47 // The mainthread side has torn down while the worker init was pending. 48 // Don't grab a reference to the worker thread, or the script transformer. 49 // Also, let the script transformer know that we do not need it after all. 50 aTransformer.NotifyReleased(); 51 return; 52 } 53 54 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info, 55 ("RTCRtpScriptTransformer is ready!")); 56 mWorkerThread = GetCurrentSerialEventTarget(); 57 MOZ_ASSERT(mWorkerThread); 58 59 MOZ_ASSERT(!mScriptTransformer); 60 mScriptTransformer = &aTransformer; 61 62 if (!mQueue.empty()) { 63 std::list<std::unique_ptr<webrtc::TransformableFrameInterface>> queue; 64 std::swap(queue, mQueue); 65 mWorkerThread->Dispatch(NS_NewRunnableFunction( 66 __func__, [this, self = RefPtr<FrameTransformerProxy>(this), 67 queue = std::move(queue)]() mutable { 68 if (NS_WARN_IF(!mScriptTransformer)) { 69 // Could happen due to errors. Is there some 70 // other processing we ought to do? 71 return; 72 } 73 while (!queue.empty()) { 74 mScriptTransformer->TransformFrame(std::move(queue.front())); 75 queue.pop_front(); 76 } 77 })); 78 } 79 } 80 81 Maybe<bool> FrameTransformerProxy::IsVideo() const { 82 MutexAutoLock lock(mMutex); 83 return mVideo; 84 } 85 86 void FrameTransformerProxy::ReleaseScriptTransformer() { 87 MutexAutoLock lock(mMutex); 88 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Debug, ("In %s", __FUNCTION__)); 89 if (mReleaseScriptTransformerCalled) { 90 return; 91 } 92 mReleaseScriptTransformerCalled = true; 93 94 if (mWorkerThread) { 95 mWorkerThread->Dispatch(NS_NewRunnableFunction( 96 __func__, [this, self = RefPtr<FrameTransformerProxy>(this)] { 97 if (mScriptTransformer) { 98 mScriptTransformer->NotifyReleased(); 99 mScriptTransformer = nullptr; 100 } 101 102 // Make sure cycles are broken; this unset might have been caused by 103 // something other than the sender/receiver being unset. 104 GetMainThreadSerialEventTarget()->Dispatch( 105 NS_NewRunnableFunction(__func__, [this, self] { 106 MutexAutoLock lock(mMutex); 107 mSender = nullptr; 108 mReceiver = nullptr; 109 })); 110 })); 111 mWorkerThread = nullptr; 112 } 113 } 114 115 void FrameTransformerProxy::SetLibwebrtcTransformer( 116 FrameTransformer* aLibwebrtcTransformer) { 117 MutexAutoLock lock(mMutex); 118 mLibwebrtcTransformer = aLibwebrtcTransformer; 119 if (mLibwebrtcTransformer) { 120 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info, 121 ("mLibwebrtcTransformer is now set!")); 122 mVideo = Some(mLibwebrtcTransformer->IsVideo()); 123 } 124 } 125 126 void FrameTransformerProxy::Transform( 127 std::unique_ptr<webrtc::TransformableFrameInterface> aFrame) { 128 MutexAutoLock lock(mMutex); 129 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Debug, ("In %s", __FUNCTION__)); 130 if (!mWorkerThread && !mReleaseScriptTransformerCalled) { 131 MOZ_LOG( 132 gFrameTransformerProxyLog, LogLevel::Info, 133 ("In %s, queueing frame because RTCRtpScriptTransformer is not ready", 134 __FUNCTION__)); 135 // We are still waiting for the script transformer to be created on the 136 // worker thread. 137 mQueue.push_back(std::move(aFrame)); 138 return; 139 } 140 141 if (mWorkerThread) { 142 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Debug, 143 ("Queueing call to RTCRtpScriptTransformer::TransformFrame")); 144 mWorkerThread->Dispatch(NS_NewRunnableFunction( 145 __func__, [this, self = RefPtr<FrameTransformerProxy>(this), 146 frame = std::move(aFrame)]() mutable { 147 if (NS_WARN_IF(!mScriptTransformer)) { 148 // Could happen due to errors. Is there some 149 // other processing we ought to do? 150 return; 151 } 152 mScriptTransformer->TransformFrame(std::move(frame)); 153 })); 154 } 155 } 156 157 void FrameTransformerProxy::OnTransformedFrame( 158 std::unique_ptr<webrtc::TransformableFrameInterface> aFrame) { 159 MutexAutoLock lock(mMutex); 160 // If the worker thread has changed, we drop the frame, to avoid frames 161 // arriving out of order. 162 if (mLibwebrtcTransformer) { 163 // This will lock, lock order is mMutex, FrameTransformer::mLibwebrtcMutex 164 mLibwebrtcTransformer->OnTransformedFrame(std::move(aFrame)); 165 } 166 } 167 168 void FrameTransformerProxy::SetSender(dom::RTCRtpSender* aSender) { 169 { 170 MutexAutoLock lock(mMutex); 171 MOZ_ASSERT(!mReceiver); 172 mSender = aSender; 173 } 174 if (!aSender) { 175 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info, ("Sender set to null")); 176 ReleaseScriptTransformer(); 177 } 178 } 179 180 void FrameTransformerProxy::SetReceiver(dom::RTCRtpReceiver* aReceiver) { 181 { 182 MutexAutoLock lock(mMutex); 183 MOZ_ASSERT(!mSender); 184 mReceiver = aReceiver; 185 } 186 if (!aReceiver) { 187 MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info, 188 ("Receiver set to null")); 189 ReleaseScriptTransformer(); 190 } 191 } 192 193 bool FrameTransformerProxy::RequestKeyFrame() { 194 { 195 // Spec wants this to reject synchronously if the RTCRtpScriptTransformer 196 // is not associated with a video receiver. This may change to an async 197 // check? 198 MutexAutoLock lock(mMutex); 199 if (!mReceiver || !mVideo.isSome() || !*mVideo) { 200 return false; 201 } 202 } 203 204 // Thread hop to main, and then the conduit thread-hops to the call thread. 205 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction( 206 __func__, [this, self = RefPtr<FrameTransformerProxy>(this)] { 207 MutexAutoLock lock(mMutex); 208 if (mReceiver && mVideo.isSome() && *mVideo) { 209 mReceiver->RequestKeyFrame(); 210 } 211 })); 212 return true; 213 } 214 215 void FrameTransformerProxy::KeyFrameRequestDone() { 216 MutexAutoLock lock(mMutex); 217 if (mWorkerThread) { 218 mWorkerThread->Dispatch(NS_NewRunnableFunction( 219 __func__, [this, self = RefPtr<FrameTransformerProxy>(this)] { 220 if (mScriptTransformer) { 221 mScriptTransformer->KeyFrameRequestDone(); 222 } 223 })); 224 } 225 } 226 227 bool FrameTransformerProxy::GenerateKeyFrame(const Maybe<std::string>& aRid) { 228 { 229 // Spec wants this to reject synchronously if the RTCRtpScriptTransformer 230 // is not associated with a video sender. This may change to an async 231 // check? 232 MutexAutoLock lock(mMutex); 233 if (!mSender || !mVideo.isSome() || !*mVideo) { 234 return false; 235 } 236 } 237 238 // Thread hop to main, and then the conduit thread-hops to the call thread. 239 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction( 240 __func__, [this, self = RefPtr<FrameTransformerProxy>(this), aRid] { 241 MutexAutoLock lock(mMutex); 242 if (!mSender || !mVideo.isSome() || !*mVideo || 243 !mSender->GenerateKeyFrame(aRid)) { 244 CopyableErrorResult rv; 245 rv.ThrowInvalidStateError("Not sending video"); 246 if (mWorkerThread) { 247 mWorkerThread->Dispatch(NS_NewRunnableFunction( 248 __func__, 249 [this, self = RefPtr<FrameTransformerProxy>(this), aRid, rv] { 250 if (mScriptTransformer) { 251 mScriptTransformer->GenerateKeyFrameError(aRid, rv); 252 } 253 })); 254 } 255 } 256 })); 257 return true; 258 } 259 260 void FrameTransformerProxy::GenerateKeyFrameError( 261 const Maybe<std::string>& aRid, const CopyableErrorResult& aResult) { 262 MutexAutoLock lock(mMutex); 263 if (mWorkerThread) { 264 mWorkerThread->Dispatch(NS_NewRunnableFunction( 265 __func__, 266 [this, self = RefPtr<FrameTransformerProxy>(this), aRid, aResult] { 267 if (mScriptTransformer) { 268 mScriptTransformer->GenerateKeyFrameError(aRid, aResult); 269 } 270 })); 271 } 272 } 273 274 } // namespace mozilla