reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
GstRemoteParticipant.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include <regex>
6
8
10#include "Conversation.hxx"
11#include "UserAgent.hxx"
12#include "DtmfEvent.hxx"
13#include "ReconSubsystem.hxx"
14
15#include <rutil/Log.hxx>
16#include <rutil/Logger.hxx>
17#include <rutil/DnsUtil.hxx>
18#include <rutil/Random.hxx>
29
33
35
36#include <memory>
37#include <utility>
38
39#include <glibmm/error.h>
40#include <glibmm/signalproxy.h>
41#include <gst/gst.h>
42#include <gst/gsterror.h>
43#include <gst/sdp/gstsdpmessage.h>
44#include <gstreamermm/pipeline.h>
45//#include <gstreamermm/promise.h>
46
47#include <gst/webrtc/webrtc.h>
48
49using namespace resipgst;
50using namespace recon;
51using namespace resip;
52using namespace std;
53
54using namespace Gst;
55using Glib::RefPtr;
56
57#define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
58
59/* The demo code for webrtcbin provides a useful example.
60 *
61 * This appears to be the current location of the demos:
62 *
63 * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/tree/main/subprojects/gst-examples/webrtc
64 *
65 * Previous locations:
66 *
67 * https://github.com/centricular/gstwebrtc-demos
68 * https://github.com/imdark/gstreamer-webrtc-demo
69 */
70
71
72// These are currently used as defaults for creating an SDP offer for WebRTC
73#define GST_RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,clock-rate=48000,payload=96"
74#define GST_RTP_CAPS_VP8 "application/x-rtp,media=video,encoding-name=VP8,clock-rate=90000,payload=97"
75
76// These are currently used as defaults for creating an SDP offer for non-WebRTC
77#define GST_RTP_CAPS_PCMA "application/x-rtp,media=audio,encoding-name=PCMA,clock-rate=8000,payload=8"
78#define GST_RTP_CAPS_H264 "application/x-rtp,media=video,encoding-name=H264,clock-rate=90000,payload=120"
79
80// used with g_signal_connect
81void voidStub(GstElement * webrtcbin, void *u)
82{
83 StackLog(<<"voidStub invoked");
84 std::function<void()> &func = *static_cast<std::function<void()>*>(u);
85 func();
86}
87/*void propertyStateStub(GstElement * webrtcbin, GParamSpec * pspec,
88 void *u)
89{
90 std::function<void(GParamSpec *)> &func = *static_cast<std::function<void(GParamSpec *)>*>(u);
91 func(pspec);
92}*/
93
94void promise_stub(GstPromise* promise, void *u) {
95 StackLog(<<"promise_stub invoked");
96 // context is static_cast<void*>(&f) below. We reverse the cast to
97 // void* and call the std::function object.
98 auto _u = static_cast<std::function<void(GstPromise* , gpointer)>*>(u);
99 std::function<void(GstPromise*, gpointer)> &func = *_u;
100 func(promise, NULL);
101 delete _u; // each promise has a copy of the function object that must be deleted
102}
103
104// UAC
106 ConversationManager& conversationManager,
107 GstMediaStackAdapter& gstreamerMediaStackAdapter,
109 RemoteParticipantDialogSet& remoteParticipantDialogSet)
110: Participant(partHandle, ConversationManager::ParticipantType_Remote, conversationManager),
111 RemoteParticipant(partHandle, conversationManager, dum, remoteParticipantDialogSet),
112 GstParticipant(partHandle, ConversationManager::ParticipantType_Remote, conversationManager, gstreamerMediaStackAdapter),
113 mIceGatheringDone(false),
114 mRemoveExtraMediaDescriptors(false),
115 mSipRtpEndpoint(true),
116 mReuseSdpAnswer(false),
117 mWSAcceptsKeyframeRequests(true),
118 mLastRemoteSdp(0),
119 mWaitingAnswer(false),
120 mWebRTCOutgoing(getDialogSet().getConversationProfile()->mediaEndpointMode() == ConversationProfile::WebRTC)
121{
122 InfoLog(<< "GstRemoteParticipant created (UAC), handle=" << mHandle);
123
124 initRtpManager();
125}
126
127// UAS - or forked leg
129 GstMediaStackAdapter& gstreamerMediaStackAdapter,
130 DialogUsageManager& dum,
131 RemoteParticipantDialogSet& remoteParticipantDialogSet)
132: Participant(ConversationManager::ParticipantType_Remote, conversationManager),
133 RemoteParticipant(conversationManager, dum, remoteParticipantDialogSet),
134 GstParticipant(ConversationManager::ParticipantType_Remote, conversationManager, gstreamerMediaStackAdapter),
135 mIceGatheringDone(false),
136 mRemoveExtraMediaDescriptors(false),
137 mSipRtpEndpoint(true),
138 mReuseSdpAnswer(false),
139 mWSAcceptsKeyframeRequests(true),
140 mLastRemoteSdp(0),
141 mWaitingAnswer(false),
142 mWebRTCOutgoing(getDialogSet().getConversationProfile()->mediaEndpointMode() == ConversationProfile::WebRTC)
143{
144 InfoLog(<< "GstRemoteParticipant created (UAS or forked leg), handle=" << mHandle);
145
146 initRtpManager();
147}
148
149void
150GstRemoteParticipant::initRtpManager()
151{
152 // FIXME - IPv6 needed
153 // FIXME - MyMessageDecorator will automatically replace 0.0.0.0 with sending IP
154 // but maybe we can do better
155 mRtpManager = make_shared<GstRtpManager>("0.0.0.0");
156
157}
158
160{
161 // Note: Ideally this call would exist in the Participant Base class - but this call requires
162 // dynamic_casts and virtual methods to function correctly during destruction.
163 // If the call is placed in the base Participant class then these things will not
164 // function as desired because a classes type changes as the descructors unwind.
165 // See https://stackoverflow.com/questions/10979250/usage-of-this-in-destructor.
167
168 mPipeline->set_state(STATE_NULL);
169
170 InfoLog(<< "GstRemoteParticipant destroyed, handle=" << mHandle);
171}
172
173int
175{
176 if(getDialogSet().getActiveRemoteParticipantHandle() == mHandle)
177 {
178 return -1; // FIXME Gst
179 }
180 else
181 {
182 // If this is not active fork leg, then we don't want to effect the bridge mixer.
183 // Note: All forked endpoints/participants have the same connection port on the bridge
184 return -1;
185 }
186}
187
188int
193
194void
196{
197 // FIXME Gst - do we need to implement this?
198}
199
200// Special version of this call used only when a participant
201// is removed from a conversation. Required when sipXConversationMediaInterfaceMode
202// is used, in order to get a pointer to the bridge mixer
203// for a participant (ie. LocalParticipant) that has no currently
204// assigned conversations.
205void
207{
208 // FIXME Gst - do we need to implement this?
209}
210
211void
212GstRemoteParticipant::linkOutgoingPipeline(unsigned int streamId, RefPtr<Bin> bin, const Glib::RefPtr<Gst::Caps> caps)
213{
214 RefPtr<Pad> src = bin->get_static_pad("src");
215
216 RefPtr<Caps> padCaps = Caps::create_from_string("application/x-rtp");
217 RefPtr<Gst::PadTemplate> tmpl = PadTemplate::create("send_rtp_sink_%u", PAD_SINK, PAD_REQUEST, padCaps);
218 ostringstream padName;
219 padName << "sink_" << streamId;
220 DebugLog(<< "linking to " << padName.str());
221 //RefPtr<Pad> binSink = mRtpTransportElement->create_compatible_pad(queueSrc, caps);
222 //RefPtr<Pad> binSink = mRtpTransportElement->get_request_pad(padName.str());
223 //RefPtr<Caps> padCaps2 = Caps::create_from_string("application/x-rtp");
224 //RefPtr<Pad> binSink = mRtpTransportElement->request_pad(tmpl, padName.str(), padCaps2);
225 // is a ghost pad, should have been created already
226
227 // it is static_pad on the rtpbin wrapper
228 RefPtr<Pad> binSink;
229 bool isWebRTC = isWebRTCSession();
230 if(isWebRTC)
231 {
232 // and it is a request pad on the webrtcbin
233 binSink = mRtpTransportElement->create_compatible_pad(src, caps);
234 }
235 else
236 {
237 binSink = mRtpTransportElement->get_static_pad(padName.str());
238 }
239 if(!binSink)
240 {
241 CritLog(<<"failed to get request pad " << padName.str());
242 resip_assert(0);
243 }
244
245 src->link(binSink);
246
247 debugGraph();
248}
249
250void
251sendCapsEvent(RefPtr<Bin> bin, RefPtr<Caps> caps)
252{
253 // webrtcbin seems to expect this
254 // https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1055#note_631630
255
256 // send RTP caps into the sink towards the webrtcbin transport bin
257 RefPtr<Gst::EventCaps> capsEvent = Gst::EventCaps::create(caps);
258 RefPtr<Gst::Element> queueOut = RefPtr<Element>::cast_dynamic(bin->get_child("queueOut"));
259 queueOut->get_static_pad("sink")->send_event(capsEvent);
260
261 // send audio/raw-something caps into the bin
262 //RefPtr<Gst::EventCaps> capsEvent = Gst::EventCaps::create(___caps);
263 //sink->send_event(capsEvent);
264}
265
266/*unsigned int
267getStreamIndex(const Data& streamName)
268{
269 if(streamName == "audio")
270 {
271 return 0;
272 }
273 else if(streamName == "video")
274 {
275 return 1;
276 }
277 else
278 {
279 ErrLog(<<"unknown stream name: " << streamName);
280 resip_assert(0);
281 }
282 return -1;
283}*/
284
285void
286GstRemoteParticipant::prepareStream(unsigned int streamId, bool loopbackMode)
287{
288 GstRtpSession::CapsVector capsV = mRtpSession->getOutgoingCaps();
289 RefPtr<Caps> caps;
290 if(capsV.size() > streamId && capsV[streamId])
291 {
292 caps = capsV[streamId];
293 }
294
295 if(!caps)
296 {
297 ErrLog(<<"caps not specified");
298 return;
299 }
300
301 RefPtr<Pad> mediaSink = mRtpSession->getOutgoingPads()[streamId];
302
303 MediaTypeName mediaTypeName = getMediaTypeName(caps);
304 resip_assert(mediaTypeName.size());
305
306 //RefPtr<Bin> bin = mRtpSession->createOutgoingPipeline(caps);
307 //mPipeline->add(bin);
308 // unsigned int streamId = getStreamIndex(streamKey); // FIXME streamId
309 //linkOutgoingPipeline(streamId, bin, caps);
310 /*RefPtr<Pad> mediaSink = mRtpSession->createMediaSink(caps, streamId);
311 resip_assert(mediaSink);
312 mEncodes[mediaTypeName] = mediaSink;*/
313
314 if(loopbackMode)
315 {
316 // FIXME - move this logic to GstRtpSession
317 //sendCapsEvent(bin, caps);
318 }
319 else
320 {
321 RefPtr<Element> testSrc = buildTestSource(mediaTypeName);
322 resip_assert(testSrc);
323 mPipeline->add(testSrc);
324 testSrc->get_static_pad("src")->link(mediaSink);
325 }
326}
327
328
329bool
330GstRemoteParticipant::initEndpointIfRequired(bool isWebRTC)
331{
332 if(mPipeline)
333 {
334 DebugLog(<<"mPipeline already exists");
335 return false;
336 }
337 if(!isWebRTC)
338 {
339 // skip the ICE phase as we don't do ICE for non-WebRTC yet
340 mIceGatheringDone = true;
341 }
342 else
343 {
344 mIceGatheringDone = false;
345 }
346
347 mPipeline = Gst::Pipeline::create();
348 mMediaBin = mRtpSession->getMediaBin();
350 mRtpTransportElement = mRtpSession->getRtpTransportBin();
352
353 if(isWebRTC)
354 {
355 // FIXME: use a glibmm-style property
356 // FIXME: per-peer bundle-policy
357 // FIXME: move to config file
358 // https://datatracker.ietf.org/doc/html/draft-ietf-rtcweb-jsep-24#section-4.1.1
359 // https://gstreamer.freedesktop.org/documentation/webrtc/index.html?gi-language=c#webrtcbin:bundle-policy
360 //GstWebRTCBundlePolicy bundlePolicy = GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
361 GstWebRTCBundlePolicy bundlePolicy = GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT;
362 GValue val = G_VALUE_INIT;
363 g_value_init (&val, G_TYPE_ENUM);
364 g_value_set_enum (&val, bundlePolicy);
365 g_object_set_property(G_OBJECT(mRtpTransportElement->gobj()), "bundle-policy", &val);
366
367 std::shared_ptr<resip::ConfigParse> cfg = mConversationManager.getConfig();
368 if(cfg)
369 {
370 // FIXME: support TURN too
371 const Data& natTraversalMode = mConversationManager.getConfig()->getConfigData("NatTraversalMode", "", true);
372 const Data& stunServerName = mConversationManager.getConfig()->getConfigData("NatTraversalServerHostname", "", true);
373 const int stunServerPort = mConversationManager.getConfig()->getConfigInt("NatTraversalServerPort", 3478);
374 if(natTraversalMode == "Bind" && !stunServerName.empty() && stunServerPort > 0)
375 {
376 Data stunUrl = "stun://" + stunServerName + ":" + stunServerPort;
377 InfoLog(<<"using STUN config: " << stunUrl);
378 mRtpTransportElement->property<Glib::ustring>("stun-server", stunUrl.c_str());
379 }
380//#define GST_TURN_USER "test" // FIXME - need to read this from the config file
381//#define GST_TURN_PASSWORD ""
382#ifdef GST_TURN_PASSWORD
383 else
384 {
385 ostringstream u;
386 u << "turn://"
387 << GST_TURN_USER
388 << ":"
389 << GST_TURN_PASSWORD
390 << "@195.8.117.61"; // FIXME read from config
391 Glib::ustring turnUrl = u.str();
392 mRtpTransportElement->property<Glib::ustring>("turn-server", turnUrl);
393 InfoLog(<<"using TURN config: " << turnUrl); // FIXME security - logs password
394
395 }
396#endif
397 }
398 }
399
400 //mPipeline->add(mRtpTransportElement);
401 mPipeline->add(mMediaBin);
402
403 // need to create all sink pads on the webrtcbin before it can be used
404
405 mRtpSession->initOutgoingBins();
406
407 // when using webrtcbin we can't go straight into loopback, it looks
408 // like we need to create some other source elements and put some caps events
409 // and maybe some media through the pipeline before getting the event
410 // on-negotiation-needed and completing the webrtcbin setup
411 bool loopbackMode = !isWebRTC;
412
413 unsigned int streamCount = mRtpSession->getOutgoingCaps().size();
414 if(streamCount == 0)
415 {
416 // sending an offer
417 InfoLog(<<"creating streams for SDP offer");
418 // FIXME - these are currently hardcoded in GstRtpSession::buildOffer
419 //mRtpSession->addStream(Gst::Caps::create_from_string(GST_RTP_CAPS_PCMA));
420 //mRtpSession->addStream(Gst::Caps::create_from_string(GST_RTP_CAPS_H264));
421 }
422 else
423 {
424 // received an offer
425 InfoLog(<<"creating streams for SDP answer");
426 for(unsigned int streamId = 0; streamId < streamCount; streamId++)
427 {
428 prepareStream(streamId, loopbackMode);
429 }
430 }
431
432 // FIXME
433 RefPtr<Bus> bus = mPipeline->get_bus();
434 bus->add_watch(sigc::mem_fun(*this, &GstRemoteParticipant::onGstBusMessage));
435
436 DebugLog(<<"mPipeline and mRtpTransportElement created");
437
438 debugGraph();
439
440 return true;
441}
442
443/*void
444GstRemoteParticipant::doIceGathering(gstreamer::ContinuationString sdpReady)
445{
446 std::shared_ptr<gstreamer::WebRtcEndpoint> webRtc = std::static_pointer_cast<gstreamer::WebRtcEndpoint>(mEndpoint);
447
448 std::shared_ptr<gstreamer::EventContinuation> elEventIceCandidateFound =
449 std::make_shared<gstreamer::EventContinuation>([this](std::shared_ptr<gstreamer::Event> event){
450 DebugLog(<<"received event: " << *event);
451 std::shared_ptr<gstreamer::OnIceCandidateFoundEvent> _event =
452 std::dynamic_pointer_cast<gstreamer::OnIceCandidateFoundEvent>(event);
453 resip_assert(_event.get());
454
455 if(!mTrickleIcePermitted)
456 {
457 return;
458 }
459 // FIXME - if we are waiting for a previous INFO to be confirmed,
460 // aggregate the candidates into a vector and send them in bulk
461 auto ice = getLocalSdp()->session().makeIceFragment(Data(_event->getCandidate()),
462 _event->getLineIndex(), Data(_event->getId()));
463 if(ice.get())
464 {
465 StackLog(<<"about to send " << *ice);
466 info(*ice);
467 }
468 else
469 {
470 WarningLog(<<"failed to create ICE fragment for mid: " << _event->getId());
471 }
472 });
473
474 std::shared_ptr<gstreamer::EventContinuation> elIceGatheringDone =
475 std::make_shared<gstreamer::EventContinuation>([this, sdpReady](std::shared_ptr<gstreamer::Event> event){
476 mIceGatheringDone = true;
477 mEndpoint->getLocalSessionDescriptor(sdpReady);
478 });
479
480 webRtc->addOnIceCandidateFoundListener(elEventIceCandidateFound, [=](){
481 webRtc->addOnIceGatheringDoneListener(elIceGatheringDone, [=](){
482 webRtc->gatherCandidates([]{
483 // FIXME - handle the case where it fails
484 // on success, we continue from the IceGatheringDone event handler
485 }); // gatherCandidates
486 });
487 });
488}*/
489
490bool
491GstRemoteParticipant::isWebRTCSession() const
492{
493 // FIXME - find a better way to do this
494 return mRtpSession->isWebRTC();
495}
496
497void
498GstRemoteParticipant::onMediaSourceAdded(const RefPtr<Pad>& pad)
499{
500 Glib::ustring padName = pad->get_name();
501 DebugLog(<<"onMediaSourceAdded: on-pad-added, padName: " << padName << " stream ID: " << pad->get_stream_id().raw());
502 if(pad->get_direction() == PAD_SRC)
503 {
504 RefPtr<Caps> caps = pad->get_current_caps();
505 /*StreamKey key = getMediaTypeName(caps);
506 if(key.empty())
507 {
508 key = deduceKeyForPadName(padName);
509 }
510 resip_assert(key.size());*/
511 MediaTypeName mediaTypeName = deduceKeyForPadName(padName);
512 int streamId = getStreamIdFromPadName(padName);
513 if(streamId < 0)
514 {
515 ErrLog(<<"unable to find encoder for " << padName);
516 }
517 else
518 {
519 auto sink = mRtpSession->getOutgoingPads()[streamId];
520
521 if(!sink)
522 {
523 ErrLog(<<"no encoder configured for stream " << streamId);
524 return;
525 }
526
527 bool loopback = !isWebRTCSession();
528
529 /*RefPtr<Bin> enc = _enc->second;
530 RefPtr<Pad> sink = enc->get_static_pad("sink");*/
531 //RefPtr<Pad> sink = _enc->second;
532 if(sink->is_linked())
533 {
534 StackLog(<<"unlinking existing src");
535 RefPtr<Pad> src = sink->get_peer();
536 if(src->unlink(sink))
537 {
538 loopback = true;
539
540 // FIXME - destroy the unlinked test source
541
542 }
543 else
544 {
545 ErrLog(<<"failed to unlink existing src");
546 resip_assert(0);
547 }
548 }
549
550 if(loopback)
551 {
552 StackLog(<<"attempting loopback configuration");
553 RefPtr<Element> proxy;
554 if(mediaTypeName == "video")
555 {
556 proxy = Gst::VideoConvert::create();
557 }
558 else if(mediaTypeName == "audio")
559 {
560 proxy = Gst::AudioConvert::create();
561 }
562 else
563 {
564 CritLog(<<"unexpected streamKey: " << mediaTypeName);
565 resip_assert(0);
566 }
567 mPipeline->add(proxy);
568 // convert->link(enc);
569 proxy->get_static_pad("src")->link(sink);
570 pad->link(proxy->get_static_pad("sink"));
571 proxy->sync_state_with_parent();
572
573 mMediaBin->sync_state_with_parent();
574
575 // FIXME - here we just loop the incoming to outgoing,
576 // but we should do that in the Conversation class instead
577 DebugLog(<<"completed connection from incoming to outgoing stream");
578 }
579 else
580 {
581 // FIXME - disconnect the test sources and do the loopback
582 ErrLog(<<"not doing loopback for WebRTC");
583 }
584
585 debugGraph();
586
587 // FIXME - delete, now in GstRtpSession
588 /*DebugLog(<<"mDecodes.size() == " << mDecodes.size());
589 shared_ptr<flowmanager::HEPRTCPEventLoggingHandler> handler =
590 std::dynamic_pointer_cast<flowmanager::HEPRTCPEventLoggingHandler>(
591 mConversationManager.getMediaStackAdapter().getRTCPEventLoggingHandler());
592 if(handler && mDecodes.size() == mEncodes.size())
593 {
594 DebugLog(<<"all pads ready, trying to setup HOMER HEP RTCP");
595 //resip::addGstreamerRtcpMonitoringPads(RefPtr<Bin>::cast_dynamic(mRtpTransportElement));
596 // Now all audio and video pads, incoming and outgoing,
597 // are present. We can enable the RTCP signals. If we
598 // ask for these signals before the pads are ready then
599 // it looks like we don't receive any signals at all.
600 RtcpPeerSpecVector peerSpecs = resip::createRtcpPeerSpecs(*getLocalSdp(), *getRemoteSdp());
601 const Data& correlationId = getDialogSet().getDialogSetId().getCallId();
602 //addGstreamerRtcpMonitoringPads(RefPtr<Bin>::cast_dynamic(mRtpTransportElement),
603 // handler->getHepAgent(), peerSpecs, correlationId);
604 }*/
605
606 }
607 }
608 else
609 {
610 StackLog(<<"not a source pad: " << padName);
611 }
612}
613
614RefPtr<Bin>
615GstRemoteParticipant::createIncomingPipeline(RefPtr<Pad> pad)
616{
617 DebugLog(<<"createIncomingPipeline: " << pad->get_name());
618 resip_assert(0); // FIXME - deprecated method
619 return RefPtr<Bin>();
620
621 MediaTypeName mediaType = getMediaTypeName(pad->get_current_caps());
622 if(mediaType.empty())
623 {
624 mediaType = deduceKeyForPadName(pad->get_name());
625 }
626
627 //RefPtr<Bin> decodeBin = mRtpSession->createDecodeBin(mediaType, pad->get_name(), isWebRTCSession());
628
629 //mDecodes[mediaType] = decodeBin;
630 //mPipeline->add(decodeBin);
631
632 //decodeBin->signal_pad_added().connect([this, streamKey](const RefPtr<Pad>& pad){
633 //});
634 //decodeBin->signal_pad_added().connect(sigc::mem_fun(*this, &GstRemoteParticipant::onMediaSourceAdded));
635
636 // Setup the keyframe request probe
637 if(mediaType == "video")
638 {
639 addGstreamerKeyframeProbe(pad, [this](){requestKeyframeFromPeer();});
640 }
641
642 //decodeBin->sync_state_with_parent();
643
644 //return decodeBin;
645}
646
647void
648GstRemoteParticipant::debugGraph()
649{
650 RefPtr<Gst::Bin> bin = mPipeline.cast_dynamic(mPipeline);
651 storeGstreamerGraph(bin, "reCon-gst-graph");
652}
653
654void
655GstRemoteParticipant::createAndConnectElements(bool isWebRTC, std::function<void()> cConnected, CallbackSdpReady sdpReady)
656{
657
658 if(isWebRTC)
659 {
660 createAndConnectElementsWebRTC(cConnected, sdpReady);
661 }
662 else
663 {
664 createAndConnectElementsStandard(cConnected, sdpReady);
665 }
666}
667
668void
669GstRemoteParticipant::createAndConnectElementsStandard(std::function<void()> cConnected, CallbackSdpReady sdpReady)
670{
671 DebugLog(<<"going to STATE_READY");
672 mPipeline->set_state(STATE_READY);
673
674 mMediaBin->signal_pad_added().connect(sigc::mem_fun(*this, &GstRemoteParticipant::onMediaSourceAdded));
675 /*mRtpTransportElement->signal_pad_added().connect([this](const RefPtr<Pad>& pad){
676 Glib::ustring padName = pad->get_name();
677 DebugLog(<<"WebRTCElement: on-pad-added, padName: " << padName << " stream ID: " << pad->get_stream_id());
678 if(pad->get_direction() == PAD_SRC)
679 {
680 //RefPtr<Caps> rtcpCaps = Gst::Caps::create_from_string("application/x-rtcp");
681
682
683 // extract ID
684 /*std::regex r("recv_rtp_src_(\\d+)_(\\d+)");
685 std::smatch match;
686 const std::string& s = padName.raw();
687 if (std::regex_search(s.begin(), s.end(), match, r))
688 {
689 unsigned int streamId = atoi(match[1].str().c_str());
690 auto& ssrc = match[2];
691 StackLog(<<"streamId: " << streamId << " SSRC: " << ssrc);
692 RefPtr<Gst::Bin> dec = mDecBin.at(streamId);
693 sinkPad = dec->get_static_pad("sink");
694
695 // FIXME - setup HOMER if all pads connected
696 //
697 // Now all audio and video pads, incoming and outgoing,
698 // are present. We can enable the RTCP signals. If we
699 // ask for these signals before the pads are ready then
700 // it looks like we don't receive any signals at all.
701 //addGstreamerRtcpMonitoringPads(rtpbin, mHepAgent, mPeerSpecs, mCorrelationId);
702
703 PadLinkReturn ret = newPad->link(sinkPad);
704
705 if (ret != PAD_LINK_OK && ret != PAD_LINK_WAS_LINKED)
706 {
707 DebugLog(<< "Linking of pads " << padName << " and "
708 << sinkPad->get_name() << " failed.");
709 }
710 DebugLog(<<"linking done");
711 return;
712 }* /
713
714 if(regex_search(padName.raw(), regex("^(in|out)bound")))
715 {
716 DebugLog(<<"found RTCP intercept pad: " << pad->get_name());
717 // FIXME - for intercepting RTCP flows in Gstreamer rtpbin
718 //resip::linkGstreamerRtcpHomer(mPipeline, pad, hepAgent, peerSpacs, correlationId);
719 }
720 else
721 {
722 RefPtr<Bin> incomingBin = createIncomingPipeline(pad);
723 pad->link(incomingBin->get_static_pad("sink"));
724 }
725
726 debugGraph();
727 }
728 });*/
729
730 //mRtpTransportElement->sync_state_with_parent();
731
732 DebugLog(<<"going to STATE_PLAYING");
733 StateChangeReturn ret = mPipeline->set_state(STATE_PLAYING);
734 if (ret == STATE_CHANGE_FAILURE)
735 {
736 ErrLog(<<"pipeline fail");
737 mPipeline.reset();
738 sdpReady(false, nullptr);
739 }
740
741 mRtpSession->onPlaying(); // FIXME - use a signal in GstRtpSession
742
743 //sdpReady(true, mRtpSession->getLocal());
744 cConnected();
745}
746
747void
748GstRemoteParticipant::createAndConnectElementsWebRTC(std::function<void()> cConnected, CallbackSdpReady sdpReady)
749{
750 // FIXME: store return value, destroy in destructor
751
752 Glib::signal_any<void>(mRtpTransportElement, "on-negotiation-needed").connect([this, cConnected]()
753 {
754 DebugLog(<<"onNegotiationNeeded invoked");
755 // FIXME - do we need to do anything here?
756 // The negotiation effort will be initiated by the
757 // requests from the SIP peer or local UA.
758
759 // From looking at the sendrecv demo, it appears that this
760 // callback is a hint that the webrtcbin element is in
761 // a suitable state for the application to call create-offer
762
763 cConnected();
764 });
765
766 Glib::signal_any<void,guint,gchararray>(mRtpTransportElement, "on-ice-candidate").connect([this](guint mline_index, gchararray candidate)
767 {
768 DebugLog(<<"cOnIceCandidate invoked");
769 Data mid("0"); // FIXME - why doesn't webrtcbin provide mid?
770 onLocalIceCandidate(candidate, mline_index, mid);
771 });
772
773 Glib::signal_any<void,GParamSpec*>(mRtpTransportElement, "notify::ice-gathering-state").connect_notify([this, sdpReady](GParamSpec * pspec)
774 {
775 DebugLog(<<"cIceGatheringStateNotify invoked");
776 GstWebRTCICEGatheringState ice_gather_state;
777 g_object_get (mRtpTransportElement->gobj(), "ice-gathering-state", &ice_gather_state,
778 NULL);
779 const Data& newState = lookupGstreamerStateName(iCEGatheringStates, ice_gather_state);
780 DebugLog(<<"ICE gathering state: " << newState);
781
782 if(ice_gather_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE)
783 {
784 GstWebRTCSessionDescription *_gstwebrtcsdp = nullptr;
785 g_object_get (mRtpTransportElement->gobj(), "local-description", &_gstwebrtcsdp,
786 NULL);
787 if(!_gstwebrtcsdp)
788 {
789 ErrLog(<<"failed to obtain local-description");
790 sdpReady(false, nullptr);
791 }
792
793 /* Send SDP to peer */
794 std::unique_ptr<SdpContents> _sdp(createSdpContentsFromGstreamer(_gstwebrtcsdp));
795 gst_webrtc_session_description_free (_gstwebrtcsdp);
796 //_sdp->session().transformLocalHold(isHolding()); // FIXME - Gstreamer can hold?
797
798 mRtpSession->setLocalSdp(std::shared_ptr<SdpContents>(dynamic_cast<SdpContents*>(_sdp->clone())));
799 sdpReady(true, std::move(_sdp));
800 }
801 });
802
803 DebugLog(<<"going to STATE_READY");
804 StateChangeReturn ret = mPipeline->set_state(STATE_READY);
805 if (ret == STATE_CHANGE_FAILURE)
806 {
807 ErrLog(<<"pipeline fail");
808 mPipeline.reset();
809 sdpReady(false, nullptr);
810 }
811
812 Glib::signal_any<void,GParamSpec*>(mRtpTransportElement, "notify::ice-connection-state").connect_notify([this](GParamSpec * pspec)
813 {
814 GstWebRTCICEConnectionState ice_connection_state;
815 g_object_get (mRtpTransportElement->gobj(), "ice-connection-state", &ice_connection_state,
816 NULL);
817 const Data& newState = lookupGstreamerStateName(iCEConnectionStates, ice_connection_state);
818 DebugLog(<<"ICE connection state: " << newState);
819 if(ice_connection_state == GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED)
820 {
821 InfoLog(<<"ICE connected");
822 }
823 });
824
825 // FIXME - commented out while trying to resolve missing on-negotiation-needed
826 Glib::signal_any<void,GParamSpec*>(mRtpTransportElement, "notify::signaling-state").connect_notify([this](GParamSpec * pspec)
827 {
828 ErrLog(<<"signaling-state");
829 GstWebRTCSignalingState signaling_state;
830 g_object_get (mRtpTransportElement->gobj(), "signaling-state", &signaling_state,
831 NULL);
832 const Data& newState = lookupGstreamerStateName(signalingStates, signaling_state);
833 DebugLog(<<"signaling state: " << newState);
834 });
835
836 mMediaBin->signal_pad_added().connect(sigc::mem_fun(*this, &GstRemoteParticipant::onMediaSourceAdded));
837 /*mRtpTransportElement->signal_pad_added().connect([this](const RefPtr<Pad>& pad){
838 DebugLog(<<"WebRTCElement: on-pad-added, stream ID: " << pad->get_stream_id());
839 if(pad->get_direction() == PAD_SRC)
840 {
841 RefPtr<Caps> rtcpCaps = Gst::Caps::create_from_string("application/x-rtcp");
842 Glib::ustring padName = pad->get_name();
843 if(!regex_search(padName.raw(), regex("^(in|out)bound")))
844 {
845 pad->link(createIncomingPipeline(pad)->get_static_pad("sink"));
846 }
847 else
848 {
849 DebugLog(<<"found RTCP pad: " << pad->get_name());
850 // FIXME - for intercepting RTCP flows in Gstreamer rtpbin
851 //resip::linkGstreamerRtcpHomer(mPipeline, pad, hepAgent, peerSpacs, correlationId);
852 debugGraph();
853 }
854 }
855 });*/
856
857 //mRtpTransportElement->sync_state_with_parent();
858
859 DebugLog(<<"going to STATE_PLAYING");
860 ret = mPipeline->set_state(STATE_PLAYING);
861 if (ret == STATE_CHANGE_FAILURE)
862 {
863 ErrLog(<<"pipeline fail");
864 mPipeline.reset();
865 sdpReady(false, nullptr);
866 }
867}
868
869/*void
870GstRemoteParticipant::createAndConnectElements(gstreamer::ContinuationVoid cConnected)
871{
872 // FIXME - implement listeners for some of the events currently using elEventDebug
873
874 std::shared_ptr<gstreamer::EventContinuation> elError =
875 std::make_shared<gstreamer::EventContinuation>([this](std::shared_ptr<gstreamer::Event> event){
876 ErrLog(<<"Error from Gst MediaObject: " << *event);
877 });
878
879 std::shared_ptr<gstreamer::EventContinuation> elEventDebug =
880 std::make_shared<gstreamer::EventContinuation>([this](std::shared_ptr<gstreamer::Event> event){
881 DebugLog(<<"received event: " << *event);
882 });
883
884 std::shared_ptr<gstreamer::EventContinuation> elEventKeyframeRequired =
885 std::make_shared<gstreamer::EventContinuation>([this](std::shared_ptr<gstreamer::Event> event){
886 DebugLog(<<"received event: " << *event);
887 requestKeyframeFromPeer();
888 });
889
890 mEndpoint->create([=]{
891 mEndpoint->addErrorListener(elError, [=](){
892 mEndpoint->addConnectionStateChangedListener(elEventDebug, [=](){
893 mEndpoint->addMediaStateChangedListener(elEventDebug, [=](){
894 mEndpoint->addMediaTranscodingStateChangeListener(elEventDebug, [=](){
895 mEndpoint->addMediaFlowInStateChangeListener(elEventDebug, [=](){
896 mEndpoint->addMediaFlowOutStateChangeListener(elEventDebug, [=](){
897 mEndpoint->addKeyframeRequiredListener(elEventKeyframeRequired, [=](){
898 //mMultiqueue->create([this, cConnected]{
899 // mMultiqueue->connect([this, cConnected]{
900 mPlayer->create([this, cConnected]{
901 mPassThrough->create([this, cConnected]{
902 mEndpoint->connect([this, cConnected]{
903 mPassThrough->connect([this, cConnected]{
904 //mPlayer->play([this, cConnected]{
905 cConnected();
906 //mPlayer->connect(cConnected, *mEndpoint); // connect
907 //});
908 }, *mEndpoint);
909 }, *mPassThrough);
910 });
911 });
912 //}, *mEndpoint); // mEndpoint->connect
913 // }, *mEndpoint); // mMultiqueue->connect
914 //}); // mMultiqueue->create
915 }); // addKeyframeRequiredListener
916
917 });
918 });
919 });
920 });
921 });
922 });
923 }); // create
924}*/
925
926
927
928void
929GstRemoteParticipant::setLocalDescription(GstWebRTCSessionDescription* gstwebrtcdesc)
930{
931 gstWebRTCSetDescription(mRtpTransportElement, "set-local-description", gstwebrtcdesc);
932}
933
934void
935GstRemoteParticipant::setRemoteDescription(GstWebRTCSessionDescription* gstwebrtcdesc)
936{
937 gstWebRTCSetDescription(mRtpTransportElement, "set-remote-description", gstwebrtcdesc);
938}
939
940void
941GstRemoteParticipant::initRtpSession(bool isWebRTC)
942{
943 if(!mRtpSession)
944 {
945 mRtpSession = make_shared<GstRtpSession>(*mRtpManager, isWebRTC);
946 mRtpSession->setKeyframeRequestHandler([this](){requestKeyframeFromPeer();});
947
948 shared_ptr<flowmanager::HEPRTCPEventLoggingHandler> handler =
949 std::dynamic_pointer_cast<flowmanager::HEPRTCPEventLoggingHandler>(
951 if(handler)
952 {
953 mRtpSession->initHomer(getDialogSet().getDialogSetId().getCallId(),
954 handler->getHepAgent());
955 }
956 }
957}
958
959void
960GstRemoteParticipant::buildSdpOffer(bool holdSdp, CallbackSdpReady sdpReady, bool preferExistingSdp)
961{
962 bool useExistingSdp = false;
963 if(getLocalSdp())
964 {
965 useExistingSdp = preferExistingSdp || mReuseSdpAnswer;
966 }
967
968 try
969 {
970 bool isWebRTC = false; // FIXME
971
972 initRtpSession(isWebRTC);
973
974 // FIXME - these are currently hardcoded in GstRtpSession::buildOffer
975 // FIXME - option to select audio only
976 // FIXME - other codecs (G.711, H.264, ...)
977 /*if(isWebRTC)
978 {
979 mRtpSession->addLocalCaps(Gst::Caps::create_from_string(GST_RTP_CAPS_VP8));
980 mRtpSession->addLocalCaps(Gst::Caps::create_from_string(GST_RTP_CAPS_OPUS));
981 }
982 else
983 {
984 mRtpSession->addLocalCaps(Gst::Caps::create_from_string(GST_RTP_CAPS_H264));
985 mRtpSession->addLocalCaps(Gst::Caps::create_from_string(GST_RTP_CAPS_PCMA));
986 }*/
987
988 bool firstUseEndpoint = initEndpointIfRequired(isWebRTC);
989
990 std::function<void(GstPromise * _promise, gpointer user_data)> cOnOfferReady =
991 [this, holdSdp, sdpReady](GstPromise * _promise, gpointer user_data){
992
993 DebugLog(<<"cOnOfferReady invoked");
994 GstWebRTCSessionDescription *_gstwebrtcoffer = nullptr;
995
996 if(gst_promise_wait(_promise) != GST_PROMISE_RESULT_REPLIED)
997 {
998 resip_assert(0);
999 }
1000
1001 // FIXME - overriding the const reply with a cast,
1002 // need to fix in Gstreamermm API
1003 Gst::Structure reply((GstStructure*)gst_promise_get_reply (_promise));
1004 gst_structure_get (reply.gobj(), "offer",
1005 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &_gstwebrtcoffer, NULL);
1006 if(!_gstwebrtcoffer)
1007 {
1008 Glib::Error __error;
1009 reply.get_field<Glib::Error>("error", __error);
1010 ErrLog(<<"failed to get SDP offer: " << __error.what());
1011 gst_promise_unref (_promise);
1012 sdpReady(false, nullptr);
1013 return;
1014 }
1015 gst_promise_unref (_promise);
1016
1017 setLocalDescription(_gstwebrtcoffer);
1018
1019 /* Send offer to peer */
1020 std::shared_ptr<SdpContents> _offer(createSdpContentsFromGstreamer(_gstwebrtcoffer));
1021 gst_webrtc_session_description_free (_gstwebrtcoffer);
1022 //_offer->session().transformLocalHold(holdSdp); // FIXME - tell Gstreamer to give us hold SDP
1023
1024 setLocalSdpGathering(*_offer);
1025 mRtpSession->setLocalSdp(_offer);
1026
1027 // FIXME - this should be done now if there is no ICE (non-WebRTC peer)
1028 //setProposedSdp(*_offer);
1029 //sdpReady(true, std::move(_offer));
1030 };
1031
1032 std::function<void()> cConnected = [this, cOnOfferReady, sdpReady, isWebRTC]{
1033 if(isWebRTC)
1034 {
1035 // FIXME - can we tell Gst to use holdSdp?
1036 // We currently mangle the SDP after-the-fact in cOnOfferReady
1037
1038 // From looking at the webrtcbin demo and related discussions in
1039 // bug trackers, it appears that all the elements should be added to
1040 // the pipeline and joined together before invoking create-offer.
1041 // The caps of the sink pads that have already been created and connected
1042 // on webrtcbin determine which media descriptors (audio, video)
1043 // will be present in the SDP offer.
1044 GstPromise *promise;
1045 std::function<void(GstPromise * _promise, gpointer user_data)> *_cOnOfferReady =
1046 new std::function<void(GstPromise * _promise, gpointer user_data)>(cOnOfferReady);
1047 promise = gst_promise_new_with_change_func (promise_stub, _cOnOfferReady,
1048 NULL);
1049 g_signal_emit_by_name (mRtpTransportElement->gobj(), "create-offer", NULL, promise);
1050 }
1051 else
1052 {
1053 bool sendAudio = true;
1054 bool sendVideo = true;
1055 std::unique_ptr<SdpContents> offer(dynamic_cast<SdpContents*>(mRtpSession->buildOffer(sendAudio, sendVideo)->clone()));
1056 mWaitingAnswer = true;
1057
1058 DebugLog(<<"built offer: " << *offer);
1059 setProposedSdp(*offer);
1060 sdpReady(true, std::move(offer));
1061 }
1062 };
1063
1064 if(firstUseEndpoint)
1065 {
1066 CallbackSdpReady _sdpReady = [this, sdpReady](bool sdpOk, std::unique_ptr<resip::SdpContents> sdp)
1067 {
1068 if(!sdpOk)
1069 {
1070 sdpReady(false, std::move(sdp));
1071 return;
1072 }
1073 setProposedSdp(*sdp);
1074 sdpReady(true, std::move(sdp));
1075 };
1076
1077 createAndConnectElements(isWebRTC, cConnected, _sdpReady);
1078 }
1079 else
1080 {
1081 if(!useExistingSdp)
1082 {
1083 cConnected();
1084 }
1085 else
1086 {
1087 std::ostringstream offerMangledBuf;
1088 getLocalSdp()->session().transformLocalHold(isHolding());
1089 offerMangledBuf << *getLocalSdp();
1090 std::shared_ptr<std::string> offerMangledStr = std::make_shared<std::string>(offerMangledBuf.str());
1091 // FIXME cOnOfferReady(*offerMangledStr);
1092 }
1093 }
1094 }
1095 catch(exception& e)
1096 {
1097 ErrLog(<<"something went wrong: " << e.what());
1098 sdpReady(false, nullptr);
1099 }
1100}
1101
1102bool
1103GstRemoteParticipant::onGstBusMessage(const RefPtr<Gst::Bus>&, const RefPtr<Gst::Message>& message)
1104{
1105 switch(message->get_message_type())
1106 {
1107 case Gst::MESSAGE_EOS:
1108 ErrLog(<< "End of stream");
1109 //main_loop->quit(); // FIXME - from echoTest
1110 return false;
1111 case Gst::MESSAGE_ERROR:
1112 {
1113 Glib::Error error;
1114 std::string debug_message;
1115 RefPtr<MessageError>::cast_static(message)->parse(error, debug_message);
1116 ErrLog(<< "Error: " << error.what() << std::endl << debug_message);
1117 // main_loop->quit(); // FIXME - from echoTest
1118 return false;
1119 }
1120 case Gst::MESSAGE_STATE_CHANGED:
1121 DebugLog(<< "state changed");
1122 return true;
1123 case Gst::MESSAGE_NEW_CLOCK:
1124 DebugLog(<< "new clock");
1125 return true;
1126 case Gst::MESSAGE_STREAM_STATUS:
1127 DebugLog(<< "stream status");
1128 return true;
1129
1130 default:
1131 ErrLog(<<"unhandled Bus Message: " << message->get_message_type());
1132 return true;
1133 }
1134
1135 return true;
1136}
1137
1138void
1139GstRemoteParticipant::buildSdpAnswer(const SdpContents& offer, CallbackSdpReady sdpReady)
1140{
1141 bool requestSent = false;
1142
1143 std::shared_ptr<SdpContents> offerMangled(dynamic_cast<SdpContents*>(offer.clone()));
1144 while(mRemoveExtraMediaDescriptors && offerMangled->session().media().size() > 2)
1145 {
1146 // FIXME hack to remove BFCP
1147 DebugLog(<<"more than 2 media descriptors, removing the last");
1148 offerMangled->session().media().pop_back();
1149 }
1150
1151 try
1152 {
1153 // do some checks on the offer
1154 // check for video, check for WebRTC
1155 bool isWebRTC = offerMangled->session().isWebRTC();
1156 DebugLog(<<"peer is " << (isWebRTC ? "WebRTC":"not WebRTC"));
1157
1158 initRtpSession(isWebRTC);
1159
1160 if(!isWebRTC)
1161 {
1162 // RFC 4145 uses the attribute name "setup"
1163 // We override the attribute name and use the legacy name "direction"
1164 // from the drafts up to draft-ietf-mmusic-sdp-comedia-05.txt
1165 // Tested with Gst and Cisco EX90
1166 // https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-sdp-comedia-05
1167 // https://datatracker.ietf.org/doc/html/rfc4145
1168
1169 // FIXME was this only necessary for Kurento?
1170 //offerMangled->session().transformCOMedia("active", "direction");
1171 }
1172
1173 std::shared_ptr<GstWebRTCSessionDescription> gstwebrtcoffer;
1174 if(isWebRTC)
1175 {
1176 gstwebrtcoffer = createGstWebRTCSessionDescriptionFromSdpContents(GST_WEBRTC_SDP_TYPE_OFFER, *offerMangled);
1177 if(!gstwebrtcoffer)
1178 {
1179 ErrLog(<<"failed to convert offer to GstWebRTCSessionDescription");
1180 sdpReady(false, nullptr);
1181 }
1182 mRtpSession->setRemoteSdp(offerMangled);
1183 }
1184 else
1185 {
1186 std::shared_ptr<SdpContents> answer = mRtpSession->buildAnswer(offerMangled);
1187
1188 DebugLog(<<"built answer: " << *answer);
1189 }
1190
1191 bool firstUseEndpoint = initEndpointIfRequired(isWebRTC);
1192
1193 std::function<void(GstPromise* _promise, gpointer user_data)> cAnswerCreated =
1194 [this, sdpReady](GstPromise * _promise, gpointer user_data)
1195 {
1196 DebugLog(<<"cAnswerCreated invoked");
1197
1198 if(gst_promise_wait(_promise) != GST_PROMISE_RESULT_REPLIED)
1199 {
1200 resip_assert(0);
1201 }
1202
1203 GstWebRTCSessionDescription *_gstwebrtcanswer = NULL;
1204 // FIXME - overriding the const reply with a cast,
1205 // need to fix in Gstreamermm API
1206 Gst::Structure reply((GstStructure*)gst_promise_get_reply (_promise));
1207 // FIXME - need Gst::WebRTCSessionDescription
1208 //_reply.get_field<Gst::WebRTCSessionDescription>("answer",
1209 // _gstwebrtcanswer);
1210 gst_structure_get (reply.gobj(), "answer",
1211 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &_gstwebrtcanswer, NULL);
1212 if(!_gstwebrtcanswer)
1213 {
1214 Glib::Error __error;
1215 reply.get_field<Glib::Error>("error", __error);
1216 ErrLog(<<"failed to get SDP answer: " << __error.what());
1217 gst_promise_unref (_promise);
1218 sdpReady(false, nullptr);
1219 return;
1220 }
1221 gst_promise_unref (_promise);
1222
1223 setLocalDescription(_gstwebrtcanswer);
1224
1225 std::unique_ptr<SdpContents> _answer(createSdpContentsFromGstreamer(_gstwebrtcanswer));
1226 gst_webrtc_session_description_free (_gstwebrtcanswer);
1227 //_answer->session().transformLocalHold(isHolding()); // FIXME - Gstreamer can hold?
1228 setLocalSdpGathering(*_answer);
1229
1230 /* FIXME
1231 setLocalSdp(*_answer);
1232 setRemoteSdp(*offerMangled);
1233 sdpReady(true, std::move(_answer)); */
1234 };
1235
1236 std::function<void()> cConnected = [this, isWebRTC, cAnswerCreated, gstwebrtcoffer, offerMangled, sdpReady]() {
1237 if(isWebRTC)
1238 {
1239 // FIXME - Gst::Promise
1240 DebugLog(<<"submitting SDP offer to Gstreamer element");
1241 GstPromise *_promise = nullptr;
1242 /* Set remote description on our pipeline */
1243 setRemoteDescription(gstwebrtcoffer.get());
1244
1245 std::function<void(GstPromise * _promise, gpointer user_data)> *_cAnswerCreated =
1246 new std::function<void(GstPromise * _promise, gpointer user_data)>(cAnswerCreated);
1247 _promise = gst_promise_new_with_change_func (promise_stub, _cAnswerCreated,
1248 NULL);
1249 g_signal_emit_by_name (mRtpTransportElement->gobj(), "create-answer", NULL, _promise);
1250 }
1251 else
1252 {
1253 //cAnswerCreated
1254 std::unique_ptr<SdpContents> answer(new SdpContents(*mRtpSession->getLocalSdp()));
1255 setLocalSdp(*answer);
1256 setRemoteSdp(*offerMangled);
1258 sdpReady(true, std::move(answer));
1259 }
1260 };
1261
1262
1263
1264 DebugLog(<<"request SDP answer from Gstreamer");
1265
1266
1267
1268 CallbackSdpReady _sdpReady = [this, offerMangled, sdpReady](bool sdpOk, std::unique_ptr<resip::SdpContents> sdp)
1269 {
1270 if(!sdpOk)
1271 {
1272 sdpReady(false, std::move(sdp));
1273 return;
1274 }
1275 setLocalSdp(*sdp);
1276 setRemoteSdp(*offerMangled);
1277 sdpReady(true, std::move(sdp));
1278 };
1279
1280 if(firstUseEndpoint)
1281 {
1282 createAndConnectElements(isWebRTC, cConnected, _sdpReady);
1283 }
1284 else
1285 {
1286 cConnected();
1287 }
1288
1289 /*bool firstUseEndpoint = initEndpointIfRequired(isWebRTC);
1290
1291 gstreamer::ContinuationString cOnAnswerReady = [this, offerMangled, isWebRTC, sdpReady](const std::string& answer){
1292 StackLog(<<"answer FROM Gst: " << answer);
1293 HeaderFieldValue hfv(answer.data(), answer.size());
1294 Mime type("application", "sdp");
1295 std::unique_ptr<SdpContents> _answer(new SdpContents(hfv, type));
1296 _answer->session().transformLocalHold(isHolding());
1297 setLocalSdp(*_answer);
1298 setRemoteSdp(*offerMangled);
1299 sdpReady(true, std::move(_answer));
1300 };
1301
1302 gstreamer::ContinuationVoid cConnected = [this, offerMangled, offerMangledStr, isWebRTC, firstUseEndpoint, sdpReady, cOnAnswerReady]{
1303 if(!firstUseEndpoint && mReuseSdpAnswer)
1304 {
1305 // FIXME - Gst should handle hold/resume
1306 // but it fails with SDP_END_POINT_ALREADY_NEGOTIATED
1307 // if we call processOffer more than once
1308 std::ostringstream answerBuf;
1309 answerBuf << *getLocalSdp();
1310 std::shared_ptr<std::string> answerStr = std::make_shared<std::string>(answerBuf.str());
1311 cOnAnswerReady(*answerStr);
1312 return;
1313 }
1314 mEndpoint->processOffer([this, offerMangled, isWebRTC, sdpReady, cOnAnswerReady](const std::string& answer){
1315 if(isWebRTC)
1316 {
1317 if(mTrickleIcePermitted && offerMangled->session().isTrickleIceSupported())
1318 {
1319 HeaderFieldValue hfv(answer.data(), answer.size());
1320 Mime type("application", "sdp");
1321 std::unique_ptr<SdpContents> _local(new SdpContents(hfv, type));
1322 DebugLog(<<"storing incomplete webrtc answer");
1323 setLocalSdp(*_local);
1324 setRemoteSdp(*offerMangled);
1325 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(getInviteSessionHandle().get());
1326 sis->provideAnswer(*_local);
1327 sis->provisional(183, true);
1328 //getDialogSet().provideAnswer(std::move(_local), getInviteSessionHandle(), false, true);
1329 enableTrickleIce(); // now we are in early media phase, it is safe to send INFO
1330
1331 // FIXME - if we sent an SDP answer here,
1332 // make sure we don't call provideAnswer again on 200 OK
1333 }
1334
1335 doIceGathering(cOnAnswerReady);
1336 }
1337 else
1338 {
1339 cOnAnswerReady(answer);
1340 }
1341 }, *offerMangledStr); // processOffer
1342 };
1343
1344 if(firstUseEndpoint)
1345 {
1346 createAndConnectElements(cConnected);
1347 }
1348 else
1349 {
1350 cConnected();
1351 }*/
1352
1353 requestSent = true;
1354 }
1355 catch(exception& e)
1356 {
1357 ErrLog(<<"something went wrong: " << e.what()); // FIXME - add try/catch to Continuation
1358 requestSent = false;
1359 }
1360
1361 if(!requestSent)
1362 {
1363 sdpReady(false, nullptr);
1364 }
1365}
1366
1367void
1369{
1370 // FIXME Gst - implement, may need to break up this method into multiple parts
1371 StackLog(<<"adjustRTPStreams");
1372
1373 std::shared_ptr<SdpContents> localSdp = sendingOffer ? getDialogSet().getProposedSdp() : getLocalSdp();
1374 resip_assert(localSdp);
1375
1376 std::shared_ptr<SdpContents> remoteSdp = getRemoteSdp();
1377 bool remoteSdpChanged = remoteSdp.get() != mLastRemoteSdp; // FIXME - better way to do this?
1378 mLastRemoteSdp = remoteSdp.get();
1379 if(remoteSdp)
1380 {
1381 const SdpContents::Session::Direction& remoteDirection = remoteSdp->session().getDirection();
1382 if(!remoteDirection.recv())
1383 {
1384 setRemoteHold(true);
1385 }
1386 else
1387 {
1388 setRemoteHold(false);
1389 }
1390 if(remoteSdpChanged && mWaitingAnswer)
1391 {
1392 // FIXME - maybe this should not be in adjustRTPStreams
1393 DebugLog(<<"remoteSdp has changed, sending to Gstreamer");
1394 mWaitingAnswer = false;
1395 if(isWebRTCSession())
1396 {
1397 std::shared_ptr<GstWebRTCSessionDescription> gstwebrtcanswer =
1398 createGstWebRTCSessionDescriptionFromSdpContents(GST_WEBRTC_SDP_TYPE_ANSWER, *remoteSdp);
1399 if(!gstwebrtcanswer)
1400 {
1401 ErrLog(<<"failed to convert offer to GstWebRTCSessionDescription");
1402 return;
1403 }
1404 mRtpSession->setRemoteSdp(remoteSdp);
1405 setRemoteDescription(gstwebrtcanswer.get());
1406 }
1407 else
1408 {
1409 mRtpSession->processAnswer(remoteSdp);
1410 }
1411 /*mEndpoint->processAnswer([this](const std::string updatedOffer){
1412 // FIXME - use updatedOffer
1413 WarningLog(<<"Gst has processed the peer's SDP answer");
1414 StackLog(<<"updatedOffer FROM Gst: " << updatedOffer);
1415 HeaderFieldValue hfv(updatedOffer.data(), updatedOffer.size());
1416 Mime type("application", "sdp");
1417 std::unique_ptr<SdpContents> _updatedOffer(new SdpContents(hfv, type));
1418 _updatedOffer->session().transformLocalHold(isHolding());
1419 setLocalSdp(*_updatedOffer);
1420 //c(true, std::move(_updatedOffer));
1421 }, answerBuf.str());*/
1422 }
1423 for(int i = 1000; i <= 5000; i+=1000)
1424 {
1425 std::chrono::milliseconds _i = std::chrono::milliseconds(i);
1426 std::chrono::milliseconds __i = std::chrono::milliseconds(i + 500);
1429 }
1430 }
1431 debugGraph();
1432}
1433
1434void
1435GstRemoteParticipant::addToConversation(Conversation *conversation, unsigned int inputGain, unsigned int outputGain)
1436{
1437 RemoteParticipant::addToConversation(conversation, inputGain, outputGain);
1438}
1439
1440void
1445
1446bool
1448{
1449 return true; // FIXME Gst - can we check with Gst somehow?
1450}
1451
1452void
1454{
1455/* std::shared_ptr<gstreamer::MediaElement> e = getWaitingModeElement();
1456 if(!e)
1457 {
1458 return;
1459 }
1460 e->connect([this]{
1461 DebugLog(<<"connected in waiting mode, waiting for peer");
1462 if(mWaitingModeVideo)
1463 {
1464 mPlayer->play([this]{}); // FIXME Gst async
1465 }
1466 else
1467 {
1468 mEndpoint->connect([this]{
1469 requestKeyframeFromPeer();
1470 }, *mPassThrough); // FIXME Gst async
1471 }
1472 requestKeyframeFromPeer();
1473 }, *mEndpoint);*/
1474}
1475
1476/*std::shared_ptr<gstreamer::MediaElement>
1477GstRemoteParticipant::getWaitingModeElement()
1478{
1479 if(mWaitingModeVideo)
1480 {
1481 return dynamic_pointer_cast<gstreamer::Endpoint>(mPlayer);
1482 }
1483 else
1484 {
1485 return mPassThrough;
1486 }
1487}*/
1488
1489bool
1491{
1493 {
1494 auto now = std::chrono::steady_clock::now();
1495 if(now < (mLastLocalKeyframeRequest + getKeyframeRequestInterval()))
1496 {
1497 DebugLog(<<"keyframe request ignored, too soon");
1498 return false;
1499 }
1500 mLastLocalKeyframeRequest = now;
1501 InfoLog(<<"onMediaControlEvent: sending to Gst");
1502 // FIXME - check the content of the event
1503 //mEndpoint->sendPictureFastUpdate([this](){}); // FIXME Gst async, do we need to wait for Gst here?
1504 return true;
1505 }
1506 else
1507 {
1508 WarningLog(<<"rejecting MediaControlEvent due to config option mWSAcceptsKeyframeRequests");
1509 return false;
1510 }
1511}
1512
1513void
1514GstRemoteParticipant::onRemoteIceCandidate(const resip::Data& candidate, unsigned int lineIndex, const resip::Data& mid)
1515{
1516 g_signal_emit_by_name (mRtpTransportElement->gobj(), "add-ice-candidate", lineIndex,
1517 candidate.c_str());
1518}
1519
1520bool
1522{
1523 DebugLog(<<"onTrickleIce: sending to Gst");
1524 // FIXME - did we already receive a suitable SDP for trickle ICE and send it to Gstreamer?
1525 // if not, Gstreamer is not ready for the candidates
1526 // FIXME - do we need to validate the ice-pwd password attribute here?
1527 for(auto m = trickleIce.media().cbegin(); m != trickleIce.media().cend(); m++)
1528 {
1529 if(m->exists("mid"))
1530 {
1531 const Data& mid = m->getValues("mid").front();
1532 unsigned int mLineIndex = mid.convertInt(); // FIXME - calculate from the full SDP
1533 if(m->exists("candidate"))
1534 {
1535 for(const auto& c : m->getValues("candidate"))
1536 {
1537 onRemoteIceCandidate(c, mLineIndex, mid);
1538 }
1539 }
1540 }
1541 else
1542 {
1543 WarningLog(<<"mid is missing for Medium in SDP fragment: " << trickleIce);
1544 return false;
1545 }
1546 }
1547 return true;
1548}
1549
1550void
1552{
1553 DebugLog(<<"requestKeyframe from local encoder");
1554
1555 Gst::Structure gSTForceKeyUnit("GstForceKeyUnit",
1556 "all-headers", true);
1557
1558 Gst::Iterator<Gst::Pad> it = mRtpTransportElement->iterate_sink_pads();
1559 while(it.next())
1560 {
1561 RefPtr<Pad> pad = *it;
1562 if(pad)
1563 {
1564 StackLog(<<"examining pad " << pad->get_name());
1565 RefPtr<Pad> peer = pad->get_peer();
1566 RefPtr<Caps> caps = pad->get_current_caps();
1567 if(peer && caps)
1568 {
1569 if(caps->get_structure(0).has_field("media"))
1570 {
1571 string mediaName;
1572 caps->get_structure(0).get_field("media", mediaName);
1573 StackLog(<<"mediaName: " << mediaName);
1574 if(mediaName == "video")
1575 {
1576 RefPtr<Gst::Event> _e = Glib::wrap(gst_event_new_custom(GST_EVENT_CUSTOM_UPSTREAM,
1577 gSTForceKeyUnit.gobj_copy()));
1578 peer->send_event(_e);
1579 }
1580 }
1581 }
1582 else
1583 {
1584 DebugLog(<<"no peer or no caps for this pad");
1585 }
1586 }
1587 else
1588 {
1589 WarningLog(<<"invalid pad");
1590 }
1591 }
1592}
1593
1594/* ====================================================================
1595
1596 Copyright (c) 2022, Software Freedom Institute https://softwarefreedom.institute
1597 Copyright (c) 2022, Daniel Pocock https://danielpocock.com
1598 Copyright (c) 2021, SIP Spectrum, Inc.
1599 Copyright (c) 2007-2008, Plantronics, Inc.
1600 All rights reserved.
1601
1602 Redistribution and use in source and binary forms, with or without
1603 modification, are permitted provided that the following conditions are
1604 met:
1605
1606 1. Redistributions of source code must retain the above copyright
1607 notice, this list of conditions and the following disclaimer.
1608
1609 2. Redistributions in binary form must reproduce the above copyright
1610 notice, this list of conditions and the following disclaimer in the
1611 documentation and/or other materials provided with the distribution.
1612
1613 3. Neither the name of Plantronics nor the names of its contributors
1614 may be used to endorse or promote products derived from this
1615 software without specific prior written permission.
1616
1617 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1618 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1619 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1620 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1621 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1622 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1623 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1624 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1625 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1626 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1627 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1628
1629 ==================================================================== */
void promise_stub(GstPromise *promise, void *u)
void voidStub(GstElement *webrtcbin, void *u)
void sendCapsEvent(RefPtr< Bin > bin, RefPtr< Caps > caps)
static const Data pad("\0\0\0\0\0\0\0", 7)
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define CritLog(args_)
Definition Logger.hxx:140
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
This class is one of two main classes of concern to an application using the UserAgent library.
virtual void requestKeyframeFromPeer(ParticipantHandle partHandle, std::chrono::duration< double > durationMs)
MediaStackAdapter & getMediaStackAdapter()
virtual void requestKeyframe(ParticipantHandle partHandle, std::chrono::duration< double > durationMs)
std::shared_ptr< resip::ConfigParse > getConfig()
This class extends the resip UserProfile to include Conversation Manager specific settings.
This class is used to manage membership of participants.
This class is one of two main classes of concern to an application using the UserAgent library.
This is the base class for a GstRemoteParticipant, GstLocalParticipant and a GstMediaResourceParticip...
GstMediaStackAdapter & mGstMediaStackAdapter
virtual void applyBridgeMixWeights() override
virtual void buildSdpOffer(bool holdSdp, CallbackSdpReady sdpReady, bool preferExistingSdp=false)
virtual bool onMediaControlEvent(resip::MediaControlContents::MediaControl &mediaControl)
virtual void requestKeyframe() override
Glib::RefPtr< Gst::Element > mRtpTransportElement
virtual void removeFromConversation(Conversation *conversation) override
Glib::RefPtr< Gst::Element > mMediaBin
GstRemoteParticipant(ParticipantHandle partHandle, ConversationManager &conversationManager, GstMediaStackAdapter &gstreamerMediaStackAdapter, resip::DialogUsageManager &dum, RemoteParticipantDialogSet &remoteParticipantDialogSet)
virtual void adjustRTPStreams(bool sendingOffer=false)
std::shared_ptr< resipgst::GstRtpSession > mRtpSession
std::shared_ptr< resipgst::GstRtpManager > mRtpManager
virtual bool onTrickleIce(resip::TrickleIceContents &trickleIce) override
virtual GstRemoteParticipantDialogSet & getGstDialogSet()
virtual void addToConversation(Conversation *conversation, unsigned int inputGain=100, unsigned int outputGain=100) override
Glib::RefPtr< Gst::Pipeline > mPipeline
virtual std::shared_ptr< flowmanager::RTCPEventLoggingHandler > getRTCPEventLoggingHandler() const
This is the base class for a RemoteParticipant, LocalParticipant and a MediaResourceParticipant.
virtual void unregisterFromAllConversations()
ParticipantHandle mHandle
ConversationManager & mConversationManager
This class is used by Invite DialogSets.
virtual std::shared_ptr< resip::SdpContents > getProposedSdp()
This class represent a remote participant.
void setProposedSdp(const resip::SdpContents &sdp)
void setRemoteSdp(const resip::SdpContents &sdp, bool answer=false)
virtual std::chrono::duration< double > getKeyframeRequestInterval() const
virtual void onLocalIceCandidate(const resip::Data &candidate, unsigned int lineIndex, const resip::Data &mid)
std::function< void(bool sdpOk, std::unique_ptr< resip::SdpContents > sdp)> CallbackSdpReady
void setLocalSdp(const resip::SdpContents &sdp)
void setLocalSdpGathering(const resip::SdpContents &sdp)
virtual void addToConversation(Conversation *conversation, unsigned int inputGain=100, unsigned int outputGain=100) override
std::shared_ptr< resip::SdpContents > getLocalSdp()
virtual void removeFromConversation(Conversation *conversation) override
std::shared_ptr< resip::SdpContents > getRemoteSdp()
void setRemoteHold(bool remoteHold)
RemoteParticipantDialogSet & getDialogSet()
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
int convertInt() const
Takes the contents of the Data and converts them to an integer.
Definition Data.cxx:1684
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
virtual Contents * clone() const
duplicate an SdpContents object
const SdpContents::Session::MediumContainer & media() const
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;