|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "CSFLog.h" |
|
6 #include "nspr.h" |
|
7 |
|
8 // For rtcp-fb constants |
|
9 #include "ccsdp.h" |
|
10 |
|
11 #include "VideoConduit.h" |
|
12 #include "AudioConduit.h" |
|
13 #include "nsThreadUtils.h" |
|
14 #include "LoadManager.h" |
|
15 #include "YuvStamper.h" |
|
16 #include "nsServiceManagerUtils.h" |
|
17 #include "nsIPrefService.h" |
|
18 #include "nsIPrefBranch.h" |
|
19 |
|
20 #include "webrtc/common_video/interface/native_handle.h" |
|
21 #include "webrtc/video_engine/include/vie_errors.h" |
|
22 |
|
23 #ifdef MOZ_WIDGET_ANDROID |
|
24 #include "AndroidJNIWrapper.h" |
|
25 #endif |
|
26 |
|
27 #include <algorithm> |
|
28 #include <math.h> |
|
29 |
|
30 namespace mozilla { |
|
31 |
|
32 static const char* logTag ="WebrtcVideoSessionConduit"; |
|
33 |
|
34 // 32 bytes is what WebRTC CodecInst expects |
|
35 const unsigned int WebrtcVideoConduit::CODEC_PLNAME_SIZE = 32; |
|
36 |
|
37 /** |
|
38 * Factory Method for VideoConduit |
|
39 */ |
|
40 mozilla::RefPtr<VideoSessionConduit> VideoSessionConduit::Create(VideoSessionConduit *aOther) |
|
41 { |
|
42 #ifdef MOZILLA_INTERNAL_API |
|
43 // unit tests create their own "main thread" |
|
44 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); |
|
45 #endif |
|
46 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
47 |
|
48 WebrtcVideoConduit* obj = new WebrtcVideoConduit(); |
|
49 if(obj->Init(static_cast<WebrtcVideoConduit*>(aOther)) != kMediaConduitNoError) |
|
50 { |
|
51 CSFLogError(logTag, "%s VideoConduit Init Failed ", __FUNCTION__); |
|
52 delete obj; |
|
53 return nullptr; |
|
54 } |
|
55 CSFLogDebug(logTag, "%s Successfully created VideoConduit ", __FUNCTION__); |
|
56 return obj; |
|
57 } |
|
58 |
|
59 WebrtcVideoConduit::~WebrtcVideoConduit() |
|
60 { |
|
61 #ifdef MOZILLA_INTERNAL_API |
|
62 // unit tests create their own "main thread" |
|
63 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); |
|
64 #endif |
|
65 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
66 |
|
67 for(std::vector<VideoCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++) |
|
68 { |
|
69 delete mRecvCodecList[i]; |
|
70 } |
|
71 |
|
72 delete mCurSendCodecConfig; |
|
73 |
|
74 // The first one of a pair to be deleted shuts down media for both |
|
75 //Deal with External Capturer |
|
76 if(mPtrViECapture) |
|
77 { |
|
78 if (!mShutDown) { |
|
79 mPtrViECapture->DisconnectCaptureDevice(mCapId); |
|
80 mPtrViECapture->ReleaseCaptureDevice(mCapId); |
|
81 mPtrExtCapture = nullptr; |
|
82 if (mOtherDirection) |
|
83 mOtherDirection->mPtrExtCapture = nullptr; |
|
84 } |
|
85 } |
|
86 |
|
87 //Deal with External Renderer |
|
88 if(mPtrViERender) |
|
89 { |
|
90 if (!mShutDown) { |
|
91 if(mRenderer) { |
|
92 mPtrViERender->StopRender(mChannel); |
|
93 } |
|
94 mPtrViERender->RemoveRenderer(mChannel); |
|
95 } |
|
96 } |
|
97 |
|
98 //Deal with the transport |
|
99 if(mPtrViENetwork) |
|
100 { |
|
101 if (!mShutDown) { |
|
102 mPtrViENetwork->DeregisterSendTransport(mChannel); |
|
103 } |
|
104 } |
|
105 |
|
106 if(mPtrViEBase) |
|
107 { |
|
108 if (!mShutDown) { |
|
109 mPtrViEBase->StopSend(mChannel); |
|
110 mPtrViEBase->StopReceive(mChannel); |
|
111 SyncTo(nullptr); |
|
112 mPtrViEBase->DeleteChannel(mChannel); |
|
113 } |
|
114 } |
|
115 |
|
116 if (mOtherDirection) |
|
117 { |
|
118 // mOtherDirection owns these now! |
|
119 mOtherDirection->mOtherDirection = nullptr; |
|
120 // let other side we terminated the channel |
|
121 mOtherDirection->mShutDown = true; |
|
122 mVideoEngine = nullptr; |
|
123 } else { |
|
124 // We can't delete the VideoEngine until all these are released! |
|
125 // And we can't use a Scoped ptr, since the order is arbitrary |
|
126 mPtrViEBase = nullptr; |
|
127 mPtrViECapture = nullptr; |
|
128 mPtrViECodec = nullptr; |
|
129 mPtrViENetwork = nullptr; |
|
130 mPtrViERender = nullptr; |
|
131 mPtrRTP = nullptr; |
|
132 mPtrExtCodec = nullptr; |
|
133 |
|
134 // only one opener can call Delete. Have it be the last to close. |
|
135 if(mVideoEngine) |
|
136 { |
|
137 webrtc::VideoEngine::Delete(mVideoEngine); |
|
138 } |
|
139 } |
|
140 } |
|
141 |
|
142 bool WebrtcVideoConduit::GetLocalSSRC(unsigned int* ssrc) { |
|
143 return !mPtrRTP->GetLocalSSRC(mChannel, *ssrc); |
|
144 } |
|
145 |
|
146 bool WebrtcVideoConduit::GetRemoteSSRC(unsigned int* ssrc) { |
|
147 return !mPtrRTP->GetRemoteSSRC(mChannel, *ssrc); |
|
148 } |
|
149 |
|
150 bool WebrtcVideoConduit::GetAVStats(int32_t* jitterBufferDelayMs, |
|
151 int32_t* playoutBufferDelayMs, |
|
152 int32_t* avSyncOffsetMs) { |
|
153 return false; |
|
154 } |
|
155 |
|
156 bool WebrtcVideoConduit::GetRTPStats(unsigned int* jitterMs, |
|
157 unsigned int* cumulativeLost) { |
|
158 unsigned short fractionLost; |
|
159 unsigned extendedMax; |
|
160 int rttMs; |
|
161 // GetReceivedRTCPStatistics is a poorly named GetRTPStatistics variant |
|
162 return !mPtrRTP->GetReceivedRTCPStatistics(mChannel, fractionLost, |
|
163 *cumulativeLost, |
|
164 extendedMax, |
|
165 *jitterMs, |
|
166 rttMs); |
|
167 } |
|
168 |
|
169 bool WebrtcVideoConduit::GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp, |
|
170 uint32_t* jitterMs, |
|
171 uint32_t* packetsReceived, |
|
172 uint64_t* bytesReceived, |
|
173 uint32_t* cumulativeLost, |
|
174 int32_t* rttMs) { |
|
175 uint32_t ntpHigh, ntpLow; |
|
176 uint16_t fractionLost; |
|
177 bool result = !mPtrRTP->GetRemoteRTCPReceiverInfo(mChannel, ntpHigh, ntpLow, |
|
178 *packetsReceived, |
|
179 *bytesReceived, |
|
180 jitterMs, |
|
181 &fractionLost, |
|
182 cumulativeLost, |
|
183 rttMs); |
|
184 if (result) { |
|
185 *timestamp = NTPtoDOMHighResTimeStamp(ntpHigh, ntpLow); |
|
186 } |
|
187 return result; |
|
188 } |
|
189 |
|
190 bool WebrtcVideoConduit::GetRTCPSenderReport(DOMHighResTimeStamp* timestamp, |
|
191 unsigned int* packetsSent, |
|
192 uint64_t* bytesSent) { |
|
193 struct webrtc::SenderInfo senderInfo; |
|
194 bool result = !mPtrRTP->GetRemoteRTCPSenderInfo(mChannel, &senderInfo); |
|
195 if (result) { |
|
196 *timestamp = NTPtoDOMHighResTimeStamp(senderInfo.NTP_timestamp_high, |
|
197 senderInfo.NTP_timestamp_low); |
|
198 *packetsSent = senderInfo.sender_packet_count; |
|
199 *bytesSent = senderInfo.sender_octet_count; |
|
200 } |
|
201 return result; |
|
202 } |
|
203 |
|
204 /** |
|
205 * Peforms intialization of the MANDATORY components of the Video Engine |
|
206 */ |
|
207 MediaConduitErrorCode WebrtcVideoConduit::Init(WebrtcVideoConduit *other) |
|
208 { |
|
209 CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other); |
|
210 |
|
211 if (other) { |
|
212 MOZ_ASSERT(!other->mOtherDirection); |
|
213 other->mOtherDirection = this; |
|
214 mOtherDirection = other; |
|
215 |
|
216 // only one can call ::Create()/GetVideoEngine() |
|
217 MOZ_ASSERT(other->mVideoEngine); |
|
218 mVideoEngine = other->mVideoEngine; |
|
219 } else { |
|
220 |
|
221 #ifdef MOZ_WIDGET_ANDROID |
|
222 jobject context = jsjni_GetGlobalContextRef(); |
|
223 |
|
224 // get the JVM |
|
225 JavaVM *jvm = jsjni_GetVM(); |
|
226 |
|
227 if (webrtc::VideoEngine::SetAndroidObjects(jvm, (void*)context) != 0) { |
|
228 CSFLogError(logTag, "%s: could not set Android objects", __FUNCTION__); |
|
229 return kMediaConduitSessionNotInited; |
|
230 } |
|
231 #endif |
|
232 |
|
233 // Per WebRTC APIs below function calls return nullptr on failure |
|
234 if( !(mVideoEngine = webrtc::VideoEngine::Create()) ) |
|
235 { |
|
236 CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); |
|
237 return kMediaConduitSessionNotInited; |
|
238 } |
|
239 |
|
240 PRLogModuleInfo *logs = GetWebRTCLogInfo(); |
|
241 if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { |
|
242 // no need to a critical section or lock here |
|
243 gWebrtcTraceLoggingOn = 1; |
|
244 |
|
245 const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); |
|
246 if (!file) { |
|
247 file = "WebRTC.log"; |
|
248 } |
|
249 CSFLogDebug(logTag, "%s Logging webrtc to %s level %d", __FUNCTION__, |
|
250 file, logs->level); |
|
251 mVideoEngine->SetTraceFilter(logs->level); |
|
252 mVideoEngine->SetTraceFile(file); |
|
253 } |
|
254 } |
|
255 |
|
256 if( !(mPtrViEBase = ViEBase::GetInterface(mVideoEngine))) |
|
257 { |
|
258 CSFLogError(logTag, "%s Unable to get video base interface ", __FUNCTION__); |
|
259 return kMediaConduitSessionNotInited; |
|
260 } |
|
261 |
|
262 if( !(mPtrViECapture = ViECapture::GetInterface(mVideoEngine))) |
|
263 { |
|
264 CSFLogError(logTag, "%s Unable to get video capture interface", __FUNCTION__); |
|
265 return kMediaConduitSessionNotInited; |
|
266 } |
|
267 |
|
268 if( !(mPtrViECodec = ViECodec::GetInterface(mVideoEngine))) |
|
269 { |
|
270 CSFLogError(logTag, "%s Unable to get video codec interface ", __FUNCTION__); |
|
271 return kMediaConduitSessionNotInited; |
|
272 } |
|
273 |
|
274 if( !(mPtrViENetwork = ViENetwork::GetInterface(mVideoEngine))) |
|
275 { |
|
276 CSFLogError(logTag, "%s Unable to get video network interface ", __FUNCTION__); |
|
277 return kMediaConduitSessionNotInited; |
|
278 } |
|
279 |
|
280 if( !(mPtrViERender = ViERender::GetInterface(mVideoEngine))) |
|
281 { |
|
282 CSFLogError(logTag, "%s Unable to get video render interface ", __FUNCTION__); |
|
283 return kMediaConduitSessionNotInited; |
|
284 } |
|
285 |
|
286 if( !(mPtrRTP = webrtc::ViERTP_RTCP::GetInterface(mVideoEngine))) |
|
287 { |
|
288 CSFLogError(logTag, "%s Unable to get video RTCP interface ", __FUNCTION__); |
|
289 return kMediaConduitSessionNotInited; |
|
290 } |
|
291 |
|
292 if ( !(mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine))) |
|
293 { |
|
294 CSFLogError(logTag, "%s Unable to get external codec interface %d ", |
|
295 __FUNCTION__, mPtrViEBase->LastError()); |
|
296 return kMediaConduitSessionNotInited; |
|
297 } |
|
298 |
|
299 if (other) { |
|
300 mChannel = other->mChannel; |
|
301 mPtrExtCapture = other->mPtrExtCapture; |
|
302 mCapId = other->mCapId; |
|
303 } else { |
|
304 CSFLogDebug(logTag, "%s Engine Created: Init'ng the interfaces ",__FUNCTION__); |
|
305 |
|
306 if(mPtrViEBase->Init() == -1) |
|
307 { |
|
308 CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__, |
|
309 mPtrViEBase->LastError()); |
|
310 return kMediaConduitSessionNotInited; |
|
311 } |
|
312 |
|
313 if(mPtrViEBase->CreateChannel(mChannel) == -1) |
|
314 { |
|
315 CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__, |
|
316 mPtrViEBase->LastError()); |
|
317 return kMediaConduitChannelError; |
|
318 } |
|
319 |
|
320 if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1) |
|
321 { |
|
322 CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__, |
|
323 mPtrViEBase->LastError()); |
|
324 return kMediaConduitTransportRegistrationFail; |
|
325 } |
|
326 |
|
327 if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId, |
|
328 mPtrExtCapture) == -1) |
|
329 { |
|
330 CSFLogError(logTag, "%s Unable to Allocate capture module: %d ", |
|
331 __FUNCTION__, mPtrViEBase->LastError()); |
|
332 return kMediaConduitCaptureError; |
|
333 } |
|
334 |
|
335 if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1) |
|
336 { |
|
337 CSFLogError(logTag, "%s Unable to Connect capture module: %d ", |
|
338 __FUNCTION__,mPtrViEBase->LastError()); |
|
339 return kMediaConduitCaptureError; |
|
340 } |
|
341 |
|
342 if(mPtrViERender->AddRenderer(mChannel, |
|
343 webrtc::kVideoI420, |
|
344 (webrtc::ExternalRenderer*) this) == -1) |
|
345 { |
|
346 CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); |
|
347 return kMediaConduitInvalidRenderer; |
|
348 } |
|
349 // Set up some parameters, per juberti. Set MTU. |
|
350 if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0) |
|
351 { |
|
352 CSFLogError(logTag, "%s MTU Failed %d ", __FUNCTION__, |
|
353 mPtrViEBase->LastError()); |
|
354 return kMediaConduitMTUError; |
|
355 } |
|
356 // Turn on RTCP and loss feedback reporting. |
|
357 if(mPtrRTP->SetRTCPStatus(mChannel, webrtc::kRtcpCompound_RFC4585) != 0) |
|
358 { |
|
359 CSFLogError(logTag, "%s RTCPStatus Failed %d ", __FUNCTION__, |
|
360 mPtrViEBase->LastError()); |
|
361 return kMediaConduitRTCPStatusError; |
|
362 } |
|
363 } |
|
364 |
|
365 CSFLogError(logTag, "%s Initialization Done", __FUNCTION__); |
|
366 return kMediaConduitNoError; |
|
367 } |
|
368 |
|
369 void |
|
370 WebrtcVideoConduit::SyncTo(WebrtcAudioConduit *aConduit) |
|
371 { |
|
372 CSFLogDebug(logTag, "%s Synced to %p", __FUNCTION__, aConduit); |
|
373 |
|
374 // SyncTo(value) syncs to the AudioConduit, and if already synced replaces |
|
375 // the current sync target. SyncTo(nullptr) cancels any existing sync and |
|
376 // releases the strong ref to AudioConduit. |
|
377 if (aConduit) { |
|
378 mPtrViEBase->SetVoiceEngine(aConduit->GetVoiceEngine()); |
|
379 mPtrViEBase->ConnectAudioChannel(mChannel, aConduit->GetChannel()); |
|
380 // NOTE: this means the VideoConduit will keep the AudioConduit alive! |
|
381 } else if ((mOtherDirection && mOtherDirection->mSyncedTo) || mSyncedTo) { |
|
382 mPtrViEBase->DisconnectAudioChannel(mChannel); |
|
383 mPtrViEBase->SetVoiceEngine(nullptr); |
|
384 } |
|
385 |
|
386 // Now manage the shared sync reference (ugly) |
|
387 if (mSyncedTo || !mOtherDirection ) { |
|
388 mSyncedTo = aConduit; |
|
389 } else { |
|
390 mOtherDirection->mSyncedTo = aConduit; |
|
391 } |
|
392 } |
|
393 |
|
394 MediaConduitErrorCode |
|
395 WebrtcVideoConduit::AttachRenderer(mozilla::RefPtr<VideoRenderer> aVideoRenderer) |
|
396 { |
|
397 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
398 |
|
399 //null renderer |
|
400 if(!aVideoRenderer) |
|
401 { |
|
402 CSFLogError(logTag, "%s NULL Renderer", __FUNCTION__); |
|
403 MOZ_ASSERT(PR_FALSE); |
|
404 return kMediaConduitInvalidRenderer; |
|
405 } |
|
406 |
|
407 //Start Rendering if we haven't already |
|
408 if(!mRenderer) |
|
409 { |
|
410 mRenderer = aVideoRenderer; // must be done before StartRender() |
|
411 |
|
412 if(mPtrViERender->StartRender(mChannel) == -1) |
|
413 { |
|
414 CSFLogError(logTag, "%s Starting the Renderer Failed %d ", __FUNCTION__, |
|
415 mPtrViEBase->LastError()); |
|
416 mRenderer = nullptr; |
|
417 return kMediaConduitRendererFail; |
|
418 } |
|
419 } else { |
|
420 //Assign the new renderer - overwrites if there is already one |
|
421 mRenderer = aVideoRenderer; |
|
422 } |
|
423 |
|
424 return kMediaConduitNoError; |
|
425 } |
|
426 |
|
427 void |
|
428 WebrtcVideoConduit::DetachRenderer() |
|
429 { |
|
430 if(mRenderer) |
|
431 { |
|
432 mPtrViERender->StopRender(mChannel); |
|
433 mRenderer = nullptr; |
|
434 } |
|
435 } |
|
436 |
|
437 MediaConduitErrorCode |
|
438 WebrtcVideoConduit::AttachTransport(mozilla::RefPtr<TransportInterface> aTransport) |
|
439 { |
|
440 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
441 if(!aTransport) |
|
442 { |
|
443 CSFLogError(logTag, "%s NULL Transport", __FUNCTION__); |
|
444 return kMediaConduitInvalidTransport; |
|
445 } |
|
446 // set the transport |
|
447 mTransport = aTransport; |
|
448 return kMediaConduitNoError; |
|
449 } |
|
450 |
|
451 /** |
|
452 * Note: Setting the send-codec on the Video Engine will restart the encoder, |
|
453 * sets up new SSRC and reset RTP_RTCP module with the new codec setting. |
|
454 */ |
|
455 MediaConduitErrorCode |
|
456 WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig) |
|
457 { |
|
458 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
459 bool codecFound = false; |
|
460 MediaConduitErrorCode condError = kMediaConduitNoError; |
|
461 int error = 0; //webrtc engine errors |
|
462 webrtc::VideoCodec video_codec; |
|
463 std::string payloadName; |
|
464 |
|
465 //validate basic params |
|
466 if((condError = ValidateCodecConfig(codecConfig,true)) != kMediaConduitNoError) |
|
467 { |
|
468 return condError; |
|
469 } |
|
470 |
|
471 //Check if we have same codec already applied |
|
472 if(CheckCodecsForMatch(mCurSendCodecConfig, codecConfig)) |
|
473 { |
|
474 CSFLogDebug(logTag, "%s Codec has been applied already ", __FUNCTION__); |
|
475 return kMediaConduitCodecInUse; |
|
476 } |
|
477 |
|
478 //transmitting already ? |
|
479 if(mEngineTransmitting) |
|
480 { |
|
481 CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__); |
|
482 if(mPtrViEBase->StopSend(mChannel) == -1) |
|
483 { |
|
484 CSFLogError(logTag, "%s StopSend() Failed %d ",__FUNCTION__, |
|
485 mPtrViEBase->LastError()); |
|
486 return kMediaConduitUnknownError; |
|
487 } |
|
488 } |
|
489 |
|
490 mEngineTransmitting = false; |
|
491 |
|
492 if (codecConfig->mLoadManager) { |
|
493 mPtrViEBase->RegisterCpuOveruseObserver(mChannel, codecConfig->mLoadManager); |
|
494 mPtrViEBase->SetLoadManager(codecConfig->mLoadManager); |
|
495 } |
|
496 |
|
497 // we should be good here to set the new codec. |
|
498 for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) |
|
499 { |
|
500 if(0 == mPtrViECodec->GetCodec(idx, video_codec)) |
|
501 { |
|
502 payloadName = video_codec.plName; |
|
503 if(codecConfig->mName.compare(payloadName) == 0) |
|
504 { |
|
505 CodecConfigToWebRTCCodec(codecConfig, video_codec); |
|
506 codecFound = true; |
|
507 break; |
|
508 } |
|
509 } |
|
510 }//for |
|
511 |
|
512 if(codecFound == false) |
|
513 { |
|
514 CSFLogError(logTag, "%s Codec Mismatch ", __FUNCTION__); |
|
515 return kMediaConduitInvalidSendCodec; |
|
516 } |
|
517 |
|
518 if(mPtrViECodec->SetSendCodec(mChannel, video_codec) == -1) |
|
519 { |
|
520 error = mPtrViEBase->LastError(); |
|
521 if(error == kViECodecInvalidCodec) |
|
522 { |
|
523 CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); |
|
524 return kMediaConduitInvalidSendCodec; |
|
525 } |
|
526 CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, |
|
527 mPtrViEBase->LastError()); |
|
528 return kMediaConduitUnknownError; |
|
529 } |
|
530 mSendingWidth = 0; |
|
531 mSendingHeight = 0; |
|
532 |
|
533 if(codecConfig->RtcpFbIsSet(SDP_RTCP_FB_NACK_BASIC)) { |
|
534 CSFLogDebug(logTag, "Enabling NACK (send) for video stream\n"); |
|
535 if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) |
|
536 { |
|
537 CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, |
|
538 mPtrViEBase->LastError()); |
|
539 return kMediaConduitNACKStatusError; |
|
540 } |
|
541 } |
|
542 |
|
543 if(mPtrViEBase->StartSend(mChannel) == -1) |
|
544 { |
|
545 CSFLogError(logTag, "%s Start Send Error %d ", __FUNCTION__, |
|
546 mPtrViEBase->LastError()); |
|
547 return kMediaConduitUnknownError; |
|
548 } |
|
549 |
|
550 //Copy the applied config for future reference. |
|
551 delete mCurSendCodecConfig; |
|
552 |
|
553 mCurSendCodecConfig = new VideoCodecConfig(*codecConfig); |
|
554 |
|
555 mPtrRTP->SetRembStatus(mChannel, true, false); |
|
556 |
|
557 // by now we should be successfully started the transmission |
|
558 mEngineTransmitting = true; |
|
559 return kMediaConduitNoError; |
|
560 } |
|
561 |
|
562 MediaConduitErrorCode |
|
563 WebrtcVideoConduit::ConfigureRecvMediaCodecs( |
|
564 const std::vector<VideoCodecConfig* >& codecConfigList) |
|
565 { |
|
566 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
567 MediaConduitErrorCode condError = kMediaConduitNoError; |
|
568 int error = 0; //webrtc engine errors |
|
569 bool success = false; |
|
570 std::string payloadName; |
|
571 |
|
572 // are we receiving already? If so, stop receiving and playout |
|
573 // since we can't apply new recv codec when the engine is playing. |
|
574 if(mEngineReceiving) |
|
575 { |
|
576 CSFLogDebug(logTag, "%s Engine Already Receiving . Attemping to Stop ", __FUNCTION__); |
|
577 if(mPtrViEBase->StopReceive(mChannel) == -1) |
|
578 { |
|
579 error = mPtrViEBase->LastError(); |
|
580 if(error == kViEBaseUnknownError) |
|
581 { |
|
582 CSFLogDebug(logTag, "%s StopReceive() Success ", __FUNCTION__); |
|
583 mEngineReceiving = false; |
|
584 } else { |
|
585 CSFLogError(logTag, "%s StopReceive() Failed %d ", __FUNCTION__, |
|
586 mPtrViEBase->LastError()); |
|
587 return kMediaConduitUnknownError; |
|
588 } |
|
589 } |
|
590 } |
|
591 |
|
592 mEngineReceiving = false; |
|
593 |
|
594 if(codecConfigList.empty()) |
|
595 { |
|
596 CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); |
|
597 return kMediaConduitMalformedArgument; |
|
598 } |
|
599 |
|
600 webrtc::ViEKeyFrameRequestMethod kf_request = webrtc::kViEKeyFrameRequestNone; |
|
601 bool use_nack_basic = false; |
|
602 |
|
603 //Try Applying the codecs in the list |
|
604 // we treat as success if atleast one codec was applied and reception was |
|
605 // started successfully. |
|
606 for(std::vector<VideoCodecConfig*>::size_type i=0;i < codecConfigList.size();i++) |
|
607 { |
|
608 //if the codec param is invalid or diplicate, return error |
|
609 if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) |
|
610 { |
|
611 return condError; |
|
612 } |
|
613 |
|
614 // Check for the keyframe request type: PLI is preferred |
|
615 // over FIR, and FIR is preferred over none. |
|
616 if (codecConfigList[i]->RtcpFbIsSet(SDP_RTCP_FB_NACK_PLI)) |
|
617 { |
|
618 kf_request = webrtc::kViEKeyFrameRequestPliRtcp; |
|
619 } else if(kf_request == webrtc::kViEKeyFrameRequestNone && |
|
620 codecConfigList[i]->RtcpFbIsSet(SDP_RTCP_FB_CCM_FIR)) |
|
621 { |
|
622 kf_request = webrtc::kViEKeyFrameRequestFirRtcp; |
|
623 } |
|
624 |
|
625 // Check whether NACK is requested |
|
626 if(codecConfigList[i]->RtcpFbIsSet(SDP_RTCP_FB_NACK_BASIC)) |
|
627 { |
|
628 use_nack_basic = true; |
|
629 } |
|
630 |
|
631 webrtc::VideoCodec video_codec; |
|
632 |
|
633 mEngineReceiving = false; |
|
634 memset(&video_codec, 0, sizeof(webrtc::VideoCodec)); |
|
635 //Retrieve pre-populated codec structure for our codec. |
|
636 for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) |
|
637 { |
|
638 if(mPtrViECodec->GetCodec(idx, video_codec) == 0) |
|
639 { |
|
640 payloadName = video_codec.plName; |
|
641 if(codecConfigList[i]->mName.compare(payloadName) == 0) |
|
642 { |
|
643 CodecConfigToWebRTCCodec(codecConfigList[i], video_codec); |
|
644 if(mPtrViECodec->SetReceiveCodec(mChannel,video_codec) == -1) |
|
645 { |
|
646 CSFLogError(logTag, "%s Invalid Receive Codec %d ", __FUNCTION__, |
|
647 mPtrViEBase->LastError()); |
|
648 } else { |
|
649 CSFLogError(logTag, "%s Successfully Set the codec %s", __FUNCTION__, |
|
650 codecConfigList[i]->mName.c_str()); |
|
651 if(CopyCodecToDB(codecConfigList[i])) |
|
652 { |
|
653 success = true; |
|
654 } else { |
|
655 CSFLogError(logTag,"%s Unable to updated Codec Database", __FUNCTION__); |
|
656 return kMediaConduitUnknownError; |
|
657 } |
|
658 } |
|
659 break; //we found a match |
|
660 } |
|
661 } |
|
662 }//end for codeclist |
|
663 |
|
664 }//end for |
|
665 |
|
666 if(!success) |
|
667 { |
|
668 CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); |
|
669 return kMediaConduitInvalidReceiveCodec; |
|
670 } |
|
671 |
|
672 // XXX Currently, we gather up all of the feedback types that the remote |
|
673 // party indicated it supports for all video codecs and configure the entire |
|
674 // conduit based on those capabilities. This is technically out of spec, |
|
675 // as these values should be configured on a per-codec basis. However, |
|
676 // the video engine only provides this API on a per-conduit basis, so that's |
|
677 // how we have to do it. The approach of considering the remote capablities |
|
678 // for the entire conduit to be a union of all remote codec capabilities |
|
679 // (rather than the more conservative approach of using an intersection) |
|
680 // is made to provide as many feedback mechanisms as are likely to be |
|
681 // processed by the remote party (and should be relatively safe, since the |
|
682 // remote party is required to ignore feedback types that it does not |
|
683 // understand). |
|
684 // |
|
685 // Note that our configuration uses this union of remote capabilites as |
|
686 // input to the configuration. It is not isomorphic to the configuration. |
|
687 // For example, it only makes sense to have one frame request mechanism |
|
688 // active at a time; so, if the remote party indicates more than one |
|
689 // supported mechanism, we're only configuring the one we most prefer. |
|
690 // |
|
691 // See http://code.google.com/p/webrtc/issues/detail?id=2331 |
|
692 |
|
693 if (kf_request != webrtc::kViEKeyFrameRequestNone) |
|
694 { |
|
695 CSFLogDebug(logTag, "Enabling %s frame requests for video stream\n", |
|
696 (kf_request == webrtc::kViEKeyFrameRequestPliRtcp ? |
|
697 "PLI" : "FIR")); |
|
698 if(mPtrRTP->SetKeyFrameRequestMethod(mChannel, kf_request) != 0) |
|
699 { |
|
700 CSFLogError(logTag, "%s KeyFrameRequest Failed %d ", __FUNCTION__, |
|
701 mPtrViEBase->LastError()); |
|
702 return kMediaConduitKeyFrameRequestError; |
|
703 } |
|
704 } |
|
705 |
|
706 switch (kf_request) { |
|
707 case webrtc::kViEKeyFrameRequestNone: |
|
708 mFrameRequestMethod = FrameRequestNone; |
|
709 break; |
|
710 case webrtc::kViEKeyFrameRequestPliRtcp: |
|
711 mFrameRequestMethod = FrameRequestPli; |
|
712 break; |
|
713 case webrtc::kViEKeyFrameRequestFirRtcp: |
|
714 mFrameRequestMethod = FrameRequestFir; |
|
715 break; |
|
716 default: |
|
717 MOZ_ASSERT(PR_FALSE); |
|
718 mFrameRequestMethod = FrameRequestUnknown; |
|
719 } |
|
720 |
|
721 if(use_nack_basic) |
|
722 { |
|
723 CSFLogDebug(logTag, "Enabling NACK (recv) for video stream\n"); |
|
724 if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) |
|
725 { |
|
726 CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, |
|
727 mPtrViEBase->LastError()); |
|
728 return kMediaConduitNACKStatusError; |
|
729 } |
|
730 } |
|
731 mUsingNackBasic = use_nack_basic; |
|
732 |
|
733 //Start Receive on the video engine |
|
734 if(mPtrViEBase->StartReceive(mChannel) == -1) |
|
735 { |
|
736 error = mPtrViEBase->LastError(); |
|
737 CSFLogError(logTag, "%s Start Receive Error %d ", __FUNCTION__, error); |
|
738 |
|
739 |
|
740 return kMediaConduitUnknownError; |
|
741 } |
|
742 |
|
743 #ifdef MOZILLA_INTERNAL_API |
|
744 if (NS_IsMainThread()) { |
|
745 nsresult rv; |
|
746 nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); |
|
747 if (NS_SUCCEEDED(rv)) { |
|
748 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); |
|
749 |
|
750 if (branch) { |
|
751 branch->GetBoolPref("media.video.test_latency", &mVideoLatencyTestEnable); |
|
752 } |
|
753 } |
|
754 } |
|
755 #endif |
|
756 |
|
757 // by now we should be successfully started the reception |
|
758 mPtrRTP->SetRembStatus(mChannel, false, true); |
|
759 mEngineReceiving = true; |
|
760 DumpCodecDB(); |
|
761 return kMediaConduitNoError; |
|
762 } |
|
763 |
|
764 // XXX we need to figure out how to feed back changes in preferred capture |
|
765 // resolution to the getUserMedia source |
|
766 bool |
|
767 WebrtcVideoConduit::SelectSendResolution(unsigned short width, |
|
768 unsigned short height) |
|
769 { |
|
770 // XXX This will do bandwidth-resolution adaptation as well - bug 877954 |
|
771 |
|
772 // Limit resolution to max-fs while keeping same aspect ratio as the |
|
773 // incoming image. |
|
774 if (mCurSendCodecConfig && mCurSendCodecConfig->mMaxFrameSize) |
|
775 { |
|
776 unsigned int cur_fs, max_width, max_height, mb_width, mb_height, mb_max; |
|
777 |
|
778 mb_width = (width + 15) >> 4; |
|
779 mb_height = (height + 15) >> 4; |
|
780 |
|
781 cur_fs = mb_width * mb_height; |
|
782 |
|
783 // Limit resolution to max_fs, but don't scale up. |
|
784 if (cur_fs > mCurSendCodecConfig->mMaxFrameSize) |
|
785 { |
|
786 double scale_ratio; |
|
787 |
|
788 scale_ratio = sqrt((double) mCurSendCodecConfig->mMaxFrameSize / |
|
789 (double) cur_fs); |
|
790 |
|
791 mb_width = mb_width * scale_ratio; |
|
792 mb_height = mb_height * scale_ratio; |
|
793 |
|
794 // Adjust mb_width and mb_height if they were truncated to zero. |
|
795 if (mb_width == 0) { |
|
796 mb_width = 1; |
|
797 mb_height = std::min(mb_height, mCurSendCodecConfig->mMaxFrameSize); |
|
798 } |
|
799 if (mb_height == 0) { |
|
800 mb_height = 1; |
|
801 mb_width = std::min(mb_width, mCurSendCodecConfig->mMaxFrameSize); |
|
802 } |
|
803 } |
|
804 |
|
805 // Limit width/height seperately to limit effect of extreme aspect ratios. |
|
806 mb_max = (unsigned) sqrt(8 * (double) mCurSendCodecConfig->mMaxFrameSize); |
|
807 |
|
808 max_width = 16 * std::min(mb_width, mb_max); |
|
809 max_height = 16 * std::min(mb_height, mb_max); |
|
810 |
|
811 if (width * max_height > max_width * height) |
|
812 { |
|
813 if (width > max_width) |
|
814 { |
|
815 // Due to the value is truncated to integer here and forced to even |
|
816 // value later, adding 1 to improve accuracy. |
|
817 height = max_width * height / width + 1; |
|
818 width = max_width; |
|
819 } |
|
820 } |
|
821 else |
|
822 { |
|
823 if (height > max_height) |
|
824 { |
|
825 // Due to the value is truncated to integer here and forced to even |
|
826 // value later, adding 1 to improve accuracy. |
|
827 width = max_height * width / height + 1; |
|
828 height = max_height; |
|
829 } |
|
830 } |
|
831 |
|
832 // Favor even multiples of pixels for width and height. |
|
833 width = std::max(width & ~1, 2); |
|
834 height = std::max(height & ~1, 2); |
|
835 } |
|
836 |
|
837 // Adapt to getUserMedia resolution changes |
|
838 // check if we need to reconfigure the sending resolution |
|
839 if (mSendingWidth != width || mSendingHeight != height) |
|
840 { |
|
841 // This will avoid us continually retrying this operation if it fails. |
|
842 // If the resolution changes, we'll try again. In the meantime, we'll |
|
843 // keep using the old size in the encoder. |
|
844 mSendingWidth = width; |
|
845 mSendingHeight = height; |
|
846 |
|
847 // Get current vie codec. |
|
848 webrtc::VideoCodec vie_codec; |
|
849 int32_t err; |
|
850 |
|
851 if ((err = mPtrViECodec->GetSendCodec(mChannel, vie_codec)) != 0) |
|
852 { |
|
853 CSFLogError(logTag, "%s: GetSendCodec failed, err %d", __FUNCTION__, err); |
|
854 return false; |
|
855 } |
|
856 if (vie_codec.width != width || vie_codec.height != height) |
|
857 { |
|
858 vie_codec.width = width; |
|
859 vie_codec.height = height; |
|
860 |
|
861 if ((err = mPtrViECodec->SetSendCodec(mChannel, vie_codec)) != 0) |
|
862 { |
|
863 CSFLogError(logTag, "%s: SetSendCodec(%ux%u) failed, err %d", |
|
864 __FUNCTION__, width, height, err); |
|
865 return false; |
|
866 } |
|
867 CSFLogDebug(logTag, "%s: Encoder resolution changed to %ux%u", |
|
868 __FUNCTION__, width, height); |
|
869 } // else no change; mSendingWidth likely was 0 |
|
870 } |
|
871 return true; |
|
872 } |
|
873 |
|
874 MediaConduitErrorCode |
|
875 WebrtcVideoConduit::SetExternalSendCodec(int pltype, |
|
876 VideoEncoder* encoder) { |
|
877 int ret = mPtrExtCodec->RegisterExternalSendCodec(mChannel, |
|
878 pltype, |
|
879 static_cast<WebrtcVideoEncoder*>(encoder), |
|
880 false); |
|
881 return ret ? kMediaConduitInvalidSendCodec : kMediaConduitNoError; |
|
882 } |
|
883 |
|
884 MediaConduitErrorCode |
|
885 WebrtcVideoConduit::SetExternalRecvCodec(int pltype, |
|
886 VideoDecoder* decoder) { |
|
887 int ret = mPtrExtCodec->RegisterExternalReceiveCodec(mChannel, |
|
888 pltype, |
|
889 static_cast<WebrtcVideoDecoder*>(decoder)); |
|
890 return ret ? kMediaConduitInvalidReceiveCodec : kMediaConduitNoError; |
|
891 } |
|
892 |
|
893 MediaConduitErrorCode |
|
894 WebrtcVideoConduit::SendVideoFrame(unsigned char* video_frame, |
|
895 unsigned int video_frame_length, |
|
896 unsigned short width, |
|
897 unsigned short height, |
|
898 VideoType video_type, |
|
899 uint64_t capture_time) |
|
900 { |
|
901 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
902 |
|
903 //check for the parameters sanity |
|
904 if(!video_frame || video_frame_length == 0 || |
|
905 width == 0 || height == 0) |
|
906 { |
|
907 CSFLogError(logTag, "%s Invalid Parameters ",__FUNCTION__); |
|
908 MOZ_ASSERT(PR_FALSE); |
|
909 return kMediaConduitMalformedArgument; |
|
910 } |
|
911 |
|
912 webrtc::RawVideoType type; |
|
913 switch (video_type) { |
|
914 case kVideoI420: |
|
915 type = webrtc::kVideoI420; |
|
916 break; |
|
917 case kVideoNV21: |
|
918 type = webrtc::kVideoNV21; |
|
919 break; |
|
920 default: |
|
921 CSFLogError(logTag, "%s VideoType Invalid. Only 1420 and NV21 Supported",__FUNCTION__); |
|
922 MOZ_ASSERT(PR_FALSE); |
|
923 return kMediaConduitMalformedArgument; |
|
924 } |
|
925 //Transmission should be enabled before we insert any frames. |
|
926 if(!mEngineTransmitting) |
|
927 { |
|
928 CSFLogError(logTag, "%s Engine not transmitting ", __FUNCTION__); |
|
929 return kMediaConduitSessionNotInited; |
|
930 } |
|
931 |
|
932 // enforce even width/height (paranoia) |
|
933 MOZ_ASSERT(!(width & 1)); |
|
934 MOZ_ASSERT(!(height & 1)); |
|
935 |
|
936 if (!SelectSendResolution(width, height)) |
|
937 { |
|
938 return kMediaConduitCaptureError; |
|
939 } |
|
940 |
|
941 //insert the frame to video engine in I420 format only |
|
942 MOZ_ASSERT(mPtrExtCapture); |
|
943 if(mPtrExtCapture->IncomingFrame(video_frame, |
|
944 video_frame_length, |
|
945 width, height, |
|
946 type, |
|
947 (unsigned long long)capture_time) == -1) |
|
948 { |
|
949 CSFLogError(logTag, "%s IncomingFrame Failed %d ", __FUNCTION__, |
|
950 mPtrViEBase->LastError()); |
|
951 return kMediaConduitCaptureError; |
|
952 } |
|
953 |
|
954 CSFLogDebug(logTag, "%s Inserted a frame", __FUNCTION__); |
|
955 return kMediaConduitNoError; |
|
956 } |
|
957 |
|
958 // Transport Layer Callbacks |
|
959 MediaConduitErrorCode |
|
960 WebrtcVideoConduit::ReceivedRTPPacket(const void *data, int len) |
|
961 { |
|
962 CSFLogDebug(logTag, "%s: Channel %d, Len %d ", __FUNCTION__, mChannel, len); |
|
963 |
|
964 // Media Engine should be receiving already. |
|
965 if(mEngineReceiving) |
|
966 { |
|
967 // let the engine know of a RTP packet to decode |
|
968 if(mPtrViENetwork->ReceivedRTPPacket(mChannel,data,len) == -1) |
|
969 { |
|
970 int error = mPtrViEBase->LastError(); |
|
971 CSFLogError(logTag, "%s RTP Processing Failed %d ", __FUNCTION__, error); |
|
972 if(error >= kViERtpRtcpInvalidChannelId && error <= kViERtpRtcpRtcpDisabled) |
|
973 { |
|
974 return kMediaConduitRTPProcessingFailed; |
|
975 } |
|
976 return kMediaConduitRTPRTCPModuleError; |
|
977 } |
|
978 } else { |
|
979 CSFLogError(logTag, "Error: %s when not receiving", __FUNCTION__); |
|
980 return kMediaConduitSessionNotInited; |
|
981 } |
|
982 |
|
983 return kMediaConduitNoError; |
|
984 } |
|
985 |
|
986 MediaConduitErrorCode |
|
987 WebrtcVideoConduit::ReceivedRTCPPacket(const void *data, int len) |
|
988 { |
|
989 CSFLogDebug(logTag, " %s Channel %d, Len %d ", __FUNCTION__, mChannel, len); |
|
990 |
|
991 //Media Engine should be receiving already |
|
992 if(mEngineTransmitting) |
|
993 { |
|
994 if(mPtrViENetwork->ReceivedRTCPPacket(mChannel,data,len) == -1) |
|
995 { |
|
996 int error = mPtrViEBase->LastError(); |
|
997 CSFLogError(logTag, "%s RTP Processing Failed %d", __FUNCTION__, error); |
|
998 if(error >= kViERtpRtcpInvalidChannelId && error <= kViERtpRtcpRtcpDisabled) |
|
999 { |
|
1000 return kMediaConduitRTPProcessingFailed; |
|
1001 } |
|
1002 return kMediaConduitRTPRTCPModuleError; |
|
1003 } |
|
1004 } else { |
|
1005 CSFLogError(logTag, "Error: %s when not receiving", __FUNCTION__); |
|
1006 return kMediaConduitSessionNotInited; |
|
1007 } |
|
1008 return kMediaConduitNoError; |
|
1009 } |
|
1010 |
|
1011 //WebRTC::RTP Callback Implementation |
|
1012 int WebrtcVideoConduit::SendPacket(int channel, const void* data, int len) |
|
1013 { |
|
1014 CSFLogDebug(logTag, "%s : channel %d len %d %s", __FUNCTION__, channel, len, |
|
1015 (mEngineReceiving && mOtherDirection) ? "(using mOtherDirection)" : ""); |
|
1016 |
|
1017 if (mEngineReceiving) |
|
1018 { |
|
1019 if (mOtherDirection) |
|
1020 { |
|
1021 return mOtherDirection->SendPacket(channel, data, len); |
|
1022 } |
|
1023 CSFLogDebug(logTag, "%s : Asked to send RTP without an RTP sender on channel %d", |
|
1024 __FUNCTION__, channel); |
|
1025 return -1; |
|
1026 } else { |
|
1027 if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK)) |
|
1028 { |
|
1029 CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__); |
|
1030 return len; |
|
1031 } else { |
|
1032 CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__); |
|
1033 return -1; |
|
1034 } |
|
1035 } |
|
1036 } |
|
1037 |
|
1038 int WebrtcVideoConduit::SendRTCPPacket(int channel, const void* data, int len) |
|
1039 { |
|
1040 CSFLogDebug(logTag, "%s : channel %d , len %d ", __FUNCTION__, channel,len); |
|
1041 |
|
1042 if (mEngineTransmitting) |
|
1043 { |
|
1044 if (mOtherDirection) |
|
1045 { |
|
1046 return mOtherDirection->SendRTCPPacket(channel, data, len); |
|
1047 } |
|
1048 } |
|
1049 |
|
1050 // We come here if we have only one pipeline/conduit setup, |
|
1051 // such as for unidirectional streams. |
|
1052 // We also end up here if we are receiving |
|
1053 if(mTransport && mTransport->SendRtcpPacket(data, len) == NS_OK) |
|
1054 { |
|
1055 CSFLogDebug(logTag, "%s Sent RTCP Packet ", __FUNCTION__); |
|
1056 return len; |
|
1057 } else { |
|
1058 CSFLogError(logTag, "%s RTCP Packet Send Failed ", __FUNCTION__); |
|
1059 return -1; |
|
1060 } |
|
1061 } |
|
1062 |
|
1063 // WebRTC::ExternalMedia Implementation |
|
1064 int |
|
1065 WebrtcVideoConduit::FrameSizeChange(unsigned int width, |
|
1066 unsigned int height, |
|
1067 unsigned int numStreams) |
|
1068 { |
|
1069 CSFLogDebug(logTag, "%s ", __FUNCTION__); |
|
1070 |
|
1071 |
|
1072 mReceivingWidth = width; |
|
1073 mReceivingHeight = height; |
|
1074 |
|
1075 if(mRenderer) |
|
1076 { |
|
1077 mRenderer->FrameSizeChange(width, height, numStreams); |
|
1078 return 0; |
|
1079 } |
|
1080 |
|
1081 CSFLogError(logTag, "%s Renderer is NULL ", __FUNCTION__); |
|
1082 return -1; |
|
1083 } |
|
1084 |
|
1085 int |
|
1086 WebrtcVideoConduit::DeliverFrame(unsigned char* buffer, |
|
1087 int buffer_size, |
|
1088 uint32_t time_stamp, |
|
1089 int64_t render_time, |
|
1090 void *handle) |
|
1091 { |
|
1092 CSFLogDebug(logTag, "%s Buffer Size %d", __FUNCTION__, buffer_size); |
|
1093 |
|
1094 if(mRenderer) |
|
1095 { |
|
1096 layers::Image* img = nullptr; |
|
1097 // |handle| should be a webrtc::NativeHandle if available. |
|
1098 if (handle) { |
|
1099 webrtc::NativeHandle* native_h = static_cast<webrtc::NativeHandle*>(handle); |
|
1100 // In the handle, there should be a layers::Image. |
|
1101 img = static_cast<layers::Image*>(native_h->GetHandle()); |
|
1102 } |
|
1103 |
|
1104 if (mVideoLatencyTestEnable && mReceivingWidth && mReceivingHeight) { |
|
1105 uint64_t now = PR_Now(); |
|
1106 uint64_t timestamp = 0; |
|
1107 bool ok = YuvStamper::Decode(mReceivingWidth, mReceivingHeight, mReceivingWidth, |
|
1108 buffer, |
|
1109 reinterpret_cast<unsigned char*>(×tamp), |
|
1110 sizeof(timestamp), 0, 0); |
|
1111 if (ok) { |
|
1112 VideoLatencyUpdate(now - timestamp); |
|
1113 } |
|
1114 } |
|
1115 |
|
1116 const ImageHandle img_h(img); |
|
1117 mRenderer->RenderVideoFrame(buffer, buffer_size, time_stamp, render_time, |
|
1118 img_h); |
|
1119 return 0; |
|
1120 } |
|
1121 |
|
1122 CSFLogError(logTag, "%s Renderer is NULL ", __FUNCTION__); |
|
1123 return -1; |
|
1124 } |
|
1125 |
|
1126 /** |
|
1127 * Copy the codec passed into Conduit's database |
|
1128 */ |
|
1129 |
|
1130 void |
|
1131 WebrtcVideoConduit::CodecConfigToWebRTCCodec(const VideoCodecConfig* codecInfo, |
|
1132 webrtc::VideoCodec& cinst) |
|
1133 { |
|
1134 cinst.plType = codecInfo->mType; |
|
1135 // leave width/height alone; they'll be overridden on the first frame |
|
1136 if (codecInfo->mMaxFrameRate > 0) |
|
1137 { |
|
1138 cinst.maxFramerate = codecInfo->mMaxFrameRate; |
|
1139 } |
|
1140 cinst.minBitrate = 200; |
|
1141 cinst.startBitrate = 300; |
|
1142 cinst.maxBitrate = 2000; |
|
1143 } |
|
1144 |
|
1145 //Copy the codec passed into Conduit's database |
|
1146 bool |
|
1147 WebrtcVideoConduit::CopyCodecToDB(const VideoCodecConfig* codecInfo) |
|
1148 { |
|
1149 VideoCodecConfig* cdcConfig = new VideoCodecConfig(*codecInfo); |
|
1150 mRecvCodecList.push_back(cdcConfig); |
|
1151 return true; |
|
1152 } |
|
1153 |
|
1154 bool |
|
1155 WebrtcVideoConduit::CheckCodecsForMatch(const VideoCodecConfig* curCodecConfig, |
|
1156 const VideoCodecConfig* codecInfo) const |
|
1157 { |
|
1158 if(!curCodecConfig) |
|
1159 { |
|
1160 return false; |
|
1161 } |
|
1162 |
|
1163 if(curCodecConfig->mType == codecInfo->mType && |
|
1164 curCodecConfig->mName.compare(codecInfo->mName) == 0 && |
|
1165 curCodecConfig->mMaxFrameSize == codecInfo->mMaxFrameSize && |
|
1166 curCodecConfig->mMaxFrameRate == codecInfo->mMaxFrameRate) |
|
1167 { |
|
1168 return true; |
|
1169 } |
|
1170 |
|
1171 return false; |
|
1172 } |
|
1173 |
|
1174 /** |
|
1175 * Checks if the codec is already in Conduit's database |
|
1176 */ |
|
1177 bool |
|
1178 WebrtcVideoConduit::CheckCodecForMatch(const VideoCodecConfig* codecInfo) const |
|
1179 { |
|
1180 //the db should have atleast one codec |
|
1181 for(std::vector<VideoCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++) |
|
1182 { |
|
1183 if(CheckCodecsForMatch(mRecvCodecList[i],codecInfo)) |
|
1184 { |
|
1185 //match |
|
1186 return true; |
|
1187 } |
|
1188 } |
|
1189 //no match or empty local db |
|
1190 return false; |
|
1191 } |
|
1192 |
|
1193 /** |
|
1194 * Perform validation on the codecConfig to be applied |
|
1195 * Verifies if the codec is already applied. |
|
1196 */ |
|
1197 MediaConduitErrorCode |
|
1198 WebrtcVideoConduit::ValidateCodecConfig(const VideoCodecConfig* codecInfo, |
|
1199 bool send) const |
|
1200 { |
|
1201 bool codecAppliedAlready = false; |
|
1202 |
|
1203 if(!codecInfo) |
|
1204 { |
|
1205 CSFLogError(logTag, "%s Null CodecConfig ", __FUNCTION__); |
|
1206 return kMediaConduitMalformedArgument; |
|
1207 } |
|
1208 |
|
1209 if((codecInfo->mName.empty()) || |
|
1210 (codecInfo->mName.length() >= CODEC_PLNAME_SIZE)) |
|
1211 { |
|
1212 CSFLogError(logTag, "%s Invalid Payload Name Length ", __FUNCTION__); |
|
1213 return kMediaConduitMalformedArgument; |
|
1214 } |
|
1215 |
|
1216 //check if we have the same codec already applied |
|
1217 if(send) |
|
1218 { |
|
1219 codecAppliedAlready = CheckCodecsForMatch(mCurSendCodecConfig,codecInfo); |
|
1220 } else { |
|
1221 codecAppliedAlready = CheckCodecForMatch(codecInfo); |
|
1222 } |
|
1223 |
|
1224 if(codecAppliedAlready) |
|
1225 { |
|
1226 CSFLogDebug(logTag, "%s Codec %s Already Applied ", __FUNCTION__, codecInfo->mName.c_str()); |
|
1227 return kMediaConduitCodecInUse; |
|
1228 } |
|
1229 return kMediaConduitNoError; |
|
1230 } |
|
1231 |
|
1232 void |
|
1233 WebrtcVideoConduit::DumpCodecDB() const |
|
1234 { |
|
1235 for(std::vector<VideoCodecConfig*>::size_type i=0;i<mRecvCodecList.size();i++) |
|
1236 { |
|
1237 CSFLogDebug(logTag,"Payload Name: %s", mRecvCodecList[i]->mName.c_str()); |
|
1238 CSFLogDebug(logTag,"Payload Type: %d", mRecvCodecList[i]->mType); |
|
1239 CSFLogDebug(logTag,"Payload Max Frame Size: %d", mRecvCodecList[i]->mMaxFrameSize); |
|
1240 CSFLogDebug(logTag,"Payload Max Frame Rate: %d", mRecvCodecList[i]->mMaxFrameRate); |
|
1241 } |
|
1242 } |
|
1243 |
|
1244 void |
|
1245 WebrtcVideoConduit::VideoLatencyUpdate(uint64_t newSample) |
|
1246 { |
|
1247 mVideoLatencyAvg = (sRoundingPadding * newSample + sAlphaNum * mVideoLatencyAvg) / sAlphaDen; |
|
1248 } |
|
1249 |
|
1250 uint64_t |
|
1251 WebrtcVideoConduit::MozVideoLatencyAvg() |
|
1252 { |
|
1253 return mVideoLatencyAvg / sRoundingPadding; |
|
1254 } |
|
1255 |
|
1256 }// end namespace |