blob: 6e61dc2168b618413eb621653b8b532797549d48 [file] [log] [blame]
/*
* 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());
video_track_ = peer_connection_factory_->CreateLocalVideoTrack(
"video_track", 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.
TEST_F(P2PTestConductor, 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());
}