| /* |
| * libjingle |
| * Copyright 2011, Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <stdio.h> |
| |
| #include <list> |
| |
| #include "talk/app/webrtc/mediastream.h" |
| #include "talk/app/webrtc/peerconnection.h" |
| #include "talk/app/webrtc/test/fakevideocapturemodule.h" |
| #include "talk/base/gunit.h" |
| #include "talk/base/scoped_ptr.h" |
| #include "talk/base/thread.h" |
| #include "talk/session/phone/fakevideorenderer.h" |
| #include "talk/session/phone/videorenderer.h" |
| |
| void GetAllVideoTracks(webrtc::MediaStreamInterface* media_stream, |
| std::list<webrtc::VideoTrackInterface*>* video_tracks) { |
| webrtc::VideoTracks* track_list = media_stream->video_tracks(); |
| for (size_t i = 0; i < track_list->count(); ++i) { |
| webrtc::VideoTrackInterface* track = track_list->at(i); |
| video_tracks->push_back(track); |
| } |
| } |
| |
| class SignalingMessageReceiver { |
| public: |
| virtual void ReceiveMessage(const std::string& msg) = 0; |
| |
| virtual int num_rendered_frames() = 0; |
| |
| // Makes it possible for the remote side to decide when to start capturing. |
| // This makes it possible to wait with capturing until a renderer has been |
| // added. |
| virtual void StartCapturing() = 0; |
| |
| protected: |
| SignalingMessageReceiver() {} |
| virtual ~SignalingMessageReceiver() {} |
| }; |
| |
| class PeerConnectionP2PTestClient |
| : public webrtc::PeerConnectionObserver, |
| public SignalingMessageReceiver { |
| public: |
| static PeerConnectionP2PTestClient* CreateClient(int id) { |
| PeerConnectionP2PTestClient* client = new PeerConnectionP2PTestClient(id); |
| if (!client->Init()) { |
| delete client; |
| return NULL; |
| } |
| return client; |
| } |
| |
| ~PeerConnectionP2PTestClient() { |
| } |
| |
| void StartSession() { |
| if (video_track_.get() != NULL) { |
| // Tracks have already been set up. |
| return; |
| } |
| // TODO: the default audio device module is used regardless of |
| // the second parameter to the CreateLocalAudioTrack(..) call. Maybe remove |
| // the second parameter from the API altogether? |
| talk_base::scoped_refptr<webrtc::LocalAudioTrackInterface> audio_track( |
| peer_connection_factory_->CreateLocalAudioTrack("audio_track", NULL)); |
| |
| CreateLocalVideoTrack(); |
| |
| talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> stream = |
| peer_connection_factory_->CreateLocalMediaStream("stream_label"); |
| |
| stream->AddTrack(audio_track); |
| stream->AddTrack(video_track_); |
| |
| peer_connection_->AddStream(stream); |
| peer_connection_->CommitStreamChanges(); |
| } |
| |
| void StartCapturing() { |
| if (fake_video_capture_module_ != NULL) { |
| fake_video_capture_module_->StartCapturing(); |
| } |
| } |
| |
| bool SessionActive() { |
| return peer_connection_->ready_state() == |
| webrtc::PeerConnectionInterface::kActive; |
| } |
| |
| void StopSession() { |
| if (fake_video_capture_module_ != NULL) { |
| fake_video_capture_module_->StopCapturing(); |
| } |
| // TODO: investigate why calling Close() triggers a crash when |
| // deleting the PeerConnection. |
| // peer_connection_->Close(); |
| } |
| |
| void set_signaling_message_receiver( |
| SignalingMessageReceiver* signaling_message_receiver) { |
| signaling_message_receiver_ = signaling_message_receiver; |
| } |
| |
| bool FramesReceivedCheck(int number_of_frames) { |
| if (number_of_frames > signaling_message_receiver_->num_rendered_frames()) { |
| return false; |
| } |
| else { |
| EXPECT_LT(number_of_frames, fake_video_capture_module_->sent_frames()); |
| } |
| return true; |
| } |
| |
| // SignalingMessageReceiver callback. |
| virtual void ReceiveMessage(const std::string& msg) { |
| peer_connection_->ProcessSignalingMessage(msg); |
| } |
| |
| virtual int num_rendered_frames() { |
| if (fake_video_renderer_ == NULL) { |
| return -1; |
| } |
| return fake_video_renderer_->num_rendered_frames(); |
| } |
| |
| // PeerConnectionObserver callbacks. |
| virtual void OnError() {} |
| virtual void OnMessage(const std::string&) {} |
| virtual void OnSignalingMessage(const std::string& msg) { |
| if (signaling_message_receiver_ == NULL) { |
| // Remote party may be deleted. |
| return; |
| } |
| signaling_message_receiver_->ReceiveMessage(msg); |
| } |
| virtual void OnStateChange(StateType /*state_changed*/) {} |
| virtual void OnAddStream(webrtc::MediaStreamInterface* media_stream) { |
| std::list<webrtc::VideoTrackInterface*> video_tracks; |
| GetAllVideoTracks(media_stream, &video_tracks); |
| int track_id = 0; |
| // Currently only one video track is supported. |
| // TODO: enable multiple video tracks. |
| EXPECT_EQ(1u, video_tracks.size()); |
| for (std::list<webrtc::VideoTrackInterface*>::iterator iter = |
| video_tracks.begin(); |
| iter != video_tracks.end(); |
| ++iter) { |
| fake_video_renderer_ = new cricket::FakeVideoRenderer(); |
| video_renderer_wrapper_ = webrtc::CreateVideoRenderer( |
| fake_video_renderer_); |
| (*iter)->SetRenderer(video_renderer_wrapper_); |
| track_id++; |
| } |
| // The video renderer has been added. Tell the far end to start capturing |
| // frames. That way the number of captured frames should be equal to number |
| // of rendered frames. |
| if (signaling_message_receiver_ != NULL) { |
| signaling_message_receiver_->StartCapturing(); |
| return; |
| } |
| } |
| virtual void OnRemoveStream(webrtc::MediaStreamInterface* /*media_stream*/) { |
| } |
| |
| private: |
| explicit PeerConnectionP2PTestClient(int id) |
| : id_(id), |
| fake_video_capture_module_(NULL), |
| fake_video_renderer_(NULL), |
| signaling_message_receiver_(NULL) { |
| } |
| |
| bool Init() { |
| EXPECT_TRUE(peer_connection_.get() == NULL); |
| EXPECT_TRUE(peer_connection_factory_.get() == NULL); |
| peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(); |
| if (peer_connection_factory_.get() == NULL) { |
| return false; |
| } |
| |
| const std::string server_configuration = "STUN stun.l.google.com:19302"; |
| peer_connection_ = peer_connection_factory_->CreatePeerConnection( |
| server_configuration, this); |
| return peer_connection_.get() != NULL; |
| } |
| |
| void GenerateRecordingFileName(int track, std::string* file_name) { |
| if (file_name == NULL) { |
| return; |
| } |
| std::stringstream file_name_stream; |
| file_name_stream << "p2p_test_client_" << id_ << "_videotrack_" << track << |
| ".yuv"; |
| file_name->clear(); |
| *file_name = file_name_stream.str(); |
| } |
| |
| void CreateLocalVideoTrack() { |
| fake_video_capture_module_ = FakeVideoCaptureModule::Create( |
| talk_base::Thread::Current()); |
| // TODO: Use FakeVideoCapturer instead of FakeVideoCaptureModule. |
| video_track_ = peer_connection_factory_->CreateLocalVideoTrack( |
| "video_track", CreateVideoCapturer(fake_video_capture_module_)); |
| } |
| |
| int id_; |
| talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_; |
| talk_base::scoped_refptr<webrtc::PeerConnectionFactoryInterface> |
| peer_connection_factory_; |
| |
| // Owns and ensures that fake_video_capture_module_ is available as long as |
| // this class exists. It also ensures destruction of the memory associated |
| // with it when this class is deleted. |
| talk_base::scoped_refptr<webrtc::LocalVideoTrackInterface> video_track_; |
| // Needed to keep track of number of frames send. |
| FakeVideoCaptureModule* fake_video_capture_module_; |
| // Ensures that fake_video_renderer_ is available as long as this class |
| // exists. It also ensures destruction of the memory associated with it when |
| // this class is deleted. |
| talk_base::scoped_refptr<webrtc::VideoRendererWrapperInterface> |
| video_renderer_wrapper_; |
| // Needed to keep track of number of frames received. |
| cricket::FakeVideoRenderer* fake_video_renderer_; |
| |
| // For remote peer communication. |
| SignalingMessageReceiver* signaling_message_receiver_; |
| }; |
| |
| class P2PTestConductor : public testing::Test { |
| public: |
| virtual void SetUp() { |
| EXPECT_TRUE(Init()); |
| } |
| // Return true if session no longer is pending. I.e. if the session is active |
| // or failed. |
| bool ActivationNotPending() { |
| if (!IsInitialized()) { |
| return true; |
| } |
| return SessionActive(); |
| } |
| bool SessionActive() { |
| return initiating_client_->SessionActive() && |
| receiving_client_->SessionActive(); |
| } |
| // Return true if the number of frames provided have been received or it is |
| // known that that will never occur (e.g. no frames will be sent or |
| // captured). |
| bool FramesNotPending(int frames_to_receive) { |
| if (!IsInitialized()) { |
| return true; |
| } |
| return FramesReceivedCheck(frames_to_receive); |
| } |
| bool FramesReceivedCheck(int frames_received) { |
| return initiating_client_->FramesReceivedCheck(frames_received) && |
| receiving_client_->FramesReceivedCheck(frames_received); |
| } |
| ~P2PTestConductor() { |
| if (initiating_client_.get() != NULL) { |
| initiating_client_->set_signaling_message_receiver(NULL); |
| } |
| if (receiving_client_.get() != NULL) { |
| receiving_client_->set_signaling_message_receiver(NULL); |
| } |
| } |
| |
| bool StartSession() { |
| if (!IsInitialized()) { |
| return false; |
| } |
| initiating_client_->StartSession(); |
| receiving_client_->StartSession(); |
| return true; |
| } |
| |
| bool StopSession() { |
| if (!IsInitialized()) { |
| return false; |
| } |
| initiating_client_->StopSession(); |
| receiving_client_->StopSession(); |
| return true; |
| } |
| |
| private: |
| bool Init() { |
| initiating_client_.reset(PeerConnectionP2PTestClient::CreateClient(0)); |
| receiving_client_.reset(PeerConnectionP2PTestClient::CreateClient(1)); |
| if ((initiating_client_.get() == NULL) || |
| (receiving_client_.get() == NULL)) { |
| return false; |
| } |
| initiating_client_->set_signaling_message_receiver(receiving_client_.get()); |
| receiving_client_->set_signaling_message_receiver(initiating_client_.get()); |
| return true; |
| } |
| bool IsInitialized() const { |
| return (initiating_client_.get() != NULL) && |
| (receiving_client_.get() != NULL); |
| } |
| |
| talk_base::scoped_ptr<PeerConnectionP2PTestClient> initiating_client_; |
| talk_base::scoped_ptr<PeerConnectionP2PTestClient> receiving_client_; |
| }; |
| |
| // This test sets up a call between two parties. Both parties send static frames |
| // to each other. Once the test is finished the number of sent frames is |
| // compared to the number of recieved frames. |
| // TODO - This unittest is failing when SRTP is enabled in |
| // PeerConnection. This is due to race between the media and signaling. |
| // When media arrives before signal, VoiceEngine asserts when it fails |
| // to decode encrypted RTP packets. Once VoiceEngine is fixed, enable |
| // this test case. |
| TEST_F(P2PTestConductor, DISABLED_LocalP2PTest) { |
| EXPECT_TRUE(StartSession()); |
| const int kMaxWaitForActivationMs = 5000; |
| EXPECT_TRUE_WAIT(ActivationNotPending(), kMaxWaitForActivationMs); |
| EXPECT_TRUE(SessionActive()); |
| |
| const int kEndFrameCount = 10; |
| const int kMaxWaitForFramesMs = 5000; |
| EXPECT_TRUE_WAIT(FramesNotPending(kEndFrameCount), kMaxWaitForFramesMs); |
| EXPECT_TRUE(FramesReceivedCheck(kEndFrameCount)); |
| EXPECT_TRUE(StopSession()); |
| } |