| /* | 
 |  *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "examples/unityplugin/simple_peer_connection.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "absl/memory/memory.h" | 
 | #include "api/audio_codecs/builtin_audio_decoder_factory.h" | 
 | #include "api/audio_codecs/builtin_audio_encoder_factory.h" | 
 | #include "api/create_peerconnection_factory.h" | 
 | #include "media/engine/internal_decoder_factory.h" | 
 | #include "media/engine/internal_encoder_factory.h" | 
 | #include "media/engine/multiplex_codec_factory.h" | 
 | #include "modules/audio_device/include/audio_device.h" | 
 | #include "modules/audio_processing/include/audio_processing.h" | 
 | #include "modules/video_capture/video_capture_factory.h" | 
 | #include "pc/video_track_source.h" | 
 | #include "test/vcm_capturer.h" | 
 |  | 
 | #if defined(WEBRTC_ANDROID) | 
 | #include "examples/unityplugin/class_reference_holder.h" | 
 | #include "modules/utility/include/helpers_android.h" | 
 | #include "sdk/android/src/jni/android_video_track_source.h" | 
 | #include "sdk/android/src/jni/jni_helpers.h" | 
 | #endif | 
 |  | 
 | // Names used for media stream ids. | 
 | const char kAudioLabel[] = "audio_label"; | 
 | const char kVideoLabel[] = "video_label"; | 
 | const char kStreamId[] = "stream_id"; | 
 |  | 
 | namespace { | 
 | static int g_peer_count = 0; | 
 | static std::unique_ptr<rtc::Thread> g_worker_thread; | 
 | static std::unique_ptr<rtc::Thread> g_signaling_thread; | 
 | static rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> | 
 |     g_peer_connection_factory; | 
 | #if defined(WEBRTC_ANDROID) | 
 | // Android case: the video track does not own the capturer, and it | 
 | // relies on the app to dispose the capturer when the peerconnection | 
 | // shuts down. | 
 | static jobject g_camera = nullptr; | 
 | #else | 
 | class CapturerTrackSource : public webrtc::VideoTrackSource { | 
 |  public: | 
 |   static rtc::scoped_refptr<CapturerTrackSource> Create() { | 
 |     const size_t kWidth = 640; | 
 |     const size_t kHeight = 480; | 
 |     const size_t kFps = 30; | 
 |     const size_t kDeviceIndex = 0; | 
 |     std::unique_ptr<webrtc::test::VcmCapturer> capturer = absl::WrapUnique( | 
 |         webrtc::test::VcmCapturer::Create(kWidth, kHeight, kFps, kDeviceIndex)); | 
 |     if (!capturer) { | 
 |       return nullptr; | 
 |     } | 
 |     return new rtc::RefCountedObject<CapturerTrackSource>(std::move(capturer)); | 
 |   } | 
 |  | 
 |  protected: | 
 |   explicit CapturerTrackSource( | 
 |       std::unique_ptr<webrtc::test::VcmCapturer> capturer) | 
 |       : VideoTrackSource(/*remote=*/false), capturer_(std::move(capturer)) {} | 
 |  | 
 |  private: | 
 |   rtc::VideoSourceInterface<webrtc::VideoFrame>* source() override { | 
 |     return capturer_.get(); | 
 |   } | 
 |   std::unique_ptr<webrtc::test::VcmCapturer> capturer_; | 
 | }; | 
 |  | 
 | #endif | 
 |  | 
 | std::string GetEnvVarOrDefault(const char* env_var_name, | 
 |                                const char* default_value) { | 
 |   std::string value; | 
 |   const char* env_var = getenv(env_var_name); | 
 |   if (env_var) | 
 |     value = env_var; | 
 |  | 
 |   if (value.empty()) | 
 |     value = default_value; | 
 |  | 
 |   return value; | 
 | } | 
 |  | 
 | std::string GetPeerConnectionString() { | 
 |   return GetEnvVarOrDefault("WEBRTC_CONNECT", "stun:stun.l.google.com:19302"); | 
 | } | 
 |  | 
 | class DummySetSessionDescriptionObserver | 
 |     : public webrtc::SetSessionDescriptionObserver { | 
 |  public: | 
 |   static DummySetSessionDescriptionObserver* Create() { | 
 |     return new rtc::RefCountedObject<DummySetSessionDescriptionObserver>(); | 
 |   } | 
 |   virtual void OnSuccess() { RTC_LOG(INFO) << __FUNCTION__; } | 
 |   virtual void OnFailure(webrtc::RTCError error) { | 
 |     RTC_LOG(INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " | 
 |                   << error.message(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   DummySetSessionDescriptionObserver() {} | 
 |   ~DummySetSessionDescriptionObserver() {} | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool SimplePeerConnection::InitializePeerConnection(const char** turn_urls, | 
 |                                                     const int no_of_urls, | 
 |                                                     const char* username, | 
 |                                                     const char* credential, | 
 |                                                     bool is_receiver) { | 
 |   RTC_DCHECK(peer_connection_.get() == nullptr); | 
 |  | 
 |   if (g_peer_connection_factory == nullptr) { | 
 |     g_worker_thread = rtc::Thread::Create(); | 
 |     g_worker_thread->Start(); | 
 |     g_signaling_thread = rtc::Thread::Create(); | 
 |     g_signaling_thread->Start(); | 
 |  | 
 |     g_peer_connection_factory = webrtc::CreatePeerConnectionFactory( | 
 |         g_worker_thread.get(), g_worker_thread.get(), g_signaling_thread.get(), | 
 |         nullptr, webrtc::CreateBuiltinAudioEncoderFactory(), | 
 |         webrtc::CreateBuiltinAudioDecoderFactory(), | 
 |         std::unique_ptr<webrtc::VideoEncoderFactory>( | 
 |             new webrtc::MultiplexEncoderFactory( | 
 |                 std::make_unique<webrtc::InternalEncoderFactory>())), | 
 |         std::unique_ptr<webrtc::VideoDecoderFactory>( | 
 |             new webrtc::MultiplexDecoderFactory( | 
 |                 std::make_unique<webrtc::InternalDecoderFactory>())), | 
 |         nullptr, nullptr); | 
 |   } | 
 |   if (!g_peer_connection_factory.get()) { | 
 |     DeletePeerConnection(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   g_peer_count++; | 
 |   if (!CreatePeerConnection(turn_urls, no_of_urls, username, credential)) { | 
 |     DeletePeerConnection(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   mandatory_receive_ = is_receiver; | 
 |   return peer_connection_.get() != nullptr; | 
 | } | 
 |  | 
 | bool SimplePeerConnection::CreatePeerConnection(const char** turn_urls, | 
 |                                                 const int no_of_urls, | 
 |                                                 const char* username, | 
 |                                                 const char* credential) { | 
 |   RTC_DCHECK(g_peer_connection_factory.get() != nullptr); | 
 |   RTC_DCHECK(peer_connection_.get() == nullptr); | 
 |  | 
 |   local_video_observer_.reset(new VideoObserver()); | 
 |   remote_video_observer_.reset(new VideoObserver()); | 
 |  | 
 |   // Add the turn server. | 
 |   if (turn_urls != nullptr) { | 
 |     if (no_of_urls > 0) { | 
 |       webrtc::PeerConnectionInterface::IceServer turn_server; | 
 |       for (int i = 0; i < no_of_urls; i++) { | 
 |         std::string url(turn_urls[i]); | 
 |         if (url.length() > 0) | 
 |           turn_server.urls.push_back(turn_urls[i]); | 
 |       } | 
 |  | 
 |       std::string user_name(username); | 
 |       if (user_name.length() > 0) | 
 |         turn_server.username = username; | 
 |  | 
 |       std::string password(credential); | 
 |       if (password.length() > 0) | 
 |         turn_server.password = credential; | 
 |  | 
 |       config_.servers.push_back(turn_server); | 
 |     } | 
 |   } | 
 |  | 
 |   // Add the stun server. | 
 |   webrtc::PeerConnectionInterface::IceServer stun_server; | 
 |   stun_server.uri = GetPeerConnectionString(); | 
 |   config_.servers.push_back(stun_server); | 
 |   config_.enable_rtp_data_channel = true; | 
 |   config_.enable_dtls_srtp = false; | 
 |  | 
 |   peer_connection_ = g_peer_connection_factory->CreatePeerConnection( | 
 |       config_, nullptr, nullptr, this); | 
 |  | 
 |   return peer_connection_.get() != nullptr; | 
 | } | 
 |  | 
 | void SimplePeerConnection::DeletePeerConnection() { | 
 |   g_peer_count--; | 
 |  | 
 | #if defined(WEBRTC_ANDROID) | 
 |   if (g_camera) { | 
 |     JNIEnv* env = webrtc::jni::GetEnv(); | 
 |     jclass pc_factory_class = | 
 |         unity_plugin::FindClass(env, "org/webrtc/UnityUtility"); | 
 |     jmethodID stop_camera_method = webrtc::GetStaticMethodID( | 
 |         env, pc_factory_class, "StopCamera", "(Lorg/webrtc/VideoCapturer;)V"); | 
 |  | 
 |     env->CallStaticVoidMethod(pc_factory_class, stop_camera_method, g_camera); | 
 |     CHECK_EXCEPTION(env); | 
 |  | 
 |     g_camera = nullptr; | 
 |   } | 
 | #endif | 
 |  | 
 |   CloseDataChannel(); | 
 |   peer_connection_ = nullptr; | 
 |   active_streams_.clear(); | 
 |  | 
 |   if (g_peer_count == 0) { | 
 |     g_peer_connection_factory = nullptr; | 
 |     g_signaling_thread.reset(); | 
 |     g_worker_thread.reset(); | 
 |   } | 
 | } | 
 |  | 
 | bool SimplePeerConnection::CreateOffer() { | 
 |   if (!peer_connection_.get()) | 
 |     return false; | 
 |  | 
 |   webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; | 
 |   if (mandatory_receive_) { | 
 |     options.offer_to_receive_audio = true; | 
 |     options.offer_to_receive_video = true; | 
 |   } | 
 |   peer_connection_->CreateOffer(this, options); | 
 |   return true; | 
 | } | 
 |  | 
 | bool SimplePeerConnection::CreateAnswer() { | 
 |   if (!peer_connection_.get()) | 
 |     return false; | 
 |  | 
 |   webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; | 
 |   if (mandatory_receive_) { | 
 |     options.offer_to_receive_audio = true; | 
 |     options.offer_to_receive_video = true; | 
 |   } | 
 |   peer_connection_->CreateAnswer(this, options); | 
 |   return true; | 
 | } | 
 |  | 
 | void SimplePeerConnection::OnSuccess( | 
 |     webrtc::SessionDescriptionInterface* desc) { | 
 |   peer_connection_->SetLocalDescription( | 
 |       DummySetSessionDescriptionObserver::Create(), desc); | 
 |  | 
 |   std::string sdp; | 
 |   desc->ToString(&sdp); | 
 |  | 
 |   if (OnLocalSdpReady) | 
 |     OnLocalSdpReady(desc->type().c_str(), sdp.c_str()); | 
 | } | 
 |  | 
 | void SimplePeerConnection::OnFailure(webrtc::RTCError error) { | 
 |   RTC_LOG(LERROR) << ToString(error.type()) << ": " << error.message(); | 
 |  | 
 |   // TODO(hta): include error.type in the message | 
 |   if (OnFailureMessage) | 
 |     OnFailureMessage(error.message()); | 
 | } | 
 |  | 
 | void SimplePeerConnection::OnIceCandidate( | 
 |     const webrtc::IceCandidateInterface* candidate) { | 
 |   RTC_LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); | 
 |  | 
 |   std::string sdp; | 
 |   if (!candidate->ToString(&sdp)) { | 
 |     RTC_LOG(LS_ERROR) << "Failed to serialize candidate"; | 
 |     return; | 
 |   } | 
 |  | 
 |   if (OnIceCandiateReady) | 
 |     OnIceCandiateReady(sdp.c_str(), candidate->sdp_mline_index(), | 
 |                        candidate->sdp_mid().c_str()); | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnLocalI420FrameReady( | 
 |     I420FRAMEREADY_CALLBACK callback) { | 
 |   if (local_video_observer_) | 
 |     local_video_observer_->SetVideoCallback(callback); | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnRemoteI420FrameReady( | 
 |     I420FRAMEREADY_CALLBACK callback) { | 
 |   if (remote_video_observer_) | 
 |     remote_video_observer_->SetVideoCallback(callback); | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnLocalDataChannelReady( | 
 |     LOCALDATACHANNELREADY_CALLBACK callback) { | 
 |   OnLocalDataChannelReady = callback; | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnDataFromDataChannelReady( | 
 |     DATAFROMEDATECHANNELREADY_CALLBACK callback) { | 
 |   OnDataFromDataChannelReady = callback; | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnFailure(FAILURE_CALLBACK callback) { | 
 |   OnFailureMessage = callback; | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnAudioBusReady( | 
 |     AUDIOBUSREADY_CALLBACK callback) { | 
 |   OnAudioReady = callback; | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnLocalSdpReadytoSend( | 
 |     LOCALSDPREADYTOSEND_CALLBACK callback) { | 
 |   OnLocalSdpReady = callback; | 
 | } | 
 |  | 
 | void SimplePeerConnection::RegisterOnIceCandiateReadytoSend( | 
 |     ICECANDIDATEREADYTOSEND_CALLBACK callback) { | 
 |   OnIceCandiateReady = callback; | 
 | } | 
 |  | 
 | bool SimplePeerConnection::SetRemoteDescription(const char* type, | 
 |                                                 const char* sdp) { | 
 |   if (!peer_connection_) | 
 |     return false; | 
 |  | 
 |   std::string remote_desc(sdp); | 
 |   std::string sdp_type(type); | 
 |   webrtc::SdpParseError error; | 
 |   webrtc::SessionDescriptionInterface* session_description( | 
 |       webrtc::CreateSessionDescription(sdp_type, remote_desc, &error)); | 
 |   if (!session_description) { | 
 |     RTC_LOG(WARNING) << "Can't parse received session description message. " | 
 |                         "SdpParseError was: " | 
 |                      << error.description; | 
 |     return false; | 
 |   } | 
 |   RTC_LOG(INFO) << " Received session description :" << remote_desc; | 
 |   peer_connection_->SetRemoteDescription( | 
 |       DummySetSessionDescriptionObserver::Create(), session_description); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool SimplePeerConnection::AddIceCandidate(const char* candidate, | 
 |                                            const int sdp_mlineindex, | 
 |                                            const char* sdp_mid) { | 
 |   if (!peer_connection_) | 
 |     return false; | 
 |  | 
 |   webrtc::SdpParseError error; | 
 |   std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate( | 
 |       webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate, &error)); | 
 |   if (!ice_candidate.get()) { | 
 |     RTC_LOG(WARNING) << "Can't parse received candidate message. " | 
 |                         "SdpParseError was: " | 
 |                      << error.description; | 
 |     return false; | 
 |   } | 
 |   if (!peer_connection_->AddIceCandidate(ice_candidate.get())) { | 
 |     RTC_LOG(WARNING) << "Failed to apply the received candidate"; | 
 |     return false; | 
 |   } | 
 |   RTC_LOG(INFO) << " Received candidate :" << candidate; | 
 |   return true; | 
 | } | 
 |  | 
 | void SimplePeerConnection::SetAudioControl(bool is_mute, bool is_record) { | 
 |   is_mute_audio_ = is_mute; | 
 |   is_record_audio_ = is_record; | 
 |  | 
 |   SetAudioControl(); | 
 | } | 
 |  | 
 | void SimplePeerConnection::SetAudioControl() { | 
 |   if (!remote_stream_) | 
 |     return; | 
 |   webrtc::AudioTrackVector tracks = remote_stream_->GetAudioTracks(); | 
 |   if (tracks.empty()) | 
 |     return; | 
 |  | 
 |   webrtc::AudioTrackInterface* audio_track = tracks[0]; | 
 |   std::string id = audio_track->id(); | 
 |   if (is_record_audio_) | 
 |     audio_track->AddSink(this); | 
 |   else | 
 |     audio_track->RemoveSink(this); | 
 |  | 
 |   for (auto& track : tracks) { | 
 |     if (is_mute_audio_) | 
 |       track->set_enabled(false); | 
 |     else | 
 |       track->set_enabled(true); | 
 |   } | 
 | } | 
 |  | 
 | void SimplePeerConnection::OnAddStream( | 
 |     rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) { | 
 |   RTC_LOG(INFO) << __FUNCTION__ << " " << stream->id(); | 
 |   remote_stream_ = stream; | 
 |   if (remote_video_observer_ && !remote_stream_->GetVideoTracks().empty()) { | 
 |     remote_stream_->GetVideoTracks()[0]->AddOrUpdateSink( | 
 |         remote_video_observer_.get(), rtc::VideoSinkWants()); | 
 |   } | 
 |   SetAudioControl(); | 
 | } | 
 |  | 
 | void SimplePeerConnection::AddStreams(bool audio_only) { | 
 |   if (active_streams_.find(kStreamId) != active_streams_.end()) | 
 |     return;  // Already added. | 
 |  | 
 |   rtc::scoped_refptr<webrtc::MediaStreamInterface> stream = | 
 |       g_peer_connection_factory->CreateLocalMediaStream(kStreamId); | 
 |  | 
 |   rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track( | 
 |       g_peer_connection_factory->CreateAudioTrack( | 
 |           kAudioLabel, g_peer_connection_factory->CreateAudioSource( | 
 |                            cricket::AudioOptions()))); | 
 |   std::string id = audio_track->id(); | 
 |   stream->AddTrack(audio_track); | 
 |  | 
 |   if (!audio_only) { | 
 | #if defined(WEBRTC_ANDROID) | 
 |     JNIEnv* env = webrtc::jni::GetEnv(); | 
 |     jclass pc_factory_class = | 
 |         unity_plugin::FindClass(env, "org/webrtc/UnityUtility"); | 
 |     jmethodID load_texture_helper_method = webrtc::GetStaticMethodID( | 
 |         env, pc_factory_class, "LoadSurfaceTextureHelper", | 
 |         "()Lorg/webrtc/SurfaceTextureHelper;"); | 
 |     jobject texture_helper = env->CallStaticObjectMethod( | 
 |         pc_factory_class, load_texture_helper_method); | 
 |     CHECK_EXCEPTION(env); | 
 |     RTC_DCHECK(texture_helper != nullptr) | 
 |         << "Cannot get the Surface Texture Helper."; | 
 |  | 
 |     rtc::scoped_refptr<webrtc::jni::AndroidVideoTrackSource> source( | 
 |         new rtc::RefCountedObject<webrtc::jni::AndroidVideoTrackSource>( | 
 |             g_signaling_thread.get(), env, /* is_screencast= */ false, | 
 |             /* align_timestamps= */ true)); | 
 |  | 
 |     // link with VideoCapturer (Camera); | 
 |     jmethodID link_camera_method = webrtc::GetStaticMethodID( | 
 |         env, pc_factory_class, "LinkCamera", | 
 |         "(JLorg/webrtc/SurfaceTextureHelper;)Lorg/webrtc/VideoCapturer;"); | 
 |     jobject camera_tmp = | 
 |         env->CallStaticObjectMethod(pc_factory_class, link_camera_method, | 
 |                                     (jlong)source.get(), texture_helper); | 
 |     CHECK_EXCEPTION(env); | 
 |     g_camera = (jobject)env->NewGlobalRef(camera_tmp); | 
 |  | 
 |     rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track( | 
 |         g_peer_connection_factory->CreateVideoTrack(kVideoLabel, | 
 |                                                     source.release())); | 
 |     stream->AddTrack(video_track); | 
 | #else | 
 |     rtc::scoped_refptr<CapturerTrackSource> video_device = | 
 |         CapturerTrackSource::Create(); | 
 |     if (video_device) { | 
 |       rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track( | 
 |           g_peer_connection_factory->CreateVideoTrack(kVideoLabel, | 
 |                                                       video_device)); | 
 |  | 
 |       stream->AddTrack(video_track); | 
 |     } | 
 | #endif | 
 |     if (local_video_observer_ && !stream->GetVideoTracks().empty()) { | 
 |       stream->GetVideoTracks()[0]->AddOrUpdateSink(local_video_observer_.get(), | 
 |                                                    rtc::VideoSinkWants()); | 
 |     } | 
 |   } | 
 |  | 
 |   if (!peer_connection_->AddStream(stream)) { | 
 |     RTC_LOG(LS_ERROR) << "Adding stream to PeerConnection failed"; | 
 |   } | 
 |  | 
 |   typedef std::pair<std::string, | 
 |                     rtc::scoped_refptr<webrtc::MediaStreamInterface>> | 
 |       MediaStreamPair; | 
 |   active_streams_.insert(MediaStreamPair(stream->id(), stream)); | 
 | } | 
 |  | 
 | bool SimplePeerConnection::CreateDataChannel() { | 
 |   struct webrtc::DataChannelInit init; | 
 |   init.ordered = true; | 
 |   init.reliable = true; | 
 |   data_channel_ = peer_connection_->CreateDataChannel("Hello", &init); | 
 |   if (data_channel_.get()) { | 
 |     data_channel_->RegisterObserver(this); | 
 |     RTC_LOG(LS_INFO) << "Succeeds to create data channel"; | 
 |     return true; | 
 |   } else { | 
 |     RTC_LOG(LS_INFO) << "Fails to create data channel"; | 
 |     return false; | 
 |   } | 
 | } | 
 |  | 
 | void SimplePeerConnection::CloseDataChannel() { | 
 |   if (data_channel_.get()) { | 
 |     data_channel_->UnregisterObserver(); | 
 |     data_channel_->Close(); | 
 |   } | 
 |   data_channel_ = nullptr; | 
 | } | 
 |  | 
 | bool SimplePeerConnection::SendDataViaDataChannel(const std::string& data) { | 
 |   if (!data_channel_.get()) { | 
 |     RTC_LOG(LS_INFO) << "Data channel is not established"; | 
 |     return false; | 
 |   } | 
 |   webrtc::DataBuffer buffer(data); | 
 |   data_channel_->Send(buffer); | 
 |   return true; | 
 | } | 
 |  | 
 | // Peerconnection observer | 
 | void SimplePeerConnection::OnDataChannel( | 
 |     rtc::scoped_refptr<webrtc::DataChannelInterface> channel) { | 
 |   channel->RegisterObserver(this); | 
 | } | 
 |  | 
 | void SimplePeerConnection::OnStateChange() { | 
 |   if (data_channel_) { | 
 |     webrtc::DataChannelInterface::DataState state = data_channel_->state(); | 
 |     if (state == webrtc::DataChannelInterface::kOpen) { | 
 |       if (OnLocalDataChannelReady) | 
 |         OnLocalDataChannelReady(); | 
 |       RTC_LOG(LS_INFO) << "Data channel is open"; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | //  A data buffer was successfully received. | 
 | void SimplePeerConnection::OnMessage(const webrtc::DataBuffer& buffer) { | 
 |   size_t size = buffer.data.size(); | 
 |   char* msg = new char[size + 1]; | 
 |   memcpy(msg, buffer.data.data(), size); | 
 |   msg[size] = 0; | 
 |   if (OnDataFromDataChannelReady) | 
 |     OnDataFromDataChannelReady(msg); | 
 |   delete[] msg; | 
 | } | 
 |  | 
 | // AudioTrackSinkInterface implementation. | 
 | void SimplePeerConnection::OnData(const void* audio_data, | 
 |                                   int bits_per_sample, | 
 |                                   int sample_rate, | 
 |                                   size_t number_of_channels, | 
 |                                   size_t number_of_frames) { | 
 |   if (OnAudioReady) | 
 |     OnAudioReady(audio_data, bits_per_sample, sample_rate, | 
 |                  static_cast<int>(number_of_channels), | 
 |                  static_cast<int>(number_of_frames)); | 
 | } | 
 |  | 
 | std::vector<uint32_t> SimplePeerConnection::GetRemoteAudioTrackSsrcs() { | 
 |   std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> receivers = | 
 |       peer_connection_->GetReceivers(); | 
 |  | 
 |   std::vector<uint32_t> ssrcs; | 
 |   for (const auto& receiver : receivers) { | 
 |     if (receiver->media_type() != cricket::MEDIA_TYPE_AUDIO) | 
 |       continue; | 
 |  | 
 |     std::vector<webrtc::RtpEncodingParameters> params = | 
 |         receiver->GetParameters().encodings; | 
 |  | 
 |     for (const auto& param : params) { | 
 |       uint32_t ssrc = param.ssrc.value_or(0); | 
 |       if (ssrc > 0) | 
 |         ssrcs.push_back(ssrc); | 
 |     } | 
 |   } | 
 |  | 
 |   return ssrcs; | 
 | } |