aboutsummaryrefslogtreecommitdiff
path: root/talk
diff options
context:
space:
mode:
authormallinath@webrtc.org <mallinath@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2014-02-03 16:57:16 +0000
committermallinath@webrtc.org <mallinath@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2014-02-03 16:57:16 +0000
commit67ee6b9a6260fa80b83326c4b4fec8857c0e578c (patch)
treed3fa9d2520edad8f96494a9e0f9e247c9ab8a54f /talk
parent422fdbf5028ac2540599170675dcf1af38545c22 (diff)
downloadwebrtc-67ee6b9a6260fa80b83326c4b4fec8857c0e578c.tar.gz
Update talk to 60923971
Review URL: https://webrtc-codereview.appspot.com/7909004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5475 4adac7df-926f-26a2-2b94-8c16560cd09d
Diffstat (limited to 'talk')
-rw-r--r--talk/app/webrtc/jsepsessiondescription.cc16
-rw-r--r--talk/app/webrtc/jsepsessiondescription_unittest.cc22
-rw-r--r--talk/app/webrtc/mediastreamhandler.cc36
-rw-r--r--talk/app/webrtc/mediastreamhandler.h27
-rw-r--r--talk/app/webrtc/mediastreaminterface.h22
-rw-r--r--talk/app/webrtc/peerconnectioninterface_unittest.cc1
-rw-r--r--talk/app/webrtc/webrtcsession.cc2
-rw-r--r--talk/app/webrtc/webrtcsession_unittest.cc4
-rw-r--r--talk/base/asyncsocket.h2
-rw-r--r--talk/base/fileutils.h6
-rw-r--r--talk/base/linux.cc83
-rw-r--r--talk/base/linux.h5
-rw-r--r--talk/base/linux_unittest.cc8
-rw-r--r--talk/base/opensslidentity.cc9
-rw-r--r--talk/base/socket.h23
-rw-r--r--talk/base/testutils.h12
-rw-r--r--talk/media/base/audiorenderer.h25
-rw-r--r--talk/media/base/videoadapter.cc37
-rw-r--r--talk/media/base/videoadapter.h22
-rw-r--r--talk/media/base/videocapturer.cc17
-rw-r--r--talk/media/base/videocapturer.h9
-rw-r--r--talk/media/base/videoengine_unittest.h2
-rw-r--r--talk/media/base/yuvframegenerator.cc263
-rw-r--r--talk/media/base/yuvframegenerator.h78
-rw-r--r--talk/media/devices/devicemanager.cc57
-rw-r--r--talk/media/devices/devicemanager.h2
-rw-r--r--talk/media/devices/yuvframescapturer.cc173
-rw-r--r--talk/media/devices/yuvframescapturer.h71
-rw-r--r--talk/media/webrtc/fakewebrtcvideocapturemodule.h44
-rw-r--r--talk/media/webrtc/webrtcvideocapturer.cc6
-rw-r--r--talk/media/webrtc/webrtcvideoengine.cc69
-rw-r--r--talk/media/webrtc/webrtcvideoengine.h12
-rw-r--r--talk/media/webrtc/webrtcvideoengine_unittest.cc1
-rw-r--r--talk/media/webrtc/webrtcvoiceengine.cc216
-rw-r--r--talk/media/webrtc/webrtcvoiceengine.h7
-rw-r--r--talk/p2p/base/dtlstransportchannel.h3
-rw-r--r--talk/p2p/base/fakesession.h4
-rw-r--r--talk/p2p/base/p2ptransportchannel.cc20
-rw-r--r--talk/p2p/base/p2ptransportchannel.h1
-rw-r--r--talk/p2p/base/p2ptransportchannel_unittest.cc57
-rw-r--r--talk/p2p/base/port.cc48
-rw-r--r--talk/p2p/base/port.h19
-rw-r--r--talk/p2p/base/port_unittest.cc111
-rw-r--r--talk/p2p/base/rawtransportchannel.h3
-rw-r--r--talk/p2p/base/relayport.cc8
-rw-r--r--talk/p2p/base/stunport.cc6
-rw-r--r--talk/p2p/base/tcpport.cc8
-rw-r--r--talk/p2p/base/transport.cc30
-rw-r--r--talk/p2p/base/transportchannelimpl.h1
-rw-r--r--talk/p2p/base/turnport.cc18
-rw-r--r--talk/p2p/base/turnport_unittest.cc10
51 files changed, 1443 insertions, 293 deletions
diff --git a/talk/app/webrtc/jsepsessiondescription.cc b/talk/app/webrtc/jsepsessiondescription.cc
index 8ec145808a..d7b37b5d76 100644
--- a/talk/app/webrtc/jsepsessiondescription.cc
+++ b/talk/app/webrtc/jsepsessiondescription.cc
@@ -118,9 +118,6 @@ bool JsepSessionDescription::AddCandidate(
}
if (mediasection_index >= number_of_mediasections())
return false;
- if (candidate_collection_[mediasection_index].HasCandidate(candidate)) {
- return true; // Silently ignore this candidate if we already have it.
- }
const std::string content_name =
description_->contents()[mediasection_index].name;
const cricket::TransportInfo* transport_info =
@@ -137,10 +134,15 @@ bool JsepSessionDescription::AddCandidate(
updated_candidate.set_password(transport_info->description.ice_pwd);
}
- candidate_collection_[mediasection_index].add(
- new JsepIceCandidate(candidate->sdp_mid(),
- static_cast<int>(mediasection_index),
- updated_candidate));
+ scoped_ptr<JsepIceCandidate> updated_candidate_wrapper(
+ new JsepIceCandidate(candidate->sdp_mid(),
+ static_cast<int>(mediasection_index),
+ updated_candidate));
+ if (!candidate_collection_[mediasection_index].HasCandidate(
+ updated_candidate_wrapper.get()))
+ candidate_collection_[mediasection_index].add(
+ updated_candidate_wrapper.release());
+
return true;
}
diff --git a/talk/app/webrtc/jsepsessiondescription_unittest.cc b/talk/app/webrtc/jsepsessiondescription_unittest.cc
index 671df36d96..55eb3d5392 100644
--- a/talk/app/webrtc/jsepsessiondescription_unittest.cc
+++ b/talk/app/webrtc/jsepsessiondescription_unittest.cc
@@ -204,6 +204,28 @@ TEST_F(JsepSessionDescriptionTest, AddBadCandidate) {
EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate2));
}
+// Tests that repeatedly adding the same candidate, with or without credentials,
+// does not increase the number of candidates in the description.
+TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) {
+ JsepIceCandidate jsep_candidate("", 0, candidate_);
+ EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
+ EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
+
+ // Add the same candidate again. It should be ignored.
+ EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
+ EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
+
+ // Create a new candidate, identical except that the ufrag and pwd are now
+ // populated.
+ candidate_.set_username(kCandidateUfragVoice);
+ candidate_.set_password(kCandidatePwdVoice);
+ JsepIceCandidate jsep_candidate_with_credentials("", 0, candidate_);
+
+ // This should also be identified as redundant and ignored.
+ EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate_with_credentials));
+ EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
+}
+
// Test that we can serialize a JsepSessionDescription and deserialize it again.
TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) {
std::string sdp = Serialize(jsep_desc_.get());
diff --git a/talk/app/webrtc/mediastreamhandler.cc b/talk/app/webrtc/mediastreamhandler.cc
index b09af7892f..932d55ee7b 100644
--- a/talk/app/webrtc/mediastreamhandler.cc
+++ b/talk/app/webrtc/mediastreamhandler.cc
@@ -56,14 +56,38 @@ void TrackHandler::OnChanged() {
}
}
+LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(NULL) {}
+
+LocalAudioSinkAdapter::~LocalAudioSinkAdapter() {}
+
+void LocalAudioSinkAdapter::OnData(const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames) {
+ talk_base::CritScope lock(&lock_);
+ if (sink_) {
+ sink_->OnData(audio_data, bits_per_sample, sample_rate,
+ number_of_channels, number_of_frames);
+ }
+}
+
+void LocalAudioSinkAdapter::SetSink(cricket::AudioRenderer::Sink* sink) {
+ talk_base::CritScope lock(&lock_);
+ ASSERT(!sink || !sink_);
+ sink_ = sink;
+}
+
LocalAudioTrackHandler::LocalAudioTrackHandler(
AudioTrackInterface* track,
uint32 ssrc,
AudioProviderInterface* provider)
: TrackHandler(track, ssrc),
audio_track_(track),
- provider_(provider) {
+ provider_(provider),
+ sink_adapter_(new LocalAudioSinkAdapter()) {
OnEnabledChanged();
+ track->AddSink(sink_adapter_.get());
}
LocalAudioTrackHandler::~LocalAudioTrackHandler() {
@@ -74,6 +98,7 @@ void LocalAudioTrackHandler::OnStateChanged() {
}
void LocalAudioTrackHandler::Stop() {
+ audio_track_->RemoveSink(sink_adapter_.get());
cricket::AudioOptions options;
provider_->SetAudioSend(ssrc(), false, options, NULL);
}
@@ -84,8 +109,13 @@ void LocalAudioTrackHandler::OnEnabledChanged() {
options = static_cast<LocalAudioSource*>(
audio_track_->GetSource())->options();
}
- provider_->SetAudioSend(ssrc(), audio_track_->enabled(), options,
- audio_track_->GetRenderer());
+
+ // Use the renderer if the audio track has one, otherwise use the sink
+ // adapter owned by this class.
+ cricket::AudioRenderer* renderer = audio_track_->GetRenderer() ?
+ audio_track_->GetRenderer() : sink_adapter_.get();
+ ASSERT(renderer);
+ provider_->SetAudioSend(ssrc(), audio_track_->enabled(), options, renderer);
}
RemoteAudioTrackHandler::RemoteAudioTrackHandler(
diff --git a/talk/app/webrtc/mediastreamhandler.h b/talk/app/webrtc/mediastreamhandler.h
index 0cd34d615a..625de85019 100644
--- a/talk/app/webrtc/mediastreamhandler.h
+++ b/talk/app/webrtc/mediastreamhandler.h
@@ -40,6 +40,7 @@
#include "talk/app/webrtc/mediastreamprovider.h"
#include "talk/app/webrtc/peerconnectioninterface.h"
#include "talk/base/thread.h"
+#include "talk/media/base/audiorenderer.h"
namespace webrtc {
@@ -67,6 +68,28 @@ class TrackHandler : public ObserverInterface {
bool enabled_;
};
+// LocalAudioSinkAdapter receives data callback as a sink to the local
+// AudioTrack, and passes the data to the sink of AudioRenderer.
+class LocalAudioSinkAdapter : public AudioTrackSinkInterface,
+ public cricket::AudioRenderer {
+ public:
+ LocalAudioSinkAdapter();
+ virtual ~LocalAudioSinkAdapter();
+
+ private:
+ // AudioSinkInterface implementation.
+ virtual void OnData(const void* audio_data, int bits_per_sample,
+ int sample_rate, int number_of_channels,
+ int number_of_frames) OVERRIDE;
+
+ // cricket::AudioRenderer implementation.
+ virtual void SetSink(cricket::AudioRenderer::Sink* sink) OVERRIDE;
+
+ cricket::AudioRenderer::Sink* sink_;
+ // Critical section protecting |sink_|.
+ talk_base::CriticalSection lock_;
+};
+
// LocalAudioTrackHandler listen to events on a local AudioTrack instance
// connected to a PeerConnection and orders the |provider| to executes the
// requested change.
@@ -86,6 +109,10 @@ class LocalAudioTrackHandler : public TrackHandler {
private:
AudioTrackInterface* audio_track_;
AudioProviderInterface* provider_;
+
+ // Used to pass the data callback from the |audio_track_| to the other
+ // end of cricket::AudioRenderer.
+ talk_base::scoped_ptr<LocalAudioSinkAdapter> sink_adapter_;
};
// RemoteAudioTrackHandler listen to events on a remote AudioTrack instance
diff --git a/talk/app/webrtc/mediastreaminterface.h b/talk/app/webrtc/mediastreaminterface.h
index b2c4468fb9..96d09428ab 100644
--- a/talk/app/webrtc/mediastreaminterface.h
+++ b/talk/app/webrtc/mediastreaminterface.h
@@ -147,15 +147,33 @@ class VideoTrackInterface : public MediaStreamTrackInterface {
class AudioSourceInterface : public MediaSourceInterface {
};
+// Interface for receiving audio data from a AudioTrack.
+class AudioTrackSinkInterface {
+ public:
+ virtual void OnData(const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames) = 0;
+ protected:
+ virtual ~AudioTrackSinkInterface() {}
+};
+
class AudioTrackInterface : public MediaStreamTrackInterface {
public:
// TODO(xians): Figure out if the following interface should be const or not.
virtual AudioSourceInterface* GetSource() const = 0;
+ // Adds/Removes a sink that will receive the audio data from the track.
+ // TODO(xians): Make them pure virtual after Chrome implements these
+ // interfaces.
+ virtual void AddSink(AudioTrackSinkInterface* sink) {}
+ virtual void RemoveSink(AudioTrackSinkInterface* sink) {}
+
// Gets a pointer to the audio renderer of this AudioTrack.
// The pointer is valid for the lifetime of this AudioTrack.
- // TODO(xians): Make the following interface pure virtual once Chrome has its
- // implementation.
+ // TODO(xians): Remove the following interface after Chrome switches to
+ // AddSink() and RemoveSink() interfaces.
virtual cricket::AudioRenderer* GetRenderer() { return NULL; }
protected:
diff --git a/talk/app/webrtc/peerconnectioninterface_unittest.cc b/talk/app/webrtc/peerconnectioninterface_unittest.cc
index f9d5e2eee8..98e0645839 100644
--- a/talk/app/webrtc/peerconnectioninterface_unittest.cc
+++ b/talk/app/webrtc/peerconnectioninterface_unittest.cc
@@ -180,7 +180,6 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
EXPECT_EQ(pc_->ice_gathering_state(), new_state);
}
virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
-
EXPECT_NE(PeerConnectionInterface::kIceGatheringNew,
pc_->ice_gathering_state());
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 0de46e71bf..59d72709f3 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -786,7 +786,7 @@ bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
return false;
}
- return UseCandidatesInSessionDescription(remote_desc_.get());
+ return UseCandidate(candidate);
}
bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index 6d3a58131e..ba58936e39 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -1421,10 +1421,12 @@ TEST_F(WebRtcSessionTest, TestAddRemoteCandidate) {
EXPECT_EQ(1, candidates->at(0)->candidate().component());
EXPECT_EQ(2, candidates->at(1)->candidate().component());
+ // |ice_candidate3| is identical to |ice_candidate2|. It can be added
+ // successfully, but the total count of candidates will not increase.
candidate.set_component(2);
JsepIceCandidate ice_candidate3(kMediaContentName0, 0, candidate);
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate3));
- ASSERT_EQ(3u, candidates->count());
+ ASSERT_EQ(2u, candidates->count());
JsepIceCandidate bad_ice_candidate("bad content name", 99, candidate);
EXPECT_FALSE(session_->ProcessIceMessage(&bad_ice_candidate));
diff --git a/talk/base/asyncsocket.h b/talk/base/asyncsocket.h
index 2854558adc..97859a7527 100644
--- a/talk/base/asyncsocket.h
+++ b/talk/base/asyncsocket.h
@@ -27,7 +27,6 @@
#ifndef TALK_BASE_ASYNCSOCKET_H_
#define TALK_BASE_ASYNCSOCKET_H_
-#ifndef __native_client__
#include "talk/base/common.h"
#include "talk/base/sigslot.h"
@@ -139,5 +138,4 @@ class AsyncSocketAdapter : public AsyncSocket, public sigslot::has_slots<> {
} // namespace talk_base
-#endif // __native_client__
#endif // TALK_BASE_ASYNCSOCKET_H_
diff --git a/talk/base/fileutils.h b/talk/base/fileutils.h
index fba0d000b0..e58f76c7e9 100644
--- a/talk/base/fileutils.h
+++ b/talk/base/fileutils.h
@@ -33,9 +33,10 @@
#ifdef WIN32
#include "talk/base/win32.h"
#else
-#include <sys/types.h>
#include <dirent.h>
+#include <stdio.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#endif
@@ -463,7 +464,10 @@ const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
#elif defined(POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
+#else
+#error Unsupported platform
#endif
+
FILE* FdopenPlatformFileForWriting(PlatformFile file);
bool ClosePlatformFile(PlatformFile file);
diff --git a/talk/base/linux.cc b/talk/base/linux.cc
index 644ec45065..16666f8976 100644
--- a/talk/base/linux.cc
+++ b/talk/base/linux.cc
@@ -250,6 +250,89 @@ bool ConfigParser::ParseLine(std::string* key, std::string* value) {
return true;
}
+#if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD)
+static bool ExpectLineFromStream(FileStream* stream,
+ std::string* out) {
+ StreamResult res = stream->ReadLine(out);
+ if (res != SR_SUCCESS) {
+ if (res != SR_EOS) {
+ LOG(LS_ERROR) << "Error when reading from stream";
+ } else {
+ LOG(LS_ERROR) << "Incorrect number of lines in stream";
+ }
+ return false;
+ }
+ return true;
+}
+
+static void ExpectEofFromStream(FileStream* stream) {
+ std::string unused;
+ StreamResult res = stream->ReadLine(&unused);
+ if (res == SR_SUCCESS) {
+ LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream";
+ } else if (res != SR_EOS) {
+ LOG(LS_WARNING) << "Error when checking for extra lines from stream";
+ }
+}
+
+// For caching the lsb_release output (reading it invokes a sub-process and
+// hence is somewhat expensive).
+static std::string lsb_release_string;
+static CriticalSection lsb_release_string_critsec;
+
+std::string ReadLinuxLsbRelease() {
+ CritScope cs(&lsb_release_string_critsec);
+ if (!lsb_release_string.empty()) {
+ // Have cached result from previous call.
+ return lsb_release_string;
+ }
+ // No cached result. Run lsb_release and parse output.
+ POpenStream lsb_release_output;
+ if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) {
+ LOG_ERR(LS_ERROR) << "Can't run lsb_release";
+ return lsb_release_string; // empty
+ }
+ // Read in the command's output and build the string.
+ std::ostringstream sstr;
+ std::string line;
+ int wait_status;
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << "DISTRIB_ID=" << line;
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << " DISTRIB_RELEASE=" << line;
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << " DISTRIB_CODENAME=" << line;
+
+ // Should not be anything left.
+ ExpectEofFromStream(&lsb_release_output);
+
+ lsb_release_output.Close();
+ wait_status = lsb_release_output.GetWaitStatus();
+ if (wait_status == -1 ||
+ !WIFEXITED(wait_status) ||
+ WEXITSTATUS(wait_status) != 0) {
+ LOG(LS_WARNING) << "Unexpected exit status from lsb_release";
+ }
+
+ lsb_release_string = sstr.str();
+
+ return lsb_release_string;
+}
+#endif
std::string ReadLinuxUname() {
struct utsname buf;
diff --git a/talk/base/linux.h b/talk/base/linux.h
index 63e3021814..46fa5ed6b2 100644
--- a/talk/base/linux.h
+++ b/talk/base/linux.h
@@ -121,6 +121,11 @@ class ProcCpuInfo {
ConfigParser::MapVector sections_;
};
+#if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD)
+// Builds a string containing the info from lsb_release on a single line.
+std::string ReadLinuxLsbRelease();
+#endif
+
// Returns the output of "uname".
std::string ReadLinuxUname();
diff --git a/talk/base/linux_unittest.cc b/talk/base/linux_unittest.cc
index efc7f87c1e..f6815141d2 100644
--- a/talk/base/linux_unittest.cc
+++ b/talk/base/linux_unittest.cc
@@ -105,6 +105,14 @@ TEST(ConfigParser, ParseConfig) {
EXPECT_EQ(true, parser.Parse(&key_val_pairs));
}
+#if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD)
+TEST(ReadLinuxLsbRelease, ReturnsSomething) {
+ std::string str = ReadLinuxLsbRelease();
+ // ChromeOS don't have lsb_release
+ // EXPECT_FALSE(str.empty());
+}
+#endif
+
TEST(ReadLinuxUname, ReturnsSomething) {
std::string str = ReadLinuxUname();
EXPECT_FALSE(str.empty());
diff --git a/talk/base/opensslidentity.cc b/talk/base/opensslidentity.cc
index eef0665850..33b02ddfbe 100644
--- a/talk/base/opensslidentity.cc
+++ b/talk/base/opensslidentity.cc
@@ -224,11 +224,11 @@ OpenSSLCertificate* OpenSSLCertificate::FromPEMString(
BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
if (!bio)
return NULL;
- (void)BIO_set_close(bio, BIO_NOCLOSE);
BIO_set_mem_eof_return(bio, 0);
X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL,
const_cast<char*>("\0"));
- BIO_free(bio);
+ BIO_free(bio); // Frees the BIO, but not the pointed-to string.
+
if (!x509)
return NULL;
@@ -364,11 +364,10 @@ SSLIdentity* OpenSSLIdentity::FromPEMStrings(
LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
return NULL;
}
- (void)BIO_set_close(bio, BIO_NOCLOSE);
BIO_set_mem_eof_return(bio, 0);
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL,
const_cast<char*>("\0"));
- BIO_free(bio);
+ BIO_free(bio); // Frees the BIO, but not the pointed-to string.
if (!pkey) {
LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
@@ -392,5 +391,3 @@ bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
} // namespace talk_base
#endif // HAVE_OPENSSL_SSL_H
-
-
diff --git a/talk/base/socket.h b/talk/base/socket.h
index 56e3ebceea..47f55225de 100644
--- a/talk/base/socket.h
+++ b/talk/base/socket.h
@@ -2,40 +2,32 @@
* libjingle
* Copyright 2004--2005, Google Inc.
*
- * Redistribution and use in source and binary forms, with or without
+ * 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,
+ * 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
+ * 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
+ * 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,
+ * 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
+ * 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.
*/
#ifndef TALK_BASE_SOCKET_H__
#define TALK_BASE_SOCKET_H__
-#if defined(__native_client__)
-namespace talk_base {
-// These should never be defined or instantiated.
-class Socket;
-class AsyncSocket;
-} // namespace talk_base
-#else
-
#include <errno.h>
#ifdef POSIX
@@ -207,5 +199,4 @@ class Socket {
} // namespace talk_base
-#endif // !__native_client__
#endif // TALK_BASE_SOCKET_H__
diff --git a/talk/base/testutils.h b/talk/base/testutils.h
index e8ad720094..363fb4cffd 100644
--- a/talk/base/testutils.h
+++ b/talk/base/testutils.h
@@ -32,6 +32,8 @@
#ifdef LINUX
#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+
// X defines a few macros that stomp on types that gunit.h uses.
#undef None
#undef Bool
@@ -601,6 +603,16 @@ inline bool IsScreencastingAvailable() {
LOG(LS_WARNING) << "No X Display available.";
return false;
}
+ int ignored_int, major_version, minor_version;
+ if (!XRRQueryExtension(display, &ignored_int, &ignored_int) ||
+ !XRRQueryVersion(display, &major_version, &minor_version) ||
+ major_version < 1 ||
+ (major_version < 2 && minor_version < 3)) {
+ LOG(LS_WARNING) << "XRandr version: " << major_version << "."
+ << minor_version;
+ LOG(LS_WARNING) << "XRandr is not supported or is too old (pre 1.3).";
+ return false;
+ }
#endif
return true;
}
diff --git a/talk/media/base/audiorenderer.h b/talk/media/base/audiorenderer.h
index 273312fab3..853813db0d 100644
--- a/talk/media/base/audiorenderer.h
+++ b/talk/media/base/audiorenderer.h
@@ -30,18 +30,37 @@
namespace cricket {
-// Abstract interface for holding the voice channel IDs.
+// Abstract interface for rendering the audio data.
class AudioRenderer {
public:
+ class Sink {
+ public:
+ virtual void OnData(const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames) = 0;
+ protected:
+ virtual ~Sink() {}
+ };
+
+ // Sets a sink to the AudioRenderer. There can be only one sink connected
+ // to the renderer at a time.
+ virtual void SetSink(Sink* sink) {}
+
// Add the WebRtc VoE channel to the renderer.
// For local stream, multiple WebRtc VoE channels can be connected to the
// renderer. While for remote stream, only one WebRtc VoE channel can be
// connected to the renderer.
- virtual void AddChannel(int channel_id) = 0;
+ // TODO(xians): Remove this interface after Chrome switches to the
+ // AudioRenderer::Sink interface.
+ virtual void AddChannel(int channel_id) {}
// Remove the WebRtc VoE channel from the renderer.
// This method is called when the VoE channel is going away.
- virtual void RemoveChannel(int channel_id) = 0;
+ // TODO(xians): Remove this interface after Chrome switches to the
+ // AudioRenderer::Sink interface.
+ virtual void RemoveChannel(int channel_id) {}
protected:
virtual ~AudioRenderer() {}
diff --git a/talk/media/base/videoadapter.cc b/talk/media/base/videoadapter.cc
index 588f1950e1..3cd6cac96c 100644
--- a/talk/media/base/videoadapter.cc
+++ b/talk/media/base/videoadapter.cc
@@ -36,9 +36,9 @@ namespace cricket {
// TODO(fbarchard): Make downgrades settable
static const int kMaxCpuDowngrades = 2; // Downgrade at most 2 times for CPU.
-// The number of milliseconds of data to require before acting on cpu sampling
-// information.
-static const size_t kCpuLoadMinSampleTime = 5000;
+// The number of cpu samples to require before adapting. This value depends on
+// the cpu monitor sampling frequency being 2000ms.
+static const int kCpuLoadMinSamples = 3;
// The amount of weight to give to each new cpu load sample. The lower the
// value, the slower we'll adapt to changing cpu conditions.
static const float kCpuLoadWeightCoefficient = 0.4f;
@@ -165,8 +165,8 @@ VideoAdapter::VideoAdapter()
frames_(0),
adapted_frames_(0),
adaption_changes_(0),
- previous_width(0),
- previous_height(0),
+ previous_width_(0),
+ previous_height_(0),
black_output_(false),
is_black_(false),
interval_next_frame_(0) {
@@ -240,7 +240,7 @@ int VideoAdapter::GetOutputNumPixels() const {
// TODO(fbarchard): Add AdaptFrameRate function that only drops frames but
// not resolution.
bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame,
- const VideoFrame** out_frame) {
+ VideoFrame** out_frame) {
talk_base::CritScope cs(&critical_section_);
if (!in_frame || !out_frame) {
return false;
@@ -306,8 +306,8 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame,
// resolution changes as well. Consider dropping the statistics into their
// own class which could be queried publically.
bool changed = false;
- if (previous_width && (previous_width != (*out_frame)->GetWidth() ||
- previous_height != (*out_frame)->GetHeight())) {
+ if (previous_width_ && (previous_width_ != (*out_frame)->GetWidth() ||
+ previous_height_ != (*out_frame)->GetHeight())) {
show = true;
++adaption_changes_;
changed = true;
@@ -325,8 +325,8 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame,
<< "x" << (*out_frame)->GetHeight()
<< " Changed: " << (changed ? "true" : "false");
}
- previous_width = (*out_frame)->GetWidth();
- previous_height = (*out_frame)->GetHeight();
+ previous_width_ = (*out_frame)->GetWidth();
+ previous_height_ = (*out_frame)->GetHeight();
return true;
}
@@ -382,7 +382,8 @@ CoordinatedVideoAdapter::CoordinatedVideoAdapter()
view_adaptation_(true),
view_switch_(false),
cpu_downgrade_count_(0),
- cpu_adapt_wait_time_(0),
+ cpu_load_min_samples_(kCpuLoadMinSamples),
+ cpu_load_num_samples_(0),
high_system_threshold_(kHighSystemCpuThreshold),
low_system_threshold_(kLowSystemCpuThreshold),
process_threshold_(kProcessCpuThreshold),
@@ -552,22 +553,18 @@ void CoordinatedVideoAdapter::OnCpuLoadUpdated(
// we'll still calculate this information, in case smoothing is later enabled.
system_load_average_ = kCpuLoadWeightCoefficient * system_load +
(1.0f - kCpuLoadWeightCoefficient) * system_load_average_;
+ ++cpu_load_num_samples_;
if (cpu_smoothing_) {
system_load = system_load_average_;
}
- // If we haven't started taking samples yet, wait until we have at least
- // the correct number of samples per the wait time.
- if (cpu_adapt_wait_time_ == 0) {
- cpu_adapt_wait_time_ = talk_base::TimeAfter(kCpuLoadMinSampleTime);
- }
AdaptRequest request = FindCpuRequest(current_cpus, max_cpus,
process_load, system_load);
// Make sure we're not adapting too quickly.
if (request != KEEP) {
- if (talk_base::TimeIsLater(talk_base::Time(),
- cpu_adapt_wait_time_)) {
+ if (cpu_load_num_samples_ < cpu_load_min_samples_) {
LOG(LS_VERBOSE) << "VAdapt CPU load high/low but do not adapt until "
- << talk_base::TimeUntil(cpu_adapt_wait_time_) << " ms";
+ << (cpu_load_min_samples_ - cpu_load_num_samples_)
+ << " more samples";
request = KEEP;
}
}
@@ -688,7 +685,7 @@ bool CoordinatedVideoAdapter::AdaptToMinimumFormat(int* new_width,
if (changed) {
// When any adaptation occurs, historic CPU load levels are no longer
// accurate. Clear out our state so we can re-learn at the new normal.
- cpu_adapt_wait_time_ = talk_base::TimeAfter(kCpuLoadMinSampleTime);
+ cpu_load_num_samples_ = 0;
system_load_average_ = kCpuLoadInitialAverage;
}
diff --git a/talk/media/base/videoadapter.h b/talk/media/base/videoadapter.h
index 38a8c9d63a..272df72de6 100644
--- a/talk/media/base/videoadapter.h
+++ b/talk/media/base/videoadapter.h
@@ -62,7 +62,7 @@ class VideoAdapter {
// successfully. Return false otherwise.
// output_frame_ is owned by the VideoAdapter that has the best knowledge on
// the output frame.
- bool AdaptFrame(const VideoFrame* in_frame, const VideoFrame** out_frame);
+ bool AdaptFrame(const VideoFrame* in_frame, VideoFrame** out_frame);
void set_scale_third(bool enable) {
LOG(LS_INFO) << "Video Adapter third scaling is now "
@@ -90,8 +90,8 @@ class VideoAdapter {
int frames_; // Number of input frames.
int adapted_frames_; // Number of frames scaled.
int adaption_changes_; // Number of changes in scale factor.
- size_t previous_width; // Previous adapter output width.
- size_t previous_height; // Previous adapter output height.
+ size_t previous_width_; // Previous adapter output width.
+ size_t previous_height_; // Previous adapter output height.
bool black_output_; // Flag to tell if we need to black output_frame_.
bool is_black_; // Flag to tell if output_frame_ is currently black.
int64 interval_next_frame_;
@@ -149,14 +149,15 @@ class CoordinatedVideoAdapter
// When the video is decreased, set the waiting time for CPU adaptation to
// decrease video again.
- void set_cpu_adapt_wait_time(uint32 cpu_adapt_wait_time) {
- if (cpu_adapt_wait_time_ != static_cast<int>(cpu_adapt_wait_time)) {
- LOG(LS_INFO) << "VAdapt Change Cpu Adapt Wait Time from: "
- << cpu_adapt_wait_time_ << " to "
- << cpu_adapt_wait_time;
- cpu_adapt_wait_time_ = static_cast<int>(cpu_adapt_wait_time);
+ void set_cpu_load_min_samples(int cpu_load_min_samples) {
+ if (cpu_load_min_samples_ != cpu_load_min_samples) {
+ LOG(LS_INFO) << "VAdapt Change Cpu Adapt Min Samples from: "
+ << cpu_load_min_samples_ << " to "
+ << cpu_load_min_samples;
+ cpu_load_min_samples_ = cpu_load_min_samples;
}
}
+ int cpu_load_min_samples() const { return cpu_load_min_samples_; }
// CPU system load high threshold for reducing resolution. e.g. 0.85f
void set_high_system_threshold(float high_system_threshold) {
ASSERT(high_system_threshold <= 1.0f);
@@ -220,7 +221,8 @@ class CoordinatedVideoAdapter
bool view_adaptation_; // True if view adaptation is enabled.
bool view_switch_; // True if view switch is enabled.
int cpu_downgrade_count_;
- int cpu_adapt_wait_time_;
+ int cpu_load_min_samples_;
+ int cpu_load_num_samples_;
// cpu system load thresholds relative to max cpus.
float high_system_threshold_;
float low_system_threshold_;
diff --git a/talk/media/base/videocapturer.cc b/talk/media/base/videocapturer.cc
index 355cc64dd8..b2f41dcfb4 100644
--- a/talk/media/base/videocapturer.cc
+++ b/talk/media/base/videocapturer.cc
@@ -475,14 +475,25 @@ void VideoCapturer::OnFrameCaptured(VideoCapturer*,
<< desired_width << " x " << desired_height;
return;
}
- if (!muted_ && !ApplyProcessors(&i420_frame)) {
+
+ VideoFrame* adapted_frame = &i420_frame;
+ if (!SignalAdaptFrame.is_empty() && !IsScreencast()) {
+ VideoFrame* out_frame = NULL;
+ SignalAdaptFrame(this, adapted_frame, &out_frame);
+ if (!out_frame) {
+ return; // VideoAdapter dropped the frame.
+ }
+ adapted_frame = out_frame;
+ }
+
+ if (!muted_ && !ApplyProcessors(adapted_frame)) {
// Processor dropped the frame.
return;
}
if (muted_) {
- i420_frame.SetToBlack();
+ adapted_frame->SetToBlack();
}
- SignalVideoFrame(this, &i420_frame);
+ SignalVideoFrame(this, adapted_frame);
#endif // VIDEO_FRAME_NAME
}
diff --git a/talk/media/base/videocapturer.h b/talk/media/base/videocapturer.h
index 933fc82500..15c016fd11 100644
--- a/talk/media/base/videocapturer.h
+++ b/talk/media/base/videocapturer.h
@@ -255,7 +255,14 @@ class VideoCapturer
// Signal the captured frame to downstream.
sigslot::signal2<VideoCapturer*, const CapturedFrame*,
sigslot::multi_threaded_local> SignalFrameCaptured;
- // Signal the captured frame converted to I420 to downstream.
+ // A VideoAdapter should be hooked up to SignalAdaptFrame which will be
+ // called before forwarding the frame to SignalVideoFrame. The parameters
+ // are this capturer instance, the input video frame and output frame
+ // pointer, respectively.
+ sigslot::signal3<VideoCapturer*, const VideoFrame*, VideoFrame**,
+ sigslot::multi_threaded_local> SignalAdaptFrame;
+ // Signal the captured and possibly adapted frame to downstream consumers
+ // such as the encoder.
sigslot::signal2<VideoCapturer*, const VideoFrame*,
sigslot::multi_threaded_local> SignalVideoFrame;
diff --git a/talk/media/base/videoengine_unittest.h b/talk/media/base/videoengine_unittest.h
index 893f0f3488..b667cb0f0a 100644
--- a/talk/media/base/videoengine_unittest.h
+++ b/talk/media/base/videoengine_unittest.h
@@ -1184,6 +1184,8 @@ class VideoMediaChannelTest : public testing::Test,
// some (e.g. 1) of these 3 frames after the renderer is set again.
EXPECT_GT_FRAME_ON_RENDERER_WAIT(
renderer1, 2, DefaultCodec().width, DefaultCodec().height, kTimeout);
+ // Detach |renderer1| before exit as there might be frames come late.
+ EXPECT_TRUE(channel_->SetRenderer(kSsrc, NULL));
}
// Tests the behavior of incoming streams in a conference scenario.
diff --git a/talk/media/base/yuvframegenerator.cc b/talk/media/base/yuvframegenerator.cc
new file mode 100644
index 0000000000..52ee19fc3c
--- /dev/null
+++ b/talk/media/base/yuvframegenerator.cc
@@ -0,0 +1,263 @@
+#include "talk/media/base/yuvframegenerator.h"
+
+#include <string.h>
+#include <sstream>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+
+namespace cricket {
+
+// These values were figured out by trial and error. If you change any
+// basic parameters e.g. unit-bar size or bars-x-offset, you may need to change
+// background-width/background-height.
+const int kBarcodeBackgroundWidth = 160;
+const int kBarcodeBackgroundHeight = 100;
+const int kBarsXOffset = 12;
+const int kBarsYOffset = 4;
+const int kUnitBarSize = 2;
+const int kBarcodeNormalBarHeight = 80;
+const int kBarcodeGuardBarHeight = 96;
+const int kBarcodeMaxEncodableDigits = 7;
+const int kBarcodeMaxEncodableValue = 9999999;
+
+YuvFrameGenerator::YuvFrameGenerator(int width, int height,
+ bool enable_barcode) {
+ width_ = width;
+ height_ = height;
+ frame_index_ = 0;
+ int size = width_ * height_;
+ int qsize = size / 4;
+ frame_data_size_ = size + 2 * qsize;
+ y_data_ = new uint8[size];
+ u_data_ = new uint8[qsize];
+ v_data_ = new uint8[qsize];
+ if (enable_barcode) {
+ ASSERT(width_ >= kBarcodeBackgroundWidth);
+ ASSERT(height_>= kBarcodeBackgroundHeight);
+ barcode_start_x_ = 0;
+ barcode_start_y_ = height_ - kBarcodeBackgroundHeight;
+ } else {
+ barcode_start_x_ = -1;
+ barcode_start_y_ = -1;
+ }
+}
+
+YuvFrameGenerator::~YuvFrameGenerator() {
+ delete y_data_;
+ delete u_data_;
+ delete v_data_;
+}
+
+void YuvFrameGenerator::GenerateNextFrame(uint8* frame_buffer,
+ int32 barcode_value) {
+ int size = width_ * height_;
+ int qsize = size / 4;
+ memset(y_data_, 0, size);
+ memset(u_data_, 0, qsize);
+ memset(v_data_, 0, qsize);
+
+ DrawLandscape(y_data_, width_, height_);
+ DrawGradientX(u_data_, width_/2, height_/2);
+ DrawGradientY(v_data_, width_/2, height_/2);
+ DrawMovingLineX(u_data_, width_/2, height_/2, frame_index_);
+ DrawMovingLineY(v_data_, width_/2, height_/2, frame_index_);
+ DrawBouncingCube(y_data_, width_, height_, frame_index_);
+
+ if (barcode_value >= 0) {
+ ASSERT(barcode_start_x_ != -1);
+ DrawBarcode(barcode_value);
+ }
+
+ memcpy(frame_buffer, y_data_, size);
+ frame_buffer += size;
+ memcpy(frame_buffer, u_data_, qsize);
+ frame_buffer += qsize;
+ memcpy(frame_buffer, v_data_, qsize);
+
+ frame_index_ = (frame_index_ + 1) & 0x0000FFFF;
+}
+
+void YuvFrameGenerator::DrawLandscape(uint8 *p, int w, int h) {
+ int x, y;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ p[x + y * w] = x % (y+1);
+ if (((x > w / 2 - (w / 32)) && (x < w / 2 + (w / 32))) ||
+ ((y > h / 2 - (h / 32)) && (y < h / 2 + (h / 32)))) {
+ p[x + y * w] = (((x + y) / 8 % 2)) ? 255 : 0;
+ }
+ }
+ }
+}
+
+void YuvFrameGenerator::DrawGradientX(uint8 *p, int w, int h) {
+ int x, y;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ p[x + y * w] = (x << 8) / w;
+ }
+ }
+}
+
+void YuvFrameGenerator::DrawGradientY(uint8 *p, int w, int h) {
+ int x, y;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ p[x + y * w] = (y << 8) / h;
+ }
+ }
+}
+
+void YuvFrameGenerator::DrawMovingLineX(uint8 *p, int w, int h, int n) {
+ int x, y;
+ x = n % (w * 2);
+ if (x >= w) x = w + w - x - 1;
+ for (y = 0; y < h; y++) {
+ p[x + y * w] = 255;
+ }
+}
+
+void YuvFrameGenerator::DrawMovingLineY(uint8 *p, int w, int h, int n) {
+ int x, y;
+ y = n % (h * 2);
+ if (y >= h) y = h + h - y - 1;
+ for (x = 0; x < w; x++) {
+ p[x + y * w] = 255;
+ }
+}
+
+void YuvFrameGenerator::DrawBouncingCube(uint8 *p, int w, int h, int n) {
+ int x, y, pw, ph, px, py;
+ pw = w / 16;
+ ph = h / 16;
+ px = n % (w * 2);
+ py = n % (h * 2);
+ if (px >= w) px = w + w - px - 1;
+ if (py >= h) py = h + h - py - 1;
+ for (y = py - ph; y < py + ph; y++) {
+ if (y >=0 && y < h) {
+ for (x = px - pw; x < px + pw; x++) {
+ if (x >= 0 && x < w) {
+ p[x + y * w] = 255;
+ }
+ }
+ }
+ }
+}
+
+void YuvFrameGenerator::GetBarcodeBounds(int* top, int* left,
+ int* width, int* height) {
+ ASSERT(barcode_start_x_ != -1);
+ *top = barcode_start_y_;
+ *left = barcode_start_x_;
+ *width = kBarcodeBackgroundWidth;
+ *height = kBarcodeBackgroundHeight;
+}
+
+static void ComputeBarcodeDigits(uint32 value, std::stringstream* result) {
+ // Serialize |value| as 7-char string, padded with 0's to the left.
+ result->width(kBarcodeMaxEncodableDigits);
+ result->fill('0');
+ *result << value;
+
+ // Compute check-digit and append to result. Steps described here:
+ // http://en.wikipedia.org/wiki/European_Article_Number#Calculation_of_checksum_digit
+ int sum = 0;
+ for (int pos = 1; pos <= kBarcodeMaxEncodableDigits; pos++) {
+ char next_char;
+ result->get(next_char);
+ uint8 digit = next_char - '0';
+ sum += digit * (pos % 2 ? 3 : 1);
+ }
+ uint8 check_digit = sum % 10;
+ if (check_digit != 0) {
+ check_digit = 10 - check_digit;
+ }
+
+ *result << static_cast<int>(check_digit);
+ result->seekg(0);
+}
+
+void YuvFrameGenerator::DrawBarcode(uint32 value) {
+ std::stringstream value_str_stream;
+ ComputeBarcodeDigits(value, &value_str_stream);
+
+ // Draw white filled rectangle as background to barcode.
+ DrawBlockRectangle(y_data_, barcode_start_x_, barcode_start_y_,
+ kBarcodeBackgroundWidth, kBarcodeBackgroundHeight,
+ width_, 255);
+ DrawBlockRectangle(u_data_, barcode_start_x_ / 2, barcode_start_y_ / 2,
+ kBarcodeBackgroundWidth / 2, kBarcodeBackgroundHeight / 2,
+ width_ / 2, 128);
+ DrawBlockRectangle(v_data_, barcode_start_x_ / 2, barcode_start_y_ / 2,
+ kBarcodeBackgroundWidth / 2, kBarcodeBackgroundHeight / 2,
+ width_ / 2, 128);
+
+ // Scan through chars (digits) and draw black bars.
+ int x = barcode_start_x_ + kBarsXOffset;
+ int y = barcode_start_y_ + kBarsYOffset;
+ int pos = 0;
+ x = DrawSideGuardBars(x, y, kBarcodeGuardBarHeight);
+ while (true) {
+ char next_char;
+ value_str_stream.get(next_char);
+ if (!value_str_stream.good()) {
+ break;
+ }
+ if (pos++ == 4) {
+ x = DrawMiddleGuardBars(x, y, kBarcodeGuardBarHeight);
+ }
+ uint8 digit = next_char - '0';
+ x = DrawEanEncodedDigit(digit, x, y, kBarcodeNormalBarHeight, pos > 4);
+ }
+ x = DrawSideGuardBars(x, y, kBarcodeGuardBarHeight);
+}
+
+int YuvFrameGenerator::DrawMiddleGuardBars(int x, int y, int height) {
+ x += kUnitBarSize;
+ DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
+ x += (kUnitBarSize * 2);
+ DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
+ return x + (kUnitBarSize * 2);
+}
+
+int YuvFrameGenerator::DrawSideGuardBars(int x, int y, int height) {
+ DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
+ x += (kUnitBarSize * 2);
+ DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
+ return x + kUnitBarSize;
+}
+
+// For each digit: 0-9, |kEanEncodings| contains a bit-mask indicating
+// which bars are black (1) and which are blank (0). These are for the L-code
+// only. R-code values are bitwise negation of these. Reference:
+// http://en.wikipedia.org/wiki/European_Article_Number#Binary_encoding_of_data_digits_into_EAN-13_barcode // NOLINT
+const uint8 kEanEncodings[] = { 13, 25, 19, 61, 35, 49, 47, 59, 55, 11 };
+
+int YuvFrameGenerator::DrawEanEncodedDigit(int digit, int x, int y,
+ int height, bool flip) {
+ uint8 ean_encoding = kEanEncodings[digit];
+ if (flip) {
+ ean_encoding = ~ean_encoding;
+ }
+ uint8 mask = 0x40;
+ for (int i = 6; i >= 0; i--, mask >>= 1) {
+ if (ean_encoding & mask) {
+ DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
+ }
+ x += kUnitBarSize;
+ }
+ return x;
+}
+
+void YuvFrameGenerator::DrawBlockRectangle(uint8* p,
+ int x_start, int y_start, int width, int height, int pitch, uint8 value) {
+ for (int x = x_start; x < x_start + width; x++) {
+ for (int y = y_start; y < y_start + height; y++) {
+ p[x + y * pitch] = value;
+ }
+ }
+}
+
+} // namespace cricket
diff --git a/talk/media/base/yuvframegenerator.h b/talk/media/base/yuvframegenerator.h
new file mode 100644
index 0000000000..4adf971f64
--- /dev/null
+++ b/talk/media/base/yuvframegenerator.h
@@ -0,0 +1,78 @@
+// Generates YUV420 frames with a "landscape with striped crosshair" in the
+// Y-plane, plus a horizontal gradient in the U-plane and a vertical one in the
+// V-plane. This makes for a nice mix of colours that is suited for both
+// catching visual errors and making sure e.g. YUV->RGB/BGR conversion looks
+// the same on different platforms.
+// There is also a solid box bouncing around in the Y-plane, and two differently
+// coloured lines bouncing horizontally and vertically in the U and V plane.
+// This helps illustrating how the frame boundary goes, and can aid as a quite
+// handy visual help for noticing e.g. packet loss if the frames are encoded
+// and sent over the network.
+
+#ifndef TALK_MEDIA_BASE_YUVFRAMEGENERATOR_H_
+#define TALK_MEDIA_BASE_YUVFRAMEGENERATOR_H_
+
+#include "talk/base/basictypes.h"
+
+namespace cricket {
+
+class YuvFrameGenerator {
+ public:
+ // Constructs a frame-generator that produces frames of size |width|x|height|.
+ // If |enable_barcode| is specified, barcodes can be included in the frames
+ // when calling |GenerateNextFrame(uint8*, uint32)|. If |enable_barcode| is
+ // |true| then |width|x|height| should be at least 160x100; otherwise this
+ // constructor will abort.
+ YuvFrameGenerator(int width, int height, bool enable_barcode);
+ ~YuvFrameGenerator();
+
+ int GetFrameSize() { return frame_data_size_; }
+
+ // Generate the next frame and return it in the provided |frame_buffer|. If
+ // barcode_value is not |nullptr| the value referred by it will be encoded
+ // into a barcode in the frame. The value should in the range:
+ // [0..9,999,999]. If the value exceeds this range or barcodes were not
+ // requested in the constructor, this function will abort.
+ void GenerateNextFrame(uint8* frame_buffer, int32 barcode_value);
+
+ int GetHeight() { return height_; }
+ int GetWidth() { return width_; }
+
+ // Fetch the bounds of the barcode from the generator. The barcode will
+ // always be at this location. This function will abort if barcodes were not
+ // requested in the constructor.
+ void GetBarcodeBounds(int* top, int* left, int* width, int* height);
+
+ private:
+ void DrawLandscape(uint8 *p, int w, int h);
+ void DrawGradientX(uint8 *p, int w, int h);
+ void DrawGradientY(uint8 *p, int w, int h);
+ void DrawMovingLineX(uint8 *p, int w, int h, int n);
+ void DrawMovingLineY(uint8 *p, int w, int h, int n);
+ void DrawBouncingCube(uint8 *p, int w, int h, int n);
+
+ void DrawBarcode(uint32 value);
+ int DrawSideGuardBars(int x, int y, int height);
+ int DrawMiddleGuardBars(int x, int y, int height);
+ int DrawEanEncodedDigit(int digit, int x, int y, int height, bool r_code);
+ void DrawBlockRectangle(uint8* p, int x_start, int y_start,
+ int width, int height, int pitch, uint8 value);
+
+ private:
+ int width_;
+ int height_;
+ int frame_index_;
+ int frame_data_size_;
+ uint8* y_data_;
+ uint8* u_data_;
+ uint8* v_data_;
+
+ int barcode_start_x_;
+ int barcode_start_y_;
+
+ DISALLOW_COPY_AND_ASSIGN(YuvFrameGenerator);
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_BASE_YUVFRAMEGENERATOR_H_
diff --git a/talk/media/devices/devicemanager.cc b/talk/media/devices/devicemanager.cc
index 150b55855d..e90ec5efdb 100644
--- a/talk/media/devices/devicemanager.cc
+++ b/talk/media/devices/devicemanager.cc
@@ -37,6 +37,7 @@
#include "talk/media/base/mediacommon.h"
#include "talk/media/devices/deviceinfo.h"
#include "talk/media/devices/filevideocapturer.h"
+#include "talk/media/devices/yuvframescapturer.h"
#if !defined(IOS)
@@ -67,7 +68,6 @@ namespace cricket {
// Initialize to empty string.
const char DeviceManagerInterface::kDefaultDeviceName[] = "";
-
class DefaultVideoCapturerFactory : public VideoCapturerFactory {
public:
DefaultVideoCapturerFactory() {}
@@ -180,12 +180,27 @@ bool DeviceManager::GetVideoCaptureDevice(const std::string& name,
}
}
- // If |name| is a valid name for a file, return a file video capturer device.
+ // If |name| is a valid name for a file or yuvframedevice,
+ // return a fake video capturer device.
+ if (GetFakeVideoCaptureDevice(name, out)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool DeviceManager::GetFakeVideoCaptureDevice(const std::string& name,
+ Device* out) const {
if (talk_base::Filesystem::IsFile(name)) {
*out = FileVideoCapturer::CreateFileVideoCapturerDevice(name);
return true;
}
+ if (name == YuvFramesCapturer::kYuvFrameDeviceName) {
+ *out = YuvFramesCapturer::CreateYuvFramesCapturerDevice();
+ return true;
+ }
+
return false;
}
@@ -205,19 +220,12 @@ VideoCapturer* DeviceManager::CreateVideoCapturer(const Device& device) const {
LOG_F(LS_ERROR) << " should never be called!";
return NULL;
#else
- // TODO(hellner): Throw out the creation of a file video capturer once the
- // refactoring is completed.
- if (FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
- FileVideoCapturer* capturer = new FileVideoCapturer;
- if (!capturer->Init(device)) {
- delete capturer;
- return NULL;
- }
- LOG(LS_INFO) << "Created file video capturer " << device.name;
- capturer->set_repeat(talk_base::kForever);
+ VideoCapturer* capturer = ConstructFakeVideoCapturer(device);
+ if (capturer) {
return capturer;
}
- VideoCapturer* capturer = device_video_capturer_factory_->Create(device);
+
+ capturer = device_video_capturer_factory_->Create(device);
if (!capturer) {
return NULL;
}
@@ -232,6 +240,29 @@ VideoCapturer* DeviceManager::CreateVideoCapturer(const Device& device) const {
#endif
}
+VideoCapturer* DeviceManager::ConstructFakeVideoCapturer(
+ const Device& device) const {
+ // TODO(hellner): Throw out the creation of a file video capturer once the
+ // refactoring is completed.
+ if (FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
+ FileVideoCapturer* capturer = new FileVideoCapturer;
+ if (!capturer->Init(device)) {
+ delete capturer;
+ return NULL;
+ }
+ LOG(LS_INFO) << "Created file video capturer " << device.name;
+ capturer->set_repeat(talk_base::kForever);
+ return capturer;
+ }
+
+ if (YuvFramesCapturer::IsYuvFramesCapturerDevice(device)) {
+ YuvFramesCapturer* capturer = new YuvFramesCapturer();
+ capturer->Init();
+ return capturer;
+ }
+ return NULL;
+}
+
bool DeviceManager::GetWindows(
std::vector<talk_base::WindowDescription>* descriptions) {
if (!window_picker_) {
diff --git a/talk/media/devices/devicemanager.h b/talk/media/devices/devicemanager.h
index 90da89157f..f6099f36d2 100644
--- a/talk/media/devices/devicemanager.h
+++ b/talk/media/devices/devicemanager.h
@@ -201,6 +201,8 @@ class DeviceManager : public DeviceManagerInterface {
// The exclusion_list MUST be a NULL terminated list.
static bool ShouldDeviceBeIgnored(const std::string& device_name,
const char* const exclusion_list[]);
+ bool GetFakeVideoCaptureDevice(const std::string& name, Device* out) const;
+ VideoCapturer* ConstructFakeVideoCapturer(const Device& device) const;
bool initialized_;
talk_base::scoped_ptr<VideoCapturerFactory> device_video_capturer_factory_;
diff --git a/talk/media/devices/yuvframescapturer.cc b/talk/media/devices/yuvframescapturer.cc
new file mode 100644
index 0000000000..648094bf43
--- /dev/null
+++ b/talk/media/devices/yuvframescapturer.cc
@@ -0,0 +1,173 @@
+#include "talk/media/devices/yuvframescapturer.h"
+
+#include "talk/base/bytebuffer.h"
+#include "talk/base/criticalsection.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+
+#include "webrtc/system_wrappers/interface/clock.h"
+
+namespace cricket {
+///////////////////////////////////////////////////////////////////////
+// Definition of private class YuvFramesThread that periodically generates
+// frames.
+///////////////////////////////////////////////////////////////////////
+class YuvFramesCapturer::YuvFramesThread
+ : public talk_base::Thread, public talk_base::MessageHandler {
+ public:
+ explicit YuvFramesThread(YuvFramesCapturer* capturer)
+ : capturer_(capturer),
+ finished_(false) {
+ }
+
+ virtual ~YuvFramesThread() {
+ Stop();
+ }
+
+ // Override virtual method of parent Thread. Context: Worker Thread.
+ virtual void Run() {
+ // Read the first frame and start the message pump. The pump runs until
+ // Stop() is called externally or Quit() is called by OnMessage().
+ int waiting_time_ms = 0;
+ if (capturer_) {
+ capturer_->ReadFrame(true);
+ PostDelayed(waiting_time_ms, this);
+ Thread::Run();
+ }
+
+ talk_base::CritScope cs(&crit_);
+ finished_ = true;
+ }
+
+ // Override virtual method of parent MessageHandler. Context: Worker Thread.
+ virtual void OnMessage(talk_base::Message* /*pmsg*/) {
+ int waiting_time_ms = 0;
+ if (capturer_) {
+ capturer_->ReadFrame(false);
+ PostDelayed(waiting_time_ms, this);
+ } else {
+ Quit();
+ }
+ }
+
+ // Check if Run() is finished.
+ bool Finished() const {
+ talk_base::CritScope cs(&crit_);
+ return finished_;
+ }
+
+ private:
+ YuvFramesCapturer* capturer_;
+ mutable talk_base::CriticalSection crit_;
+ bool finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(YuvFramesThread);
+};
+
+/////////////////////////////////////////////////////////////////////
+// Implementation of class YuvFramesCapturer.
+/////////////////////////////////////////////////////////////////////
+
+const char* YuvFramesCapturer::kYuvFrameDeviceName = "YuvFramesGenerator";
+
+// TODO(shaowei): allow width_ and height_ to be configurable.
+YuvFramesCapturer::YuvFramesCapturer()
+ : frames_generator_thread(NULL),
+ width_(640),
+ height_(480),
+ frame_index_(0),
+ barcode_interval_(1) {
+}
+
+YuvFramesCapturer::~YuvFramesCapturer() {
+ Stop();
+ delete[] static_cast<char*>(captured_frame_.data);
+}
+
+void YuvFramesCapturer::Init() {
+ int size = width_ * height_;
+ int qsize = size / 4;
+ frame_generator_ = new YuvFrameGenerator(width_, height_, true);
+ frame_data_size_ = size + 2 * qsize;
+ captured_frame_.data = new char[frame_data_size_];
+ captured_frame_.fourcc = FOURCC_IYUV;
+ captured_frame_.pixel_height = 1;
+ captured_frame_.pixel_width = 1;
+ captured_frame_.width = width_;
+ captured_frame_.height = height_;
+ captured_frame_.data_size = frame_data_size_;
+
+ // Enumerate the supported formats. We have only one supported format.
+ VideoFormat format(width_, height_, VideoFormat::kMinimumInterval,
+ FOURCC_IYUV);
+ std::vector<VideoFormat> supported;
+ supported.push_back(format);
+ SetSupportedFormats(supported);
+}
+
+CaptureState YuvFramesCapturer::Start(const VideoFormat& capture_format) {
+ if (IsRunning()) {
+ LOG(LS_ERROR) << "Yuv Frame Generator is already running";
+ return CS_FAILED;
+ }
+ SetCaptureFormat(&capture_format);
+
+ barcode_reference_timestamp_millis_ =
+ static_cast<int64>(talk_base::Time()) * 1000;
+ // Create a thread to generate frames.
+ frames_generator_thread = new YuvFramesThread(this);
+ bool ret = frames_generator_thread->Start();
+ if (ret) {
+ LOG(LS_INFO) << "Yuv Frame Generator started";
+ return CS_RUNNING;
+ } else {
+ LOG(LS_ERROR) << "Yuv Frame Generator failed to start";
+ return CS_FAILED;
+ }
+}
+
+bool YuvFramesCapturer::IsRunning() {
+ return frames_generator_thread && !frames_generator_thread->Finished();
+}
+
+void YuvFramesCapturer::Stop() {
+ if (frames_generator_thread) {
+ frames_generator_thread->Stop();
+ frames_generator_thread = NULL;
+ LOG(LS_INFO) << "Yuv Frame Generator stopped";
+ }
+ SetCaptureFormat(NULL);
+}
+
+bool YuvFramesCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) {
+ if (!fourccs) {
+ return false;
+ }
+ fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
+ return true;
+}
+
+// Executed in the context of YuvFramesThread.
+void YuvFramesCapturer::ReadFrame(bool first_frame) {
+ // 1. Signal the previously read frame to downstream.
+ if (!first_frame) {
+ SignalFrameCaptured(this, &captured_frame_);
+ }
+ uint8* buffer = new uint8[frame_data_size_];
+ frame_generator_->GenerateNextFrame(buffer, GetBarcodeValue());
+ frame_index_++;
+ memmove(captured_frame_.data, buffer, frame_data_size_);
+ delete[] buffer;
+}
+
+
+int32 YuvFramesCapturer::GetBarcodeValue() {
+ if (barcode_reference_timestamp_millis_ == -1 ||
+ frame_index_ % barcode_interval_ != 0) {
+ return -1;
+ }
+ int64 now_millis = static_cast<int64>(talk_base::Time()) * 1000;
+ return static_cast<int32>(now_millis - barcode_reference_timestamp_millis_);
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/yuvframescapturer.h b/talk/media/devices/yuvframescapturer.h
new file mode 100644
index 0000000000..7886525840
--- /dev/null
+++ b/talk/media/devices/yuvframescapturer.h
@@ -0,0 +1,71 @@
+#ifndef TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_
+#define TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/base/stream.h"
+#include "talk/base/stringutils.h"
+#include "talk/media/base/videocapturer.h"
+#include "talk/media/base/yuvframegenerator.h"
+
+
+namespace talk_base {
+class FileStream;
+}
+
+namespace cricket {
+
+
+// Simulated video capturer that periodically reads frames from a file.
+class YuvFramesCapturer : public VideoCapturer {
+ public:
+ YuvFramesCapturer();
+ YuvFramesCapturer(int width, int height);
+ virtual ~YuvFramesCapturer();
+
+ static const char* kYuvFrameDeviceName;
+ static Device CreateYuvFramesCapturerDevice() {
+ std::stringstream id;
+ id << kYuvFrameDeviceName;
+ return Device(id.str(), id.str());
+ }
+ static bool IsYuvFramesCapturerDevice(const Device& device) {
+ return talk_base::starts_with(device.id.c_str(), kYuvFrameDeviceName);
+ }
+
+ void Init();
+ // Override virtual methods of parent class VideoCapturer.
+ virtual CaptureState Start(const VideoFormat& capture_format);
+ virtual void Stop();
+ virtual bool IsRunning();
+ virtual bool IsScreencast() const { return false; }
+
+ protected:
+ // Override virtual methods of parent class VideoCapturer.
+ virtual bool GetPreferredFourccs(std::vector<uint32>* fourccs);
+
+ // Read a frame and determine how long to wait for the next frame.
+ void ReadFrame(bool first_frame);
+
+ private:
+ class YuvFramesThread; // Forward declaration, defined in .cc.
+
+ YuvFrameGenerator* frame_generator_;
+ CapturedFrame captured_frame_;
+ YuvFramesThread* frames_generator_thread;
+ int width_;
+ int height_;
+ uint32 frame_data_size_;
+ uint32 frame_index_;
+
+ int64 barcode_reference_timestamp_millis_;
+ int32 barcode_interval_;
+ int32 GetBarcodeValue();
+
+ DISALLOW_COPY_AND_ASSIGN(YuvFramesCapturer);
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_
diff --git a/talk/media/webrtc/fakewebrtcvideocapturemodule.h b/talk/media/webrtc/fakewebrtcvideocapturemodule.h
index a53f31b3d4..5628e01bbe 100644
--- a/talk/media/webrtc/fakewebrtcvideocapturemodule.h
+++ b/talk/media/webrtc/fakewebrtcvideocapturemodule.h
@@ -59,6 +59,7 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule {
id_ = id;
return 0;
}
+#if defined(USE_WEBRTC_DEV_BRANCH)
virtual void RegisterCaptureDataCallback(
webrtc::VideoCaptureDataCallback& callback) {
callback_ = &callback;
@@ -70,6 +71,40 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule {
virtual void DeRegisterCaptureCallback() {
// Not implemented.
}
+ virtual void SetCaptureDelay(int32_t delay) { delay_ = delay; }
+ virtual int32_t CaptureDelay() { return delay_; }
+ virtual void EnableFrameRateCallback(const bool enable) {
+ // not implemented
+ }
+ virtual void EnableNoPictureAlarm(const bool enable) {
+ // not implemented
+ }
+#else
+ virtual int32_t RegisterCaptureDataCallback(
+ webrtc::VideoCaptureDataCallback& callback) {
+ callback_ = &callback;
+ }
+ virtual void DeRegisterCaptureDataCallback() { callback_ = NULL; }
+ virtual void RegisterCaptureCallback(webrtc::VideoCaptureFeedBack& callback) {
+ // Not implemented.
+ }
+ virtual void DeRegisterCaptureCallback() {
+ // Not implemented.
+ }
+ virtual int32_t SetCaptureDelay(int32_t delay) {
+ delay_ = delay;
+ return 0;
+ }
+ virtual int32_t CaptureDelay() {
+ return delay_;
+ }
+ virtual int32_t EnableFrameRateCallback(const bool enable) {
+ return -1; // not implemented
+ }
+ virtual int32_t EnableNoPictureAlarm(const bool enable) {
+ return -1; // not implemented
+ }
+#endif
virtual int32_t StartCapture(
const webrtc::VideoCaptureCapability& cap) {
if (running_) return -1;
@@ -93,8 +128,7 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule {
settings = cap_;
return 0;
}
- virtual void SetCaptureDelay(int32_t delay) { delay_ = delay; }
- virtual int32_t CaptureDelay() { return delay_; }
+
virtual int32_t SetCaptureRotation(
webrtc::VideoCaptureRotation rotation) {
return -1; // not implemented
@@ -103,12 +137,6 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule {
const webrtc::VideoCodec& codec) {
return NULL; // not implemented
}
- virtual void EnableFrameRateCallback(const bool enable) {
- // not implemented
- }
- virtual void EnableNoPictureAlarm(const bool enable) {
- // not implemented
- }
virtual int32_t AddRef() {
return 0;
}
diff --git a/talk/media/webrtc/webrtcvideocapturer.cc b/talk/media/webrtc/webrtcvideocapturer.cc
index 1ce4d77517..1afa45060e 100644
--- a/talk/media/webrtc/webrtcvideocapturer.cc
+++ b/talk/media/webrtc/webrtcvideocapturer.cc
@@ -248,6 +248,7 @@ CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
return CS_NO_DEVICE;
}
+ talk_base::CritScope cs(&critical_section_stopping_);
// TODO(hellner): weird to return failure when it is in fact actually running.
if (IsRunning()) {
LOG(LS_ERROR) << "The capturer is already running";
@@ -264,8 +265,13 @@ CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
std::string camera_id(GetId());
uint32 start = talk_base::Time();
+#if defined(USE_WEBRTC_DEV_BRANCH)
module_->RegisterCaptureDataCallback(*this);
if (module_->StartCapture(cap) != 0) {
+#else
+ if (module_->RegisterCaptureDataCallback(*this) != 0 ||
+ module_->StartCapture(cap) != 0) {
+#endif
LOG(LS_ERROR) << "Camera '" << camera_id << "' failed to start";
return CS_FAILED;
}
diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc
index ca0ed414c8..178d538c2f 100644
--- a/talk/media/webrtc/webrtcvideoengine.cc
+++ b/talk/media/webrtc/webrtcvideoengine.cc
@@ -583,13 +583,12 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
external_capture_(external_capture),
capturer_updated_(false),
interval_(0),
- video_adapter_(new CoordinatedVideoAdapter),
cpu_monitor_(cpu_monitor) {
- overuse_observer_.reset(new WebRtcOveruseObserver(video_adapter_.get()));
- SignalCpuAdaptationUnable.repeat(video_adapter_->SignalCpuAdaptationUnable);
+ overuse_observer_.reset(new WebRtcOveruseObserver(&video_adapter_));
+ SignalCpuAdaptationUnable.repeat(video_adapter_.SignalCpuAdaptationUnable);
if (cpu_monitor) {
cpu_monitor->SignalUpdate.connect(
- video_adapter_.get(), &CoordinatedVideoAdapter::OnCpuLoadUpdated);
+ &video_adapter_, &CoordinatedVideoAdapter::OnCpuLoadUpdated);
}
}
@@ -599,7 +598,7 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
bool sending() const { return sending_; }
void set_muted(bool on) {
// TODO(asapersson): add support.
- // video_adapter_->SetBlackOutput(on);
+ // video_adapter_.SetBlackOutput(on);
muted_ = on;
}
bool muted() {return muted_; }
@@ -614,7 +613,7 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
if (video_format_ != cricket::VideoFormat()) {
interval_ = video_format_.interval;
}
- video_adapter_->OnOutputFormatRequest(video_format_);
+ video_adapter_.OnOutputFormatRequest(video_format_);
}
void set_interval(int64 interval) {
if (video_format() == cricket::VideoFormat()) {
@@ -627,17 +626,13 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
VideoFormat format(codec.width, codec.height,
VideoFormat::FpsToInterval(codec.maxFramerate),
FOURCC_I420);
- if (video_adapter_->output_format().IsSize0x0()) {
- video_adapter_->SetOutputFormat(format);
+ if (video_adapter_.output_format().IsSize0x0()) {
+ video_adapter_.SetOutputFormat(format);
}
}
- bool AdaptFrame(const VideoFrame* in_frame, const VideoFrame** out_frame) {
- *out_frame = NULL;
- return video_adapter_->AdaptFrame(in_frame, out_frame);
- }
int CurrentAdaptReason() const {
- return video_adapter_->adapt_reason();
+ return video_adapter_.adapt_reason();
}
webrtc::CpuOveruseObserver* overuse_observer() {
return overuse_observer_.get();
@@ -663,6 +658,12 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
return;
}
capturer_updated_ = true;
+
+ // Disconnect from the previous video capturer.
+ if (video_capturer_) {
+ video_capturer_->SignalAdaptFrame.disconnect(this);
+ }
+
video_capturer_ = video_capturer;
if (video_capturer && !video_capturer->IsScreencast()) {
const VideoFormat* capture_format = video_capturer->GetCaptureFormat();
@@ -674,40 +675,51 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
// be zero, and all frames may be dropped.
// Consider fixing this by having video_adapter keep a pointer to the
// video capturer.
- video_adapter_->SetInputFormat(*capture_format);
+ video_adapter_.SetInputFormat(*capture_format);
}
+ // TODO(thorcarpenter): When the adapter supports "only frame dropping"
+ // mode, also hook it up to screencast capturers.
+ video_capturer->SignalAdaptFrame.connect(
+ this, &WebRtcVideoChannelSendInfo::AdaptFrame);
}
}
+ CoordinatedVideoAdapter* video_adapter() { return &video_adapter_; }
+
+ void AdaptFrame(VideoCapturer* capturer, const VideoFrame* input,
+ VideoFrame** adapted) {
+ video_adapter_.AdaptFrame(input, adapted);
+ }
+
void ApplyCpuOptions(const VideoOptions& options) {
bool cpu_adapt, cpu_smoothing, adapt_third;
float low, med, high;
if (options.adapt_input_to_cpu_usage.Get(&cpu_adapt)) {
- video_adapter_->set_cpu_adaptation(cpu_adapt);
+ video_adapter_.set_cpu_adaptation(cpu_adapt);
}
if (options.adapt_cpu_with_smoothing.Get(&cpu_smoothing)) {
- video_adapter_->set_cpu_smoothing(cpu_smoothing);
+ video_adapter_.set_cpu_smoothing(cpu_smoothing);
}
if (options.process_adaptation_threshhold.Get(&med)) {
- video_adapter_->set_process_threshold(med);
+ video_adapter_.set_process_threshold(med);
}
if (options.system_low_adaptation_threshhold.Get(&low)) {
- video_adapter_->set_low_system_threshold(low);
+ video_adapter_.set_low_system_threshold(low);
}
if (options.system_high_adaptation_threshhold.Get(&high)) {
- video_adapter_->set_high_system_threshold(high);
+ video_adapter_.set_high_system_threshold(high);
}
if (options.video_adapt_third.Get(&adapt_third)) {
- video_adapter_->set_scale_third(adapt_third);
+ video_adapter_.set_scale_third(adapt_third);
}
}
void SetCpuOveruseDetection(bool enable) {
if (cpu_monitor_ && enable) {
- cpu_monitor_->SignalUpdate.disconnect(video_adapter_.get());
+ cpu_monitor_->SignalUpdate.disconnect(&video_adapter_);
}
overuse_observer_->Enable(enable);
- video_adapter_->set_cpu_adaptation(enable);
+ video_adapter_.set_cpu_adaptation(enable);
}
void ProcessFrame(const VideoFrame& original_frame, bool mute,
@@ -761,7 +773,7 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
int64 interval_;
- talk_base::scoped_ptr<CoordinatedVideoAdapter> video_adapter_;
+ CoordinatedVideoAdapter video_adapter_;
talk_base::CpuMonitor* cpu_monitor_;
talk_base::scoped_ptr<WebRtcOveruseObserver> overuse_observer_;
};
@@ -2854,7 +2866,16 @@ bool WebRtcVideoMediaChannel::GetRenderer(uint32 ssrc,
return true;
}
-// TODO(zhurunz): Add unittests to test this function.
+bool WebRtcVideoMediaChannel::GetVideoAdapter(
+ uint32 ssrc, CoordinatedVideoAdapter** video_adapter) {
+ SendChannelMap::iterator it = send_channels_.find(ssrc);
+ if (it == send_channels_.end()) {
+ return false;
+ }
+ *video_adapter = it->second->video_adapter();
+ return true;
+}
+
void WebRtcVideoMediaChannel::SendFrame(VideoCapturer* capturer,
const VideoFrame* frame) {
// If the |capturer| is registered to any send channel, then send the frame
diff --git a/talk/media/webrtc/webrtcvideoengine.h b/talk/media/webrtc/webrtcvideoengine.h
index d4949c473c..fa1b24881c 100644
--- a/talk/media/webrtc/webrtcvideoengine.h
+++ b/talk/media/webrtc/webrtcvideoengine.h
@@ -60,12 +60,13 @@ class CpuMonitor;
namespace cricket {
+class CoordinatedVideoAdapter;
+class ViETraceWrapper;
+class ViEWrapper;
class VideoCapturer;
class VideoFrame;
class VideoProcessor;
class VideoRenderer;
-class ViETraceWrapper;
-class ViEWrapper;
class VoiceMediaChannel;
class WebRtcDecoderObserver;
class WebRtcEncoderObserver;
@@ -227,10 +228,6 @@ class WebRtcVideoEngine : public sigslot::has_slots<>,
int local_renderer_h_;
VideoRenderer* local_renderer_;
- // Critical section to protect the media processor register/unregister
- // while processing a frame
- talk_base::CriticalSection signal_media_critical_;
-
talk_base::scoped_ptr<talk_base::CpuMonitor> cpu_monitor_;
};
@@ -289,12 +286,11 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler,
// Public functions for use by tests and other specialized code.
uint32 send_ssrc() const { return 0; }
bool GetRenderer(uint32 ssrc, VideoRenderer** renderer);
+ bool GetVideoAdapter(uint32 ssrc, CoordinatedVideoAdapter** video_adapter);
void SendFrame(VideoCapturer* capturer, const VideoFrame* frame);
bool SendFrame(WebRtcVideoChannelSendInfo* channel_info,
const VideoFrame* frame, bool is_screencast);
- void AdaptAndSendFrame(VideoCapturer* capturer, const VideoFrame* frame);
-
// Thunk functions for use with HybridVideoEngine
void OnLocalFrame(VideoCapturer* capturer, const VideoFrame* frame) {
SendFrame(0u, frame, capturer->IsScreencast());
diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc
index e331188b59..386ec0c524 100644
--- a/talk/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc
@@ -36,6 +36,7 @@
#include "talk/media/base/fakevideorenderer.h"
#include "talk/media/base/mediachannel.h"
#include "talk/media/base/testutils.h"
+#include "talk/media/base/videoadapter.h"
#include "talk/media/base/videoengine_unittest.h"
#include "talk/media/webrtc/fakewebrtcvideocapturemodule.h"
#include "talk/media/webrtc/fakewebrtcvideoengine.h"
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index b4b96d1800..4ad2ff502c 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -1639,17 +1639,68 @@ int WebRtcVoiceEngine::CreateSoundclipVoiceChannel() {
return CreateVoiceChannel(voe_wrapper_sc_.get());
}
-// This struct relies on the generated copy constructor and assignment operator
-// since it is used in an stl::map.
-struct WebRtcVoiceMediaChannel::WebRtcVoiceChannelInfo {
- WebRtcVoiceChannelInfo() : channel(-1), renderer(NULL) {}
- WebRtcVoiceChannelInfo(int ch, AudioRenderer* r)
- : channel(ch),
- renderer(r) {}
- ~WebRtcVoiceChannelInfo() {}
-
- int channel;
- AudioRenderer* renderer;
+class WebRtcVoiceMediaChannel::WebRtcVoiceChannelRenderer
+ : public AudioRenderer::Sink {
+ public:
+ WebRtcVoiceChannelRenderer(int ch,
+ webrtc::AudioTransport* voe_audio_transport)
+ : channel_(ch),
+ voe_audio_transport_(voe_audio_transport),
+ renderer_(NULL) {
+ }
+ virtual ~WebRtcVoiceChannelRenderer() {
+ Stop();
+ }
+
+ // Starts the rendering by setting a sink to the renderer to get data
+ // callback.
+ // TODO(xians): Make sure Start() is called only once.
+ void Start(AudioRenderer* renderer) {
+ ASSERT(renderer != NULL);
+ if (renderer_) {
+ ASSERT(renderer_ == renderer);
+ return;
+ }
+
+ // TODO(xians): Remove AddChannel() call after Chrome turns on APM
+ // in getUserMedia by default.
+ renderer->AddChannel(channel_);
+ renderer->SetSink(this);
+ renderer_ = renderer;
+ }
+
+ // Stops rendering by setting the sink of the renderer to NULL. No data
+ // callback will be received after this method.
+ void Stop() {
+ if (!renderer_)
+ return;
+
+ renderer_->RemoveChannel(channel_);
+ renderer_->SetSink(NULL);
+ renderer_ = NULL;
+ }
+
+ // AudioRenderer::Sink implementation.
+ virtual void OnData(const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames) OVERRIDE {
+ // TODO(xians): Make new interface in AudioTransport to pass the data to
+ // WebRtc VoE channel.
+ }
+
+ // Accessor to the VoE channel ID.
+ int channel() const { return channel_; }
+
+ private:
+ const int channel_;
+ webrtc::AudioTransport* const voe_audio_transport_;
+
+ // Raw pointer to AudioRenderer owned by LocalAudioTrackHandler.
+ // PeerConnection will make sure invalidating the pointer before the object
+ // goes away.
+ AudioRenderer* renderer_;
};
// WebRtcVoiceMediaChannel
@@ -1841,8 +1892,8 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs(
for (ChannelMap::iterator it = receive_channels_.begin();
it != receive_channels_.end() && ret; ++it) {
if (engine()->voe()->codec()->SetRecPayloadType(
- it->second.channel, voe_codec) == -1) {
- LOG_RTCERR2(SetRecPayloadType, it->second.channel,
+ it->second->channel(), voe_codec) == -1) {
+ LOG_RTCERR2(SetRecPayloadType, it->second->channel(),
ToString(voe_codec));
ret = false;
}
@@ -2047,7 +2098,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
send_codecs_ = codecs;
for (ChannelMap::iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
- if (!SetSendCodecs(iter->second.channel, codecs)) {
+ if (!SetSendCodecs(iter->second->channel(), codecs)) {
return false;
}
}
@@ -2061,7 +2112,7 @@ void WebRtcVoiceMediaChannel::SetNack(const ChannelMap& channels,
bool nack_enabled) {
for (ChannelMap::const_iterator it = channels.begin();
it != channels.end(); ++it) {
- SetNack(it->second.channel, nack_enabled);
+ SetNack(it->second->channel(), nack_enabled);
}
}
@@ -2081,7 +2132,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodec(
<< ", bitrate=" << send_codec.rate;
for (ChannelMap::iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
- if (!SetSendCodec(iter->second.channel, send_codec))
+ if (!SetSendCodec(iter->second->channel(), send_codec))
return false;
}
@@ -2132,9 +2183,9 @@ bool WebRtcVoiceMediaChannel::SetSendRtpHeaderExtensions(
for (ChannelMap::const_iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
if (engine()->voe()->rtp()->SetRTPAudioLevelIndicationStatus(
- iter->second.channel, enable, id) == -1) {
+ iter->second->channel(), enable, id) == -1) {
LOG_RTCERR3(SetRTPAudioLevelIndicationStatus,
- iter->second.channel, enable, id);
+ iter->second->channel(), enable, id);
return false;
}
}
@@ -2168,9 +2219,9 @@ bool WebRtcVoiceMediaChannel::ChangePlayout(bool playout) {
}
for (ChannelMap::iterator it = receive_channels_.begin();
it != receive_channels_.end() && result; ++it) {
- if (!SetPlayout(it->second.channel, playout)) {
+ if (!SetPlayout(it->second->channel(), playout)) {
LOG(LS_ERROR) << "SetPlayout " << playout << " on channel "
- << it->second.channel << " failed";
+ << it->second->channel() << " failed";
result = false;
}
}
@@ -2208,7 +2259,7 @@ bool WebRtcVoiceMediaChannel::ChangeSend(SendFlags send) {
// Change the settings on each send channel.
for (ChannelMap::iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
- if (!ChangeSend(iter->second.channel, send))
+ if (!ChangeSend(iter->second->channel(), send))
return false;
}
@@ -2280,7 +2331,7 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
bool default_channel_is_available = true;
for (ChannelMap::const_iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
- if (IsDefaultChannel(iter->second.channel)) {
+ if (IsDefaultChannel(iter->second->channel())) {
default_channel_is_available = false;
break;
}
@@ -2300,7 +2351,15 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
// Save the channel to send_channels_, so that RemoveSendStream() can still
// delete the channel in case failure happens below.
- send_channels_[sp.first_ssrc()] = WebRtcVoiceChannelInfo(channel, NULL);
+#ifdef USE_WEBRTC_DEV_BRANCH
+ webrtc::AudioTransport* audio_transport =
+ engine()->voe()->base()->audio_transport();
+#else
+ webrtc::AudioTransport* audio_transport = NULL;
+#endif
+ send_channels_.insert(std::make_pair(
+ sp.first_ssrc(),
+ new WebRtcVoiceChannelRenderer(channel, audio_transport)));
// Set the send (local) SSRC.
// If there are multiple send SSRCs, we can only set the first one here, and
@@ -2319,10 +2378,10 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
for (ChannelMap::const_iterator it = receive_channels_.begin();
it != receive_channels_.end(); ++it) {
// Only update the SSRC for non-default channels.
- if (!IsDefaultChannel(it->second.channel)) {
- if (engine()->voe()->rtp()->SetLocalSSRC(it->second.channel,
+ if (!IsDefaultChannel(it->second->channel())) {
+ if (engine()->voe()->rtp()->SetLocalSSRC(it->second->channel(),
sp.first_ssrc()) != 0) {
- LOG_RTCERR2(SetLocalSSRC, it->second.channel, sp.first_ssrc());
+ LOG_RTCERR2(SetLocalSSRC, it->second->channel(), sp.first_ssrc());
return false;
}
}
@@ -2349,12 +2408,13 @@ bool WebRtcVoiceMediaChannel::RemoveSendStream(uint32 ssrc) {
return false;
}
- int channel = it->second.channel;
+ int channel = it->second->channel();
ChangeSend(channel, SEND_NOTHING);
- // Notify the audio renderer that the send channel is going away.
- if (it->second.renderer)
- it->second.renderer->RemoveChannel(channel);
+ // Delete the WebRtcVoiceChannelRenderer object connected to the channel,
+ // this will disconnect the audio renderer with the send channel.
+ delete it->second;
+ send_channels_.erase(it);
if (IsDefaultChannel(channel)) {
// Do not delete the default channel since the receive channels depend on
@@ -2368,7 +2428,6 @@ bool WebRtcVoiceMediaChannel::RemoveSendStream(uint32 ssrc) {
return false;
}
- send_channels_.erase(it);
if (send_channels_.empty())
ChangeSend(SEND_NOTHING);
@@ -2394,12 +2453,19 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
// Reuse default channel for recv stream in non-conference mode call
// when the default channel is not being used.
+#ifdef USE_WEBRTC_DEV_BRANCH
+ webrtc::AudioTransport* audio_transport =
+ engine()->voe()->base()->audio_transport();
+#else
+ webrtc::AudioTransport* audio_transport = NULL;
+#endif
if (!InConferenceMode() && default_receive_ssrc_ == 0) {
LOG(LS_INFO) << "Recv stream " << sp.first_ssrc()
<< " reuse default channel";
default_receive_ssrc_ = sp.first_ssrc();
receive_channels_.insert(std::make_pair(
- default_receive_ssrc_, WebRtcVoiceChannelInfo(voe_channel(), NULL)));
+ default_receive_ssrc_,
+ new WebRtcVoiceChannelRenderer(voe_channel(), audio_transport)));
return SetPlayout(voe_channel(), playout_);
}
@@ -2416,7 +2482,8 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
}
receive_channels_.insert(
- std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL)));
+ std::make_pair(
+ ssrc, new WebRtcVoiceChannelRenderer(channel, audio_transport)));
LOG(LS_INFO) << "New audio stream " << ssrc
<< " registered to VoiceEngine channel #"
@@ -2493,34 +2560,28 @@ bool WebRtcVoiceMediaChannel::RemoveRecvStream(uint32 ssrc) {
return false;
}
+ // Delete the WebRtcVoiceChannelRenderer object connected to the channel, this
+ // will disconnect the audio renderer with the receive channel.
+ // Cache the channel before the deletion.
+ const int channel = it->second->channel();
+ delete it->second;
+ receive_channels_.erase(it);
+
if (ssrc == default_receive_ssrc_) {
- ASSERT(IsDefaultChannel(it->second.channel));
+ ASSERT(IsDefaultChannel(channel));
// Recycle the default channel is for recv stream.
if (playout_)
SetPlayout(voe_channel(), false);
- if (it->second.renderer)
- it->second.renderer->RemoveChannel(voe_channel());
-
default_receive_ssrc_ = 0;
- receive_channels_.erase(it);
return true;
}
- // Non default channel.
- // Notify the renderer that channel is going away.
- if (it->second.renderer)
- it->second.renderer->RemoveChannel(it->second.channel);
-
LOG(LS_INFO) << "Removing audio stream " << ssrc
- << " with VoiceEngine channel #" << it->second.channel << ".";
- if (!DeleteChannel(it->second.channel)) {
- // Erase the entry anyhow.
- receive_channels_.erase(it);
+ << " with VoiceEngine channel #" << channel << ".";
+ if (!DeleteChannel(channel))
return false;
- }
- receive_channels_.erase(it);
bool enable_default_channel_playout = false;
if (receive_channels_.empty()) {
// The last stream was removed. We can now enable the default
@@ -2558,19 +2619,11 @@ bool WebRtcVoiceMediaChannel::SetRemoteRenderer(uint32 ssrc,
return true;
}
- AudioRenderer* remote_renderer = it->second.renderer;
- if (renderer) {
- ASSERT(remote_renderer == NULL || remote_renderer == renderer);
- if (!remote_renderer) {
- renderer->AddChannel(it->second.channel);
- }
- } else if (remote_renderer) {
- // |renderer| == NULL, remove the channel from the renderer.
- remote_renderer->RemoveChannel(it->second.channel);
- }
+ if (renderer)
+ it->second->Start(renderer);
+ else
+ it->second->Stop();
- // Assign the new value to the struct.
- it->second.renderer = renderer;
return true;
}
@@ -2588,17 +2641,11 @@ bool WebRtcVoiceMediaChannel::SetLocalRenderer(uint32 ssrc,
return true;
}
- AudioRenderer* local_renderer = it->second.renderer;
- if (renderer) {
- ASSERT(local_renderer == NULL || local_renderer == renderer);
- if (!local_renderer)
- renderer->AddChannel(it->second.channel);
- } else if (local_renderer) {
- local_renderer->RemoveChannel(it->second.channel);
- }
+ if (renderer)
+ it->second->Start(renderer);
+ else
+ it->second->Stop();
- // Assign the new value to the struct.
- it->second.renderer = renderer;
return true;
}
@@ -2609,7 +2656,7 @@ bool WebRtcVoiceMediaChannel::GetActiveStreams(
actives->clear();
for (ChannelMap::iterator it = receive_channels_.begin();
it != receive_channels_.end(); ++it) {
- int level = GetOutputLevel(it->second.channel);
+ int level = GetOutputLevel(it->second->channel());
if (level > 0) {
actives->push_back(std::make_pair(it->first, level));
}
@@ -2622,7 +2669,7 @@ int WebRtcVoiceMediaChannel::GetOutputLevel() {
int highest = GetOutputLevel(voe_channel());
for (ChannelMap::iterator it = receive_channels_.begin();
it != receive_channels_.end(); ++it) {
- int level = GetOutputLevel(it->second.channel);
+ int level = GetOutputLevel(it->second->channel());
highest = talk_base::_max(level, highest);
}
return highest;
@@ -2665,7 +2712,7 @@ bool WebRtcVoiceMediaChannel::SetOutputScaling(
channels.push_back(voe_channel());
for (ChannelMap::const_iterator it = receive_channels_.begin();
it != receive_channels_.end(); ++it) {
- channels.push_back(it->second.channel);
+ channels.push_back(it->second->channel());
}
} else { // Collect only the channel of the specified ssrc.
int channel = GetReceiveChannelNum(ssrc);
@@ -2801,7 +2848,7 @@ bool WebRtcVoiceMediaChannel::InsertDtmf(uint32 ssrc, int event,
bool default_channel_is_inuse = false;
for (ChannelMap::const_iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
- if (IsDefaultChannel(iter->second.channel)) {
+ if (IsDefaultChannel(iter->second->channel())) {
default_channel_is_inuse = true;
break;
}
@@ -2809,7 +2856,7 @@ bool WebRtcVoiceMediaChannel::InsertDtmf(uint32 ssrc, int event,
if (default_channel_is_inuse) {
channel = voe_channel();
} else if (!send_channels_.empty()) {
- channel = send_channels_.begin()->second.channel;
+ channel = send_channels_.begin()->second->channel();
}
} else {
channel = GetSendChannelNum(ssrc);
@@ -2907,11 +2954,12 @@ void WebRtcVoiceMediaChannel::OnRtcpReceived(
for (ChannelMap::iterator iter = send_channels_.begin();
iter != send_channels_.end(); ++iter) {
// Make sure not sending the same packet to default channel more than once.
- if (IsDefaultChannel(iter->second.channel) && has_sent_to_default_channel)
+ if (IsDefaultChannel(iter->second->channel()) &&
+ has_sent_to_default_channel)
continue;
engine()->voe()->network()->ReceivedRTCPPacket(
- iter->second.channel,
+ iter->second->channel(),
packet->data(),
static_cast<unsigned int>(packet->length()));
}
@@ -3022,7 +3070,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) {
for (ChannelMap::const_iterator channel_iter = send_channels_.begin();
channel_iter != send_channels_.end(); ++channel_iter) {
- const int channel = channel_iter->second.channel;
+ const int channel = channel_iter->second->channel();
// Fill in the sender info, based on what we know, and what the
// remote side told us it got from its RTCP report.
@@ -3094,7 +3142,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) {
std::vector<int> channels;
for (ChannelMap::const_iterator it = receive_channels_.begin();
it != receive_channels_.end(); ++it) {
- channels.push_back(it->second.channel);
+ channels.push_back(it->second->channel());
}
if (channels.empty()) {
channels.push_back(voe_channel());
@@ -3172,7 +3220,7 @@ bool WebRtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) {
// Check whether this is a sending channel.
for (ChannelMap::const_iterator it = send_channels_.begin();
it != send_channels_.end(); ++it) {
- if (it->second.channel == channel_num) {
+ if (it->second->channel() == channel_num) {
// This is a sending channel.
uint32 local_ssrc = 0;
if (engine()->voe()->rtp()->GetLocalSSRC(
@@ -3186,7 +3234,7 @@ bool WebRtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) {
// Check whether this is a receiving channel.
for (ChannelMap::const_iterator it = receive_channels_.begin();
it != receive_channels_.end(); ++it) {
- if (it->second.channel == channel_num) {
+ if (it->second->channel() == channel_num) {
*ssrc = it->first;
return true;
}
@@ -3214,14 +3262,14 @@ int WebRtcVoiceMediaChannel::GetOutputLevel(int channel) {
int WebRtcVoiceMediaChannel::GetReceiveChannelNum(uint32 ssrc) {
ChannelMap::iterator it = receive_channels_.find(ssrc);
if (it != receive_channels_.end())
- return it->second.channel;
+ return it->second->channel();
return (ssrc == default_receive_ssrc_) ? voe_channel() : -1;
}
int WebRtcVoiceMediaChannel::GetSendChannelNum(uint32 ssrc) {
ChannelMap::iterator it = send_channels_.find(ssrc);
if (it != send_channels_.end())
- return it->second.channel;
+ return it->second->channel();
return -1;
}
diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h
index 4b31656236..ebcdb5f6e9 100644
--- a/talk/media/webrtc/webrtcvoiceengine.h
+++ b/talk/media/webrtc/webrtcvoiceengine.h
@@ -392,8 +392,11 @@ class WebRtcVoiceMediaChannel
static Error WebRtcErrorToChannelError(int err_code);
private:
- struct WebRtcVoiceChannelInfo;
- typedef std::map<uint32, WebRtcVoiceChannelInfo> ChannelMap;
+ class WebRtcVoiceChannelRenderer;
+ // Map of ssrc to WebRtcVoiceChannelRenderer object. A new object of
+ // WebRtcVoiceChannelRenderer will be created for every new stream and
+ // will be destroyed when the stream goes away.
+ typedef std::map<uint32, WebRtcVoiceChannelRenderer*> ChannelMap;
void SetNack(int channel, bool nack_enabled);
void SetNack(const ChannelMap& channels, bool nack_enabled);
diff --git a/talk/p2p/base/dtlstransportchannel.h b/talk/p2p/base/dtlstransportchannel.h
index d6b7346748..c9778881e1 100644
--- a/talk/p2p/base/dtlstransportchannel.h
+++ b/talk/p2p/base/dtlstransportchannel.h
@@ -193,6 +193,9 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
virtual void SetIceTiebreaker(uint64 tiebreaker) {
channel_->SetIceTiebreaker(tiebreaker);
}
+ virtual bool GetIceProtocolType(IceProtocolType* type) const {
+ return channel_->GetIceProtocolType(type);
+ }
virtual void SetIceProtocolType(IceProtocolType type) {
channel_->SetIceProtocolType(type);
}
diff --git a/talk/p2p/base/fakesession.h b/talk/p2p/base/fakesession.h
index 2615f50dff..d199449945 100644
--- a/talk/p2p/base/fakesession.h
+++ b/talk/p2p/base/fakesession.h
@@ -101,6 +101,10 @@ class FakeTransportChannel : public TransportChannelImpl,
virtual void SetIceRole(IceRole role) { role_ = role; }
virtual IceRole GetIceRole() const { return role_; }
virtual void SetIceTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; }
+ virtual bool GetIceProtocolType(IceProtocolType* type) const {
+ *type = ice_proto_;
+ return true;
+ }
virtual void SetIceProtocolType(IceProtocolType type) { ice_proto_ = type; }
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc
index 38cc35445e..104b5e69df 100644
--- a/talk/p2p/base/p2ptransportchannel.cc
+++ b/talk/p2p/base/p2ptransportchannel.cc
@@ -167,7 +167,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& content_name,
pending_best_connection_(NULL),
sort_dirty_(false),
was_writable_(false),
- protocol_type_(ICEPROTO_GOOGLE),
+ protocol_type_(ICEPROTO_HYBRID),
remote_ice_mode_(ICEMODE_FULL),
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
@@ -237,6 +237,11 @@ void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) {
tiebreaker_ = tiebreaker;
}
+bool P2PTransportChannel::GetIceProtocolType(IceProtocolType* type) const {
+ *type = protocol_type_;
+ return true;
+}
+
void P2PTransportChannel::SetIceProtocolType(IceProtocolType type) {
ASSERT(worker_thread_ == talk_base::Thread::Current());
@@ -467,7 +472,7 @@ void P2PTransportChannel::OnUnknownAddress(
// Create a new candidate with this address.
std::string type;
- if (protocol_type_ == ICEPROTO_RFC5245) {
+ if (port->IceProtocol() == ICEPROTO_RFC5245) {
type = PRFLX_PORT_TYPE;
} else {
// G-ICE doesn't support prflx candidate.
@@ -491,7 +496,7 @@ void P2PTransportChannel::OnUnknownAddress(
new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX));
}
- if (protocol_type_ == ICEPROTO_RFC5245) {
+ if (port->IceProtocol() == ICEPROTO_RFC5245) {
// RFC 5245
// If the source transport address of the request does not match any
// existing remote candidates, it represents a new peer reflexive remote
@@ -884,6 +889,15 @@ void P2PTransportChannel::SortConnections() {
// will be sorted.
UpdateConnectionStates();
+ if (protocol_type_ == ICEPROTO_HYBRID) {
+ // If we are in hybrid mode, we are not sending any ping requests, so there
+ // is no point in sorting the connections. In hybrid state, ports can have
+ // different protocol than hybrid and protocol may differ from one another.
+ // Instead just update the state of this channel
+ UpdateChannelState();
+ return;
+ }
+
// Any changes after this point will require a re-sort.
sort_dirty_ = false;
diff --git a/talk/p2p/base/p2ptransportchannel.h b/talk/p2p/base/p2ptransportchannel.h
index 6f287f369c..b7c5929b15 100644
--- a/talk/p2p/base/p2ptransportchannel.h
+++ b/talk/p2p/base/p2ptransportchannel.h
@@ -79,6 +79,7 @@ class P2PTransportChannel : public TransportChannelImpl,
virtual void SetIceRole(IceRole role);
virtual IceRole GetIceRole() const { return ice_role_; }
virtual void SetIceTiebreaker(uint64 tiebreaker);
+ virtual bool GetIceProtocolType(IceProtocolType* type) const;
virtual void SetIceProtocolType(IceProtocolType type);
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd);
diff --git a/talk/p2p/base/p2ptransportchannel_unittest.cc b/talk/p2p/base/p2ptransportchannel_unittest.cc
index 3c24ded632..28b71290a7 100644
--- a/talk/p2p/base/p2ptransportchannel_unittest.cc
+++ b/talk/p2p/base/p2ptransportchannel_unittest.cc
@@ -588,6 +588,46 @@ class P2PTransportChannelTestBase : public testing::Test,
TestSendRecv(1);
}
+ void TestHybridConnectivity(cricket::IceProtocolType proto) {
+ AddAddress(0, kPublicAddrs[0]);
+ AddAddress(1, kPublicAddrs[1]);
+
+ SetAllocationStepDelay(0, kMinimumStepDelay);
+ SetAllocationStepDelay(1, kMinimumStepDelay);
+
+ SetIceRole(0, cricket::ICEROLE_CONTROLLING);
+ SetIceProtocol(0, cricket::ICEPROTO_HYBRID);
+ SetIceTiebreaker(0, kTiebreaker1);
+ SetIceRole(1, cricket::ICEROLE_CONTROLLED);
+ SetIceProtocol(1, proto);
+ SetIceTiebreaker(1, kTiebreaker2);
+
+ CreateChannels(1);
+ // When channel is in hybrid and it's controlling agent, channel will
+ // receive ping request from the remote. Hence connection is readable.
+ // Since channel is in hybrid, it will not send any pings, so no writable
+ // connection. Since channel2 is in controlled state, it will not have
+ // any connections which are readable or writable, as it didn't received
+ // pings (or none) with USE-CANDIDATE attribute.
+ EXPECT_TRUE_WAIT(ep1_ch1()->readable(), 1000);
+
+ // Set real protocol type.
+ ep1_ch1()->SetIceProtocolType(proto);
+
+ // Channel should able to send ping requests and connections become writable
+ // in both directions.
+ EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
+ ep2_ch1()->readable() && ep2_ch1()->writable(),
+ 1000);
+ EXPECT_TRUE(
+ ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
+ LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
+ RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
+
+ TestSendRecv(1);
+ DestroyChannels();
+ }
+
void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
channel->OnSignalingReady();
}
@@ -1082,6 +1122,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeAsIce) {
cricket::ICEPROTO_RFC5245);
CreateChannels(1);
TestHandleIceUfragPasswordChanged();
+ DestroyChannels();
}
// Test that we restart candidate allocation when local ufrag&pwd changed.
@@ -1097,6 +1138,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeBundleAsIce) {
CreateChannels(2);
TestHandleIceUfragPasswordChanged();
+ DestroyChannels();
}
// Test that we restart candidate allocation when local ufrag&pwd changed.
@@ -1109,6 +1151,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeAsGice) {
cricket::ICEPROTO_GOOGLE);
CreateChannels(1);
TestHandleIceUfragPasswordChanged();
+ DestroyChannels();
}
// Test that ICE restart works when bundle is enabled.
@@ -1124,6 +1167,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeBundleAsGice) {
CreateChannels(2);
TestHandleIceUfragPasswordChanged();
+ DestroyChannels();
}
// Test the operation of GetStats.
@@ -1389,6 +1433,19 @@ TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) {
ep2_ch1()->best_connection());
TestSendRecv(1);
+ DestroyChannels();
+}
+
+// This test verifies channel can handle ice messages when channel is in
+// hybrid mode.
+TEST_F(P2PTransportChannelTest, TestConnectivityBetweenHybridandIce) {
+ TestHybridConnectivity(cricket::ICEPROTO_RFC5245);
+}
+
+// This test verifies channel can handle Gice messages when channel is in
+// hybrid mode.
+TEST_F(P2PTransportChannelTest, TestConnectivityBetweenHybridandGice) {
+ TestHybridConnectivity(cricket::ICEPROTO_GOOGLE);
}
// Verify that we can set DSCP value and retrieve properly from P2PTC.
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index 2fc2cb2b67..b6421ad213 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -178,11 +178,10 @@ Port::Port(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory,
password_(password),
timeout_delay_(kPortTimeoutDelay),
enable_port_packets_(false),
- ice_protocol_(ICEPROTO_GOOGLE),
+ ice_protocol_(ICEPROTO_HYBRID),
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
- shared_socket_(true),
- default_dscp_(talk_base::DSCP_NO_CHANGE) {
+ shared_socket_(true) {
Construct();
}
@@ -205,11 +204,10 @@ Port::Port(talk_base::Thread* thread, const std::string& type,
password_(password),
timeout_delay_(kPortTimeoutDelay),
enable_port_packets_(false),
- ice_protocol_(ICEPROTO_GOOGLE),
+ ice_protocol_(ICEPROTO_HYBRID),
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
- shared_socket_(false),
- default_dscp_(talk_base::DSCP_NO_CHANGE) {
+ shared_socket_(false) {
ASSERT(factory_ != NULL);
Construct();
}
@@ -341,6 +339,10 @@ bool Port::IsGoogleIce() const {
return (ice_protocol_ == ICEPROTO_GOOGLE);
}
+bool Port::IsHybridIce() const {
+ return (ice_protocol_ == ICEPROTO_HYBRID);
+}
+
bool Port::GetStunMessage(const char* data, size_t size,
const talk_base::SocketAddress& addr,
IceMessage** out_msg, std::string* out_username) {
@@ -382,7 +384,9 @@ bool Port::GetStunMessage(const char* data, size_t size,
// If the username is bad or unknown, fail with a 401 Unauthorized.
std::string local_ufrag;
std::string remote_ufrag;
- if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag) ||
+ IceProtocolType remote_protocol_type;
+ if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag,
+ &remote_protocol_type) ||
local_ufrag != username_fragment()) {
LOG_J(LS_ERROR, this) << "Received STUN request with bad local username "
<< local_ufrag << " from "
@@ -392,6 +396,15 @@ bool Port::GetStunMessage(const char* data, size_t size,
return true;
}
+ // Port is initialized to GOOGLE-ICE protocol type. If pings from remote
+ // are received before the signal message, protocol type may be different.
+ // Based on the STUN username, we can determine what's the remote protocol.
+ // This also enables us to send the response back using the same protocol
+ // as the request.
+ if (IsHybridIce()) {
+ SetIceProtocolType(remote_protocol_type);
+ }
+
// If ICE, and the MESSAGE-INTEGRITY is bad, fail with a 401 Unauthorized
if (IsStandardIce() &&
!stun_msg->ValidateMessageIntegrity(data, size, password_)) {
@@ -453,7 +466,8 @@ bool Port::IsCompatibleAddress(const talk_base::SocketAddress& addr) {
bool Port::ParseStunUsername(const StunMessage* stun_msg,
std::string* local_ufrag,
- std::string* remote_ufrag) const {
+ std::string* remote_ufrag,
+ IceProtocolType* remote_protocol_type) const {
// The packet must include a username that either begins or ends with our
// fragment. It should begin with our fragment if it is a request and it
// should end with our fragment if it is a response.
@@ -465,8 +479,16 @@ bool Port::ParseStunUsername(const StunMessage* stun_msg,
return false;
const std::string username_attr_str = username_attr->GetString();
- if (IsStandardIce()) {
- size_t colon_pos = username_attr_str.find(":");
+ size_t colon_pos = username_attr_str.find(":");
+ // If we are in hybrid mode set the appropriate ice protocol type based on
+ // the username argument style.
+ if (IsHybridIce()) {
+ *remote_protocol_type = (colon_pos != std::string::npos) ?
+ ICEPROTO_RFC5245 : ICEPROTO_GOOGLE;
+ } else {
+ *remote_protocol_type = ice_protocol_;
+ }
+ if (*remote_protocol_type == ICEPROTO_RFC5245) {
if (colon_pos != std::string::npos) { // RFRAG:LFRAG
*local_ufrag = username_attr_str.substr(0, colon_pos);
*remote_ufrag = username_attr_str.substr(
@@ -474,7 +496,7 @@ bool Port::ParseStunUsername(const StunMessage* stun_msg,
} else {
return false;
}
- } else if (IsGoogleIce()) {
+ } else if (*remote_protocol_type == ICEPROTO_GOOGLE) {
int remote_frag_len = static_cast<int>(username_attr_str.size());
remote_frag_len -= static_cast<int>(username_fragment().size());
if (remote_frag_len < 0)
@@ -716,7 +738,7 @@ void Port::CheckTimeout() {
}
const std::string Port::username_fragment() const {
- if (IsGoogleIce() &&
+ if (!IsStandardIce() &&
component_ == ICE_CANDIDATE_COMPONENT_RTCP) {
// In GICE mode, we should adjust username fragment for rtcp component.
return GetRtcpUfragFromRtpUfrag(ice_username_fragment_);
@@ -997,7 +1019,7 @@ void Connection::OnReadPacket(
// id's match.
case STUN_BINDING_RESPONSE:
case STUN_BINDING_ERROR_RESPONSE:
- if (port_->IceProtocol() == ICEPROTO_GOOGLE ||
+ if (port_->IsGoogleIce() ||
msg->ValidateMessageIntegrity(
data, size, remote_candidate().password())) {
requests_.CheckResponse(msg.get());
diff --git a/talk/p2p/base/port.h b/talk/p2p/base/port.h
index 21f3d61922..1c43c935f2 100644
--- a/talk/p2p/base/port.h
+++ b/talk/p2p/base/port.h
@@ -280,7 +280,8 @@ class Port : public PortInterface, public talk_base::MessageHandler,
// stun username attribute if present.
bool ParseStunUsername(const StunMessage* stun_msg,
std::string* local_username,
- std::string* remote_username) const;
+ std::string* remote_username,
+ IceProtocolType* remote_protocol_type) const;
void CreateStunUsername(const std::string& remote_username,
std::string* stun_username_attr_str) const;
@@ -301,10 +302,8 @@ class Port : public PortInterface, public talk_base::MessageHandler,
// Returns if Google ICE protocol is used.
bool IsGoogleIce() const;
- // Returns default DSCP value.
- talk_base::DiffServCodePoint DefaultDscpValue() const {
- return default_dscp_;
- }
+ // Returns if Hybrid ICE protocol is used.
+ bool IsHybridIce() const;
protected:
enum {
@@ -341,9 +340,10 @@ class Port : public PortInterface, public talk_base::MessageHandler,
// Checks if the address in addr is compatible with the port's ip.
bool IsCompatibleAddress(const talk_base::SocketAddress& addr);
- // Default DSCP value for this port. Set by TransportChannel.
- void SetDefaultDscpValue(talk_base::DiffServCodePoint dscp) {
- default_dscp_ = dscp;
+ // Returns default DSCP value.
+ talk_base::DiffServCodePoint DefaultDscpValue() const {
+ // No change from what MediaChannel set.
+ return talk_base::DSCP_NO_CHANGE;
}
private:
@@ -384,9 +384,6 @@ class Port : public PortInterface, public talk_base::MessageHandler,
IceRole ice_role_;
uint64 tiebreaker_;
bool shared_socket_;
- // DSCP value for ICE/STUN messages. Set by the P2PTransportChannel after
- // port becomes ready.
- talk_base::DiffServCodePoint default_dscp_;
// Information to use when going through a proxy.
std::string user_agent_;
talk_base::ProxyInfo proxy_;
diff --git a/talk/p2p/base/port_unittest.cc b/talk/p2p/base/port_unittest.cc
index 3ea6375b46..000dc5ca3f 100644
--- a/talk/p2p/base/port_unittest.cc
+++ b/talk/p2p/base/port_unittest.cc
@@ -1280,21 +1280,37 @@ TEST_F(PortTest, TestSkipCrossFamilyUdp) {
// This test verifies DSCP value set through SetOption interface can be
// get through DefaultDscpValue.
TEST_F(PortTest, TestDefaultDscpValue) {
+ int dscp;
talk_base::scoped_ptr<UDPPort> udpport(CreateUdpPort(kLocalAddr1));
- udpport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_CS6);
- EXPECT_EQ(talk_base::DSCP_CS6, udpport->DefaultDscpValue());
+ EXPECT_EQ(0, udpport->SetOption(talk_base::Socket::OPT_DSCP,
+ talk_base::DSCP_CS6));
+ EXPECT_EQ(0, udpport->GetOption(talk_base::Socket::OPT_DSCP, &dscp));
talk_base::scoped_ptr<TCPPort> tcpport(CreateTcpPort(kLocalAddr1));
- tcpport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_AF31);
- EXPECT_EQ(talk_base::DSCP_AF31, tcpport->DefaultDscpValue());
+ EXPECT_EQ(0, tcpport->SetOption(talk_base::Socket::OPT_DSCP,
+ talk_base::DSCP_AF31));
+ EXPECT_EQ(0, tcpport->GetOption(talk_base::Socket::OPT_DSCP, &dscp));
+ EXPECT_EQ(talk_base::DSCP_AF31, dscp);
talk_base::scoped_ptr<StunPort> stunport(
CreateStunPort(kLocalAddr1, nat_socket_factory1()));
- stunport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_AF41);
- EXPECT_EQ(talk_base::DSCP_AF41, stunport->DefaultDscpValue());
- talk_base::scoped_ptr<TurnPort> turnport(CreateTurnPort(
+ EXPECT_EQ(0, stunport->SetOption(talk_base::Socket::OPT_DSCP,
+ talk_base::DSCP_AF41));
+ EXPECT_EQ(0, stunport->GetOption(talk_base::Socket::OPT_DSCP, &dscp));
+ EXPECT_EQ(talk_base::DSCP_AF41, dscp);
+ talk_base::scoped_ptr<TurnPort> turnport1(CreateTurnPort(
kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP));
- turnport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_CS7);
- EXPECT_EQ(talk_base::DSCP_CS7, turnport->DefaultDscpValue());
- // TODO(mallinath) - Test DSCP through GetOption.
+ // Socket is created in PrepareAddress.
+ turnport1->PrepareAddress();
+ EXPECT_EQ(0, turnport1->SetOption(talk_base::Socket::OPT_DSCP,
+ talk_base::DSCP_CS7));
+ EXPECT_EQ(0, turnport1->GetOption(talk_base::Socket::OPT_DSCP, &dscp));
+ EXPECT_EQ(talk_base::DSCP_CS7, dscp);
+ // This will verify correct value returned without the socket.
+ talk_base::scoped_ptr<TurnPort> turnport2(CreateTurnPort(
+ kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP));
+ EXPECT_EQ(0, turnport2->SetOption(talk_base::Socket::OPT_DSCP,
+ talk_base::DSCP_CS6));
+ EXPECT_EQ(0, turnport2->GetOption(talk_base::Socket::OPT_DSCP, &dscp));
+ EXPECT_EQ(talk_base::DSCP_CS6, dscp);
}
// Test sending STUN messages in GICE format.
@@ -1665,6 +1681,81 @@ TEST_F(PortTest, TestHandleStunMessageAsIce) {
out_msg->GetErrorCode()->reason());
}
+// This test verifies port can handle ICE messages in Hybrid mode and switches
+// ICEPROTO_RFC5245 mode after successfully handling the message.
+TEST_F(PortTest, TestHandleStunMessageAsIceInHybridMode) {
+ // Our port will act as the "remote" port.
+ talk_base::scoped_ptr<TestPort> port(
+ CreateTestPort(kLocalAddr2, "rfrag", "rpass"));
+ port->SetIceProtocolType(ICEPROTO_HYBRID);
+
+ talk_base::scoped_ptr<IceMessage> in_msg, out_msg;
+ talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer());
+ talk_base::SocketAddress addr(kLocalAddr1);
+ std::string username;
+
+ // BINDING-REQUEST from local to remote with valid ICE username,
+ // MESSAGE-INTEGRITY, and FINGERPRINT.
+ in_msg.reset(CreateStunMessageWithUsername(STUN_BINDING_REQUEST,
+ "rfrag:lfrag"));
+ in_msg->AddMessageIntegrity("rpass");
+ in_msg->AddFingerprint();
+ WriteStunMessage(in_msg.get(), buf.get());
+ EXPECT_TRUE(port->GetStunMessage(buf->Data(), buf->Length(), addr,
+ out_msg.accept(), &username));
+ EXPECT_TRUE(out_msg.get() != NULL);
+ EXPECT_EQ("lfrag", username);
+ EXPECT_EQ(ICEPROTO_RFC5245, port->IceProtocol());
+}
+
+// This test verifies port can handle GICE messages in Hybrid mode and switches
+// ICEPROTO_GOOGLE mode after successfully handling the message.
+TEST_F(PortTest, TestHandleStunMessageAsGiceInHybridMode) {
+ // Our port will act as the "remote" port.
+ talk_base::scoped_ptr<TestPort> port(
+ CreateTestPort(kLocalAddr2, "rfrag", "rpass"));
+ port->SetIceProtocolType(ICEPROTO_HYBRID);
+
+ talk_base::scoped_ptr<IceMessage> in_msg, out_msg;
+ talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer());
+ talk_base::SocketAddress addr(kLocalAddr1);
+ std::string username;
+
+ // BINDING-REQUEST from local to remote with valid GICE username and no M-I.
+ in_msg.reset(CreateStunMessageWithUsername(STUN_BINDING_REQUEST,
+ "rfraglfrag"));
+ WriteStunMessage(in_msg.get(), buf.get());
+ EXPECT_TRUE(port->GetStunMessage(buf->Data(), buf->Length(), addr,
+ out_msg.accept(), &username));
+ EXPECT_TRUE(out_msg.get() != NULL); // Succeeds, since this is GICE.
+ EXPECT_EQ("lfrag", username);
+ EXPECT_EQ(ICEPROTO_GOOGLE, port->IceProtocol());
+}
+
+// Verify port is not switched out of RFC5245 mode if GICE message is received
+// in that mode.
+TEST_F(PortTest, TestHandleStunMessageAsGiceInIceMode) {
+ // Our port will act as the "remote" port.
+ talk_base::scoped_ptr<TestPort> port(
+ CreateTestPort(kLocalAddr2, "rfrag", "rpass"));
+ port->SetIceProtocolType(ICEPROTO_RFC5245);
+
+ talk_base::scoped_ptr<IceMessage> in_msg, out_msg;
+ talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer());
+ talk_base::SocketAddress addr(kLocalAddr1);
+ std::string username;
+
+ // BINDING-REQUEST from local to remote with valid GICE username and no M-I.
+ in_msg.reset(CreateStunMessageWithUsername(STUN_BINDING_REQUEST,
+ "rfraglfrag"));
+ WriteStunMessage(in_msg.get(), buf.get());
+ // Should fail as there is no MI and fingerprint.
+ EXPECT_FALSE(port->GetStunMessage(buf->Data(), buf->Length(), addr,
+ out_msg.accept(), &username));
+ EXPECT_EQ(ICEPROTO_RFC5245, port->IceProtocol());
+}
+
+
// Tests handling of GICE binding requests with missing or incorrect usernames.
TEST_F(PortTest, TestHandleStunMessageAsGiceBadUsername) {
talk_base::scoped_ptr<TestPort> port(
diff --git a/talk/p2p/base/rawtransportchannel.h b/talk/p2p/base/rawtransportchannel.h
index ed38952d56..2042d5f119 100644
--- a/talk/p2p/base/rawtransportchannel.h
+++ b/talk/p2p/base/rawtransportchannel.h
@@ -97,7 +97,10 @@ class RawTransportChannel : public TransportChannelImpl,
virtual IceRole GetIceRole() const { return ICEROLE_UNKNOWN; }
virtual void SetIceRole(IceRole role) {}
virtual void SetIceTiebreaker(uint64 tiebreaker) {}
+
+ virtual bool GetIceProtocolType(IceProtocolType* type) const { return false; }
virtual void SetIceProtocolType(IceProtocolType type) {}
+
virtual void SetIceUfrag(const std::string& ice_ufrag) {}
virtual void SetIcePwd(const std::string& ice_pwd) {}
virtual void SetRemoteIceMode(IceMode mode) {}
diff --git a/talk/p2p/base/relayport.cc b/talk/p2p/base/relayport.cc
index ddfca7114c..9112b1067d 100644
--- a/talk/p2p/base/relayport.cc
+++ b/talk/p2p/base/relayport.cc
@@ -359,14 +359,6 @@ int RelayPort::SendTo(const void* data, size_t size,
int RelayPort::SetOption(talk_base::Socket::Option opt, int value) {
int result = 0;
- // DSCP option is not passed to the socket.
- // TODO(mallinath) - After we have the support on socket,
- // remove this specialization.
- if (opt == talk_base::Socket::OPT_DSCP) {
- SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value));
- return result;
- }
-
for (size_t i = 0; i < entries_.size(); ++i) {
if (entries_[i]->SetSocketOption(opt, value) < 0) {
result = -1;
diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc
index 913f9af5f5..def3c9b4da 100644
--- a/talk/p2p/base/stunport.cc
+++ b/talk/p2p/base/stunport.cc
@@ -230,12 +230,6 @@ int UDPPort::SendTo(const void* data, size_t size,
}
int UDPPort::SetOption(talk_base::Socket::Option opt, int value) {
- // TODO(mallinath) - After we have the support on socket,
- // remove this specialization.
- if (opt == talk_base::Socket::OPT_DSCP) {
- SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value));
- return 0;
- }
return socket_->SetOption(opt, value);
}
diff --git a/talk/p2p/base/tcpport.cc b/talk/p2p/base/tcpport.cc
index 2cca82f194..09812443c1 100644
--- a/talk/p2p/base/tcpport.cc
+++ b/talk/p2p/base/tcpport.cc
@@ -167,14 +167,6 @@ int TCPPort::GetOption(talk_base::Socket::Option opt, int* value) {
}
int TCPPort::SetOption(talk_base::Socket::Option opt, int value) {
- // If we are setting DSCP value, pass value to base Port and return.
- // TODO(mallinath) - After we have the support on socket,
- // remove this specialization.
- if (opt == talk_base::Socket::OPT_DSCP) {
- SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value));
- return 0;
- }
-
if (socket_) {
return socket_->SetOption(opt, value);
} else {
diff --git a/talk/p2p/base/transport.cc b/talk/p2p/base/transport.cc
index d177833ba8..3781b2a542 100644
--- a/talk/p2p/base/transport.cc
+++ b/talk/p2p/base/transport.cc
@@ -208,15 +208,14 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) {
// Push down our transport state to the new channel.
impl->SetIceRole(ice_role_);
impl->SetIceTiebreaker(tiebreaker_);
- if (local_description_) {
- // TODO(ronghuawu): Change CreateChannel_w to be able to return error since
- // below Apply**Description_w calls can fail.
+ // TODO(ronghuawu): Change CreateChannel_w to be able to return error since
+ // below Apply**Description_w calls can fail.
+ if (local_description_)
ApplyLocalTransportDescription_w(impl, NULL);
- if (remote_description_) {
- ApplyRemoteTransportDescription_w(impl, NULL);
- ApplyNegotiatedTransportDescription_w(impl, NULL);
- }
- }
+ if (remote_description_)
+ ApplyRemoteTransportDescription_w(impl, NULL);
+ if (local_description_ && remote_description_)
+ ApplyNegotiatedTransportDescription_w(impl, NULL);
impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState);
impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
@@ -684,6 +683,21 @@ bool Transport::SetRemoteTransportDescription_w(
bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch,
std::string* error_desc) {
+ // If existing protocol_type is HYBRID, we may have not chosen the final
+ // protocol type, so update the channel protocol type from the
+ // local description. Otherwise, skip updating the protocol type.
+ // We check for HYBRID to avoid accidental changes; in the case of a
+ // session renegotiation, the new offer will have the google-ice ICE option,
+ // so we need to make sure we don't switch back from ICE mode to HYBRID
+ // when this happens.
+ // There are some other ways we could have solved this, but this is the
+ // simplest. The ultimate solution will be to get rid of GICE altogether.
+ IceProtocolType protocol_type;
+ if (ch->GetIceProtocolType(&protocol_type) &&
+ protocol_type == ICEPROTO_HYBRID) {
+ ch->SetIceProtocolType(
+ TransportProtocolFromDescription(local_description()));
+ }
ch->SetIceCredentials(local_description_->ice_ufrag,
local_description_->ice_pwd);
return true;
diff --git a/talk/p2p/base/transportchannelimpl.h b/talk/p2p/base/transportchannelimpl.h
index d8432b7323..c66e0c024a 100644
--- a/talk/p2p/base/transportchannelimpl.h
+++ b/talk/p2p/base/transportchannelimpl.h
@@ -54,6 +54,7 @@ class TransportChannelImpl : public TransportChannel {
virtual void SetIceRole(IceRole role) = 0;
virtual void SetIceTiebreaker(uint64 tiebreaker) = 0;
// To toggle G-ICE/ICE.
+ virtual bool GetIceProtocolType(IceProtocolType* type) const = 0;
virtual void SetIceProtocolType(IceProtocolType type) = 0;
// SetIceCredentials only need to be implemented by the ICE
// transport channels. Non-ICE transport channels can just ignore.
diff --git a/talk/p2p/base/turnport.cc b/talk/p2p/base/turnport.cc
index d8c0cb6e29..eed7f8d0c8 100644
--- a/talk/p2p/base/turnport.cc
+++ b/talk/p2p/base/turnport.cc
@@ -304,14 +304,6 @@ Connection* TurnPort::CreateConnection(const Candidate& address,
}
int TurnPort::SetOption(talk_base::Socket::Option opt, int value) {
- // DSCP option is not passed to the socket.
- // TODO(mallinath) - After we have the support on socket,
- // remove this specialization.
- if (opt == talk_base::Socket::OPT_DSCP) {
- SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value));
- return 0;
- }
-
if (!socket_) {
// If socket is not created yet, these options will be applied during socket
// creation.
@@ -322,8 +314,14 @@ int TurnPort::SetOption(talk_base::Socket::Option opt, int value) {
}
int TurnPort::GetOption(talk_base::Socket::Option opt, int* value) {
- if (!socket_)
- return -1;
+ if (!socket_) {
+ SocketOptionsMap::const_iterator it = socket_options_.find(opt);
+ if (it == socket_options_.end()) {
+ return -1;
+ }
+ *value = it->second;
+ return 0;
+ }
return socket_->GetOption(opt, value);
}
diff --git a/talk/p2p/base/turnport_unittest.cc b/talk/p2p/base/turnport_unittest.cc
index 0284f51ccf..e09c196e61 100644
--- a/talk/p2p/base/turnport_unittest.cc
+++ b/talk/p2p/base/turnport_unittest.cc
@@ -179,6 +179,12 @@ class TurnPortTest : public testing::Test,
local_address.ipaddr(), 0, 0,
kIceUfrag1, kIcePwd1,
server_address, credentials));
+ // Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be
+ // in Hybrid mode. Protocol type is necessary to send correct type STUN ping
+ // messages.
+ // This TURN port will be the controlling.
+ turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+ turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING);
turn_port_->SignalPortComplete.connect(this,
&TurnPortTest::OnTurnPortComplete);
turn_port_->SignalPortError.connect(this,
@@ -192,6 +198,10 @@ class TurnPortTest : public testing::Test,
udp_port_.reset(UDPPort::Create(main_, &socket_factory_, &network_,
kLocalAddr2.ipaddr(), 0, 0,
kIceUfrag2, kIcePwd2));
+ // Set protocol type to RFC5245, as turn port is also in same mode.
+ // UDP port will be controlled.
+ udp_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+ udp_port_->SetIceRole(cricket::ICEROLE_CONTROLLED);
udp_port_->SignalPortComplete.connect(
this, &TurnPortTest::OnUdpPortComplete);
}