Update talk to 51664136.
R=mallinath@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/2148004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4649 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/datachannel.cc b/talk/app/webrtc/datachannel.cc
index 8b3f1ac..48a25b8 100644
--- a/talk/app/webrtc/datachannel.cc
+++ b/talk/app/webrtc/datachannel.cc
@@ -324,15 +324,13 @@
data_session_->SignalDataReceived.connect(this, &DataChannel::OnDataReceived);
cricket::StreamParams params =
cricket::StreamParams::CreateLegacy(id());
- data_session_->media_channel()->AddSendStream(params);
- data_session_->media_channel()->AddRecvStream(params);
+ data_session_->AddRecvStream(params);
+ data_session_->AddSendStream(params);
}
void DataChannel::DisconnectFromDataSession() {
- if (data_session_->media_channel() != NULL) {
- data_session_->media_channel()->RemoveSendStream(id());
- data_session_->media_channel()->RemoveRecvStream(id());
- }
+ data_session_->RemoveSendStream(id());
+ data_session_->RemoveRecvStream(id());
data_session_->SignalReadyToSendData.disconnect(this);
data_session_->SignalDataReceived.disconnect(this);
data_session_ = NULL;
diff --git a/talk/app/webrtc/datachannel_unittest.cc b/talk/app/webrtc/datachannel_unittest.cc
index d04378f..4c815aa 100644
--- a/talk/app/webrtc/datachannel_unittest.cc
+++ b/talk/app/webrtc/datachannel_unittest.cc
@@ -29,6 +29,7 @@
#include "talk/app/webrtc/jsep.h"
#include "talk/app/webrtc/mediastreamsignaling.h"
#include "talk/app/webrtc/test/fakeconstraints.h"
+#include "talk/app/webrtc/test/fakedtlsidentityservice.h"
#include "talk/app/webrtc/webrtcsession.h"
#include "talk/base/gunit.h"
#include "talk/media/base/fakemediaengine.h"
@@ -92,7 +93,8 @@
constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels,
true);
- ASSERT_TRUE(session_.Initialize(&constraints, NULL));
+ ASSERT_TRUE(session_.Initialize(&constraints,
+ new FakeIdentityService()));
talk_base::scoped_refptr<CreateSessionDescriptionObserverForTest> observer
= new CreateSessionDescriptionObserverForTest();
session_.CreateOffer(observer.get(), NULL);
@@ -116,7 +118,6 @@
session_.data_channel()->SignalReadyToSendData(true);
}
}
-
cricket::FakeMediaEngine* media_engine_;
cricket::FakeDataEngine* data_engine_;
talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
diff --git a/talk/app/webrtc/localvideosource.cc b/talk/app/webrtc/localvideosource.cc
index 2d43885..9b344da 100644
--- a/talk/app/webrtc/localvideosource.cc
+++ b/talk/app/webrtc/localvideosource.cc
@@ -54,6 +54,9 @@
const char MediaConstraintsInterface::kLeakyBucket[] = "googLeakyBucket";
const char MediaConstraintsInterface::kTemporalLayeredScreencast[] =
"googTemporalLayeredScreencast";
+// TODO(ronghuawu): Remove once cpu overuse detection is stable.
+const char MediaConstraintsInterface::kCpuOveruseDetection[] =
+ "googCpuOveruseDetection";
} // namespace webrtc
@@ -202,7 +205,9 @@
} else if (constraint.key == MediaConstraintsInterface::kNoiseReduction ||
constraint.key == MediaConstraintsInterface::kLeakyBucket ||
constraint.key ==
- MediaConstraintsInterface::kTemporalLayeredScreencast) {
+ MediaConstraintsInterface::kTemporalLayeredScreencast ||
+ constraint.key ==
+ MediaConstraintsInterface::kCpuOveruseDetection) {
// These are actually options, not constraints, so they can be satisfied
// regardless of the format.
return true;
@@ -316,6 +321,9 @@
all_valid &= ExtractOption(all_constraints,
MediaConstraintsInterface::kTemporalLayeredScreencast,
&(options->video_temporal_layer_screencast));
+ all_valid &= ExtractOption(all_constraints,
+ MediaConstraintsInterface::kCpuOveruseDetection,
+ &(options->cpu_overuse_detection));
return all_valid;
}
diff --git a/talk/app/webrtc/localvideosource_unittest.cc b/talk/app/webrtc/localvideosource_unittest.cc
index 24a8588..5ce963f 100644
--- a/talk/app/webrtc/localvideosource_unittest.cc
+++ b/talk/app/webrtc/localvideosource_unittest.cc
@@ -339,6 +339,8 @@
MediaConstraintsInterface::kTemporalLayeredScreencast, "false");
constraints.AddOptional(
MediaConstraintsInterface::kLeakyBucket, "true");
+ constraints.AddOptional(
+ MediaConstraintsInterface::kCpuOveruseDetection, "true");
CreateLocalVideoSource(&constraints);
@@ -350,6 +352,8 @@
EXPECT_FALSE(value);
EXPECT_TRUE(local_source_->options()->video_leaky_bucket.Get(&value));
EXPECT_TRUE(value);
+ EXPECT_TRUE(local_source_->options()->
+ cpu_overuse_detection.GetWithDefaultIfUnset(false));
}
TEST_F(LocalVideoSourceTest, OptionNotSet) {
@@ -357,6 +361,7 @@
CreateLocalVideoSource(&constraints);
bool value;
EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value));
+ EXPECT_FALSE(local_source_->options()->cpu_overuse_detection.Get(&value));
}
TEST_F(LocalVideoSourceTest, MandatoryOptionOverridesOptional) {
diff --git a/talk/app/webrtc/mediaconstraintsinterface.h b/talk/app/webrtc/mediaconstraintsinterface.h
index a6b23c6..b14a0ec 100644
--- a/talk/app/webrtc/mediaconstraintsinterface.h
+++ b/talk/app/webrtc/mediaconstraintsinterface.h
@@ -86,6 +86,7 @@
static const char kLeakyBucket[]; // googLeakyBucket
// googTemporalLayeredScreencast
static const char kTemporalLayeredScreencast[];
+ static const char kCpuOveruseDetection[];
// Constraint keys for CreateOffer / CreateAnswer
// Specified by the W3C PeerConnection spec
diff --git a/talk/app/webrtc/webrtcsdp.cc b/talk/app/webrtc/webrtcsdp.cc
index a3bfa71..516b015 100644
--- a/talk/app/webrtc/webrtcsdp.cc
+++ b/talk/app/webrtc/webrtcsdp.cc
@@ -1316,7 +1316,11 @@
// RFC 4566
// b=AS:<bandwidth>
- if (media_desc->bandwidth() >= 1000) {
+ // We should always use the default bandwidth for RTP-based data
+ // channels. Don't allow SDP to set the bandwidth, because that
+ // would give JS the opportunity to "break the Internet".
+ if (media_desc->bandwidth() >= 1000 &&
+ media_type != cricket::MEDIA_TYPE_DATA) {
InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
AddLine(os.str(), message);
@@ -2105,6 +2109,10 @@
message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
codec_preference, pos, &content_name,
&transport, candidates, error));
+ // We should always use the default bandwidth for RTP-based data
+ // channels. Don't allow SDP to set the bandwidth, because that
+ // would give JS the opportunity to "break the Internet".
+ content->set_bandwidth(cricket::kAutoBandwidth);
} else {
LOG(LS_WARNING) << "Unsupported media type: " << line;
continue;
diff --git a/talk/app/webrtc/webrtcsdp_unittest.cc b/talk/app/webrtc/webrtcsdp_unittest.cc
index 5fa8b0c..7e36cd9 100644
--- a/talk/app/webrtc/webrtcsdp_unittest.cc
+++ b/talk/app/webrtc/webrtcsdp_unittest.cc
@@ -1427,6 +1427,25 @@
EXPECT_EQ(message, expected_sdp);
}
+TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
+ AddRtpDataChannel();
+ data_desc_->set_bandwidth(100*1000);
+ JsepSessionDescription jsep_desc(kDummyString);
+
+ ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
+ std::string message = webrtc::SdpSerialize(jsep_desc);
+
+ std::string expected_sdp = kSdpString;
+ expected_sdp.append(kSdpRtpDataChannelString);
+ // We want to test that serializing data content ignores bandwidth
+ // settings (it should always be the default). Thus, we don't do
+ // the following:
+ // InjectAfter("a=mid:data_content_name\r\n",
+ // "b=AS:100\r\n",
+ // &expected_sdp);
+ EXPECT_EQ(message, expected_sdp);
+}
+
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
AddExtmap();
JsepSessionDescription desc_with_extmap("dummy");
@@ -1739,6 +1758,29 @@
EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
}
+TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {
+ AddRtpDataChannel();
+ JsepSessionDescription jdesc(kDummyString);
+ // We want to test that deserializing data content ignores bandwidth
+ // settings (it should always be the default). Thus, we don't do
+ // the following:
+ // DataContentDescription* dcd = static_cast<DataContentDescription*>(
+ // GetFirstDataContent(&desc_)->description);
+ // dcd->set_bandwidth(100 * 1000);
+ ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
+
+ std::string sdp_with_bandwidth = kSdpString;
+ sdp_with_bandwidth.append(kSdpRtpDataChannelString);
+ InjectAfter("a=mid:data_content_name\r\n",
+ "b=AS:100\r\n",
+ &sdp_with_bandwidth);
+ JsepSessionDescription jdesc_with_bandwidth(kDummyString);
+
+ EXPECT_TRUE(
+ SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
+ EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth));
+}
+
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) {
TestDeserializeExtmap(true, false);
}
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 7016e2a..f9c49cf 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -938,7 +938,6 @@
if (data_channel_.get()) {
channel->SetReceiveSsrc(new_config.id);
channel->SetSendSsrc(new_config.id);
- channel->ConnectToDataSession();
}
if (!config->negotiated) {
talk_base::Buffer *payload = new talk_base::Buffer;
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index 053bcc2..b9a2e5a 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -2484,7 +2484,7 @@
webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
constraints_->AddOptional(
webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, true);
- Init(NULL);
+ Init(new FakeIdentityService());
SetLocalDescriptionWithDataChannel();
EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
@@ -2508,7 +2508,7 @@
webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
constraints_->AddOptional(
webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, true);
- Init(NULL);
+ Init(new FakeIdentityService());
SetLocalDescriptionWithDataChannel();
EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
diff --git a/talk/media/base/fakemediaengine.h b/talk/media/base/fakemediaengine.h
index ded5698..7ef0c9b 100644
--- a/talk/media/base/fakemediaengine.h
+++ b/talk/media/base/fakemediaengine.h
@@ -864,8 +864,6 @@
renderer_ = r;
return true;
}
- bool SetVideoCapturer(VideoCapturer* /*capturer*/) { return true; }
- VideoCapturer* GetVideoCapturer() const { return NULL; }
bool SetCapture(bool capture) {
capture_ = capture;
return true;
diff --git a/talk/media/base/fakevideorenderer.h b/talk/media/base/fakevideorenderer.h
index 4000d5e..362e592 100644
--- a/talk/media/base/fakevideorenderer.h
+++ b/talk/media/base/fakevideorenderer.h
@@ -28,6 +28,7 @@
#ifndef TALK_MEDIA_BASE_FAKEVIDEORENDERER_H_
#define TALK_MEDIA_BASE_FAKEVIDEORENDERER_H_
+#include "talk/base/logging.h"
#include "talk/base/sigslot.h"
#include "talk/media/base/videoframe.h"
#include "talk/media/base/videorenderer.h"
@@ -62,6 +63,13 @@
if (!frame ||
frame->GetWidth() != static_cast<size_t>(width_) ||
frame->GetHeight() != static_cast<size_t>(height_)) {
+ if (!frame) {
+ LOG(LS_WARNING) << "RenderFrame expected non-null frame.";
+ } else {
+ LOG(LS_WARNING) << "RenderFrame expected frame of size " << width_
+ << "x" << height_ << " but received frame of size "
+ << frame->GetWidth() << "x" << frame->GetHeight();
+ }
++errors_;
return false;
}
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index a9e3778..127b2ed 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -236,15 +236,18 @@
adapt_input_to_cpu_usage.SetFrom(change.adapt_input_to_cpu_usage);
adapt_cpu_with_smoothing.SetFrom(change.adapt_cpu_with_smoothing);
adapt_view_switch.SetFrom(change.adapt_view_switch);
+ video_adapt_third.SetFrom(change.video_adapt_third);
video_noise_reduction.SetFrom(change.video_noise_reduction);
video_three_layers.SetFrom(change.video_three_layers);
video_enable_camera_list.SetFrom(change.video_enable_camera_list);
video_one_layer_screencast.SetFrom(change.video_one_layer_screencast);
+ video_one_to_one.SetFrom(change.video_one_to_one);
video_high_bitrate.SetFrom(change.video_high_bitrate);
video_watermark.SetFrom(change.video_watermark);
video_temporal_layer_screencast.SetFrom(
change.video_temporal_layer_screencast);
video_leaky_bucket.SetFrom(change.video_leaky_bucket);
+ cpu_overuse_detection.SetFrom(change.cpu_overuse_detection);
conference_mode.SetFrom(change.conference_mode);
process_adaptation_threshhold.SetFrom(change.process_adaptation_threshhold);
system_low_adaptation_threshhold.SetFrom(
@@ -259,14 +262,17 @@
adapt_input_to_cpu_usage == o.adapt_input_to_cpu_usage &&
adapt_cpu_with_smoothing == o.adapt_cpu_with_smoothing &&
adapt_view_switch == o.adapt_view_switch &&
+ video_adapt_third == o.video_adapt_third &&
video_noise_reduction == o.video_noise_reduction &&
video_three_layers == o.video_three_layers &&
video_enable_camera_list == o.video_enable_camera_list &&
video_one_layer_screencast == o.video_one_layer_screencast &&
+ video_one_to_one == o.video_one_to_one &&
video_high_bitrate == o.video_high_bitrate &&
video_watermark == o.video_watermark &&
video_temporal_layer_screencast == o.video_temporal_layer_screencast &&
video_leaky_bucket == o.video_leaky_bucket &&
+ cpu_overuse_detection == o.cpu_overuse_detection &&
conference_mode == o.conference_mode &&
process_adaptation_threshhold == o.process_adaptation_threshhold &&
system_low_adaptation_threshhold ==
@@ -283,16 +289,18 @@
ost << ToStringIfSet("cpu adaption", adapt_input_to_cpu_usage);
ost << ToStringIfSet("cpu adaptation smoothing", adapt_cpu_with_smoothing);
ost << ToStringIfSet("adapt view switch", adapt_view_switch);
+ ost << ToStringIfSet("video adapt third", video_adapt_third);
ost << ToStringIfSet("noise reduction", video_noise_reduction);
ost << ToStringIfSet("3 layers", video_three_layers);
ost << ToStringIfSet("camera list", video_enable_camera_list);
- ost << ToStringIfSet("1 layer screencast",
- video_one_layer_screencast);
+ ost << ToStringIfSet("1 layer screencast", video_one_layer_screencast);
+ ost << ToStringIfSet("1 to 1", video_one_to_one);
ost << ToStringIfSet("high bitrate", video_high_bitrate);
ost << ToStringIfSet("watermark", video_watermark);
ost << ToStringIfSet("video temporal layer screencast",
video_temporal_layer_screencast);
ost << ToStringIfSet("leaky bucket", video_leaky_bucket);
+ ost << ToStringIfSet("cpu overuse detection", cpu_overuse_detection);
ost << ToStringIfSet("conference mode", conference_mode);
ost << ToStringIfSet("process", process_adaptation_threshhold);
ost << ToStringIfSet("low", system_low_adaptation_threshhold);
@@ -310,6 +318,8 @@
Settable<bool> adapt_cpu_with_smoothing;
// Enable Adapt View Switch?
Settable<bool> adapt_view_switch;
+ // Enable video adapt third?
+ Settable<bool> video_adapt_third;
// Enable denoising?
Settable<bool> video_noise_reduction;
// Experimental: Enable multi layer?
@@ -318,6 +328,8 @@
Settable<bool> video_enable_camera_list;
// Experimental: Enable one layer screencast?
Settable<bool> video_one_layer_screencast;
+ // Experimental: Enable one to one?
+ Settable<bool> video_one_to_one;
// Experimental: Enable WebRtc higher bitrate?
Settable<bool> video_high_bitrate;
// Experimental: Add watermark to the rendered video image.
@@ -326,6 +338,10 @@
Settable<bool> video_temporal_layer_screencast;
// Enable WebRTC leaky bucket when sending media packets.
Settable<bool> video_leaky_bucket;
+ // Enable WebRTC Cpu Overuse Detection, which is a new version of the CPU
+ // adaptation algorithm. So this option will override the
+ // |adapt_input_to_cpu_usage|.
+ Settable<bool> cpu_overuse_detection;
// Use conference mode?
Settable<bool> conference_mode;
// Threshhold for process cpu adaptation. (Process limit)
diff --git a/talk/media/base/testutils.h b/talk/media/base/testutils.h
index 7bc7dc3..4d037b7 100644
--- a/talk/media/base/testutils.h
+++ b/talk/media/base/testutils.h
@@ -28,8 +28,16 @@
#ifndef TALK_MEDIA_BASE_TESTUTILS_H_
#define TALK_MEDIA_BASE_TESTUTILS_H_
+#ifdef LINUX
+#include <X11/Xlib.h>
+// X defines a few macros that stomp on types that gunit.h uses.
+#undef None
+#undef Bool
+#endif
+
#include <string>
#include <vector>
+
#if !defined(DISABLE_YUV)
#include "libyuv/compare.h"
#endif
@@ -237,6 +245,37 @@
return false;
}
+#define MAYBE_SKIP_SCREENCAST_TEST() \
+ if (!cricket::IsScreencastingAvailable()) { \
+ LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \
+ << "X environment for screen capture."; \
+ return; \
+ } \
+
+#ifdef LINUX
+struct XDisplay {
+ XDisplay() : display_(XOpenDisplay(NULL)) { }
+ ~XDisplay() { if (display_) XCloseDisplay(display_); }
+ bool IsValid() const { return display_ != NULL; }
+ operator Display*() { return display_; }
+ private:
+ Display* display_;
+};
+#endif
+
+// Returns true if screencasting is available. When false, anything that uses
+// screencasting features may fail.
+inline bool IsScreencastingAvailable() {
+#ifdef LINUX
+ XDisplay display;
+ if (!display.IsValid()) {
+ LOG(LS_WARNING) << "No X Display available.";
+ return false;
+ }
+#endif
+ return true;
+}
+
} // namespace cricket
#endif // TALK_MEDIA_BASE_TESTUTILS_H_
diff --git a/talk/media/base/videoadapter.cc b/talk/media/base/videoadapter.cc
index cef4248..21da78a 100644
--- a/talk/media/base/videoadapter.cc
+++ b/talk/media/base/videoadapter.cc
@@ -45,86 +45,100 @@
// The seed value for the cpu load moving average.
static const float kCpuLoadInitialAverage = 0.5f;
-// TODO(fbarchard): Consider making scale factor table settable, to allow
-// application to select quality vs performance tradeoff.
-// TODO(fbarchard): Add framerate scaling to tables for 1/2 framerate.
-// List of scale factors that adapter will scale by.
-#if defined(IOS) || defined(ANDROID)
-// Mobile needs 1/4 scale for VGA (640 x 360) to QQVGA (160 x 90)
-// or 1/4 scale for HVGA (480 x 270) to QQHVGA (120 x 67)
-static const int kMinNumPixels = 120 * 67;
-static float kScaleFactors[] = {
- 1.f / 1.f, // Full size.
- 3.f / 4.f, // 3/4 scale.
- 1.f / 2.f, // 1/2 scale.
- 3.f / 8.f, // 3/8 scale.
- 1.f / 4.f, // 1/4 scale.
-};
-#else
// Desktop needs 1/8 scale for HD (1280 x 720) to QQVGA (160 x 90)
-static const int kMinNumPixels = 160 * 100;
-static float kScaleFactors[] = {
- 1.f / 1.f, // Full size.
- 3.f / 4.f, // 3/4 scale.
- 1.f / 2.f, // 1/2 scale.
- 3.f / 8.f, // 3/8 scale.
- 1.f / 4.f, // 1/4 scale.
+static const float kScaleFactors[] = {
+ 1.f / 1.f, // Full size.
+ 3.f / 4.f, // 3/4 scale.
+ 1.f / 2.f, // 1/2 scale.
+ 3.f / 8.f, // 3/8 scale.
+ 1.f / 4.f, // 1/4 scale.
3.f / 16.f, // 3/16 scale.
- 1.f / 8.f // 1/8 scale.
+ 1.f / 8.f, // 1/8 scale.
+ 0.f // End of table.
};
-#endif
-static const int kNumScaleFactors = ARRAY_SIZE(kScaleFactors);
+// TODO(fbarchard): Use this table (optionally) for CPU and GD as well.
+static const float kViewScaleFactors[] = {
+ 1.f / 1.f, // Full size.
+ 3.f / 4.f, // 3/4 scale.
+ 2.f / 3.f, // 2/3 scale. // Allow 1080p to 720p.
+ 1.f / 2.f, // 1/2 scale.
+ 3.f / 8.f, // 3/8 scale.
+ 1.f / 3.f, // 1/3 scale. // Allow 1080p to 360p.
+ 1.f / 4.f, // 1/4 scale.
+ 3.f / 16.f, // 3/16 scale.
+ 1.f / 8.f, // 1/8 scale.
+ 0.f // End of table.
+};
+const float* VideoAdapter::GetViewScaleFactors() const {
+ return scale_third_ ? kViewScaleFactors : kScaleFactors;
+}
+
+// For resolutions that would scale down a little instead of up a little,
+// bias toward scaling up a little. This will tend to choose 3/4 scale instead
+// of 2/3 scale, when the 2/3 is not an exact match.
+static const float kUpBias = -0.9f;
// Find the scale factor that, when applied to width and height, is closest
// to num_pixels.
-float VideoAdapter::FindClosestScale(int width, int height,
- int target_num_pixels) {
+float VideoAdapter::FindScale(const float* scale_factors,
+ const float upbias,
+ int width, int height,
+ int target_num_pixels) {
+ const float kMinNumPixels = 160 * 90;
if (!target_num_pixels) {
return 0.f;
}
- int best_distance = INT_MAX;
- int best_index = kNumScaleFactors - 1; // Default to max scale.
- for (int i = 0; i < kNumScaleFactors; ++i) {
- int test_num_pixels = static_cast<int>(width * kScaleFactors[i] *
- height * kScaleFactors[i]);
- int diff = test_num_pixels - target_num_pixels;
+ float best_distance = static_cast<float>(INT_MAX);
+ float best_scale = 1.f; // Default to unscaled if nothing matches.
+ float pixels = static_cast<float>(width * height);
+ for (int i = 0; ; ++i) {
+ float scale = scale_factors[i];
+ float test_num_pixels = pixels * scale * scale;
+ // Do not consider scale factors that produce too small images.
+ // Scale factor of 0 at end of table will also exit here.
+ if (test_num_pixels < kMinNumPixels) {
+ break;
+ }
+ float diff = target_num_pixels - test_num_pixels;
+ // If resolution is higher than desired, bias the difference based on
+ // preference for slightly larger for nearest, or avoid completely if
+ // looking for lower resolutions only.
if (diff < 0) {
- diff = -diff;
+ diff = diff * kUpBias;
}
if (diff < best_distance) {
best_distance = diff;
- best_index = i;
+ best_scale = scale;
if (best_distance == 0) { // Found exact match.
break;
}
}
}
- return kScaleFactors[best_index];
+ return best_scale;
+}
+
+// Find the closest scale factor.
+float VideoAdapter::FindClosestScale(int width, int height,
+ int target_num_pixels) {
+ return FindScale(kScaleFactors, kUpBias,
+ width, height, target_num_pixels);
+}
+
+// Find the closest view scale factor.
+float VideoAdapter::FindClosestViewScale(int width, int height,
+ int target_num_pixels) {
+ return FindScale(GetViewScaleFactors(), kUpBias,
+ width, height, target_num_pixels);
}
// Finds the scale factor that, when applied to width and height, produces
// fewer than num_pixels.
+static const float kUpAvoidBias = -1000000000.f;
float VideoAdapter::FindLowerScale(int width, int height,
int target_num_pixels) {
- if (!target_num_pixels) {
- return 0.f;
- }
- int best_distance = INT_MAX;
- int best_index = kNumScaleFactors - 1; // Default to max scale.
- for (int i = 0; i < kNumScaleFactors; ++i) {
- int test_num_pixels = static_cast<int>(width * kScaleFactors[i] *
- height * kScaleFactors[i]);
- int diff = target_num_pixels - test_num_pixels;
- if (diff >= 0 && diff < best_distance) {
- best_distance = diff;
- best_index = i;
- if (best_distance == 0) { // Found exact match.
- break;
- }
- }
- }
- return kScaleFactors[best_index];
+ return FindScale(GetViewScaleFactors(), kUpAvoidBias,
+ width, height, target_num_pixels);
}
// There are several frame sizes used by Adapter. This explains them
@@ -147,6 +161,12 @@
// Implementation of VideoAdapter
VideoAdapter::VideoAdapter()
: output_num_pixels_(INT_MAX),
+ scale_third_(false),
+ frames_(0),
+ adapted_frames_(0),
+ adaption_changes_(0),
+ previous_width(0),
+ previous_height(0),
black_output_(false),
is_black_(false),
interval_next_frame_(0) {
@@ -208,6 +228,7 @@
if (!in_frame || !out_frame) {
return false;
}
+ ++frames_;
// Update input to actual frame dimensions.
SetInputFormat(*in_frame);
@@ -236,8 +257,9 @@
return true;
}
+ float scale = 1.f;
if (output_num_pixels_) {
- float scale = VideoAdapter::FindClosestScale(
+ scale = VideoAdapter::FindClosestViewScale(
static_cast<int>(in_frame->GetWidth()),
static_cast<int>(in_frame->GetHeight()),
output_num_pixels_);
@@ -251,9 +273,45 @@
}
*out_frame = output_frame_.get();
+
+ // Show VAdapt log every 300 frames. (10 seconds)
+ // TODO(fbarchard): Consider GetLogSeverity() to change interval to less
+ // for LS_VERBOSE and more for LS_INFO.
+ bool show = frames_ % 300 == 0;
+ if (in_frame->GetWidth() != (*out_frame)->GetWidth() ||
+ in_frame->GetHeight() != (*out_frame)->GetHeight()) {
+ ++adapted_frames_;
+ }
+ // TODO(fbarchard): LOG the previous output resolution and track input
+ // 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())) {
+ show = true;
+ ++adaption_changes_;
+ changed = true;
+ }
+ if (show) {
+ // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
+ // in default calls.
+ LOG(LS_INFO) << "VAdapt Frame: " << adapted_frames_
+ << " / " << frames_
+ << " Changes: " << adaption_changes_
+ << " Input: " << in_frame->GetWidth()
+ << "x" << in_frame->GetHeight()
+ << " Scale: " << scale
+ << " Output: " << (*out_frame)->GetWidth()
+ << "x" << (*out_frame)->GetHeight()
+ << " Changed: " << (changed ? "true" : "false");
+ }
+ previous_width = (*out_frame)->GetWidth();
+ previous_height = (*out_frame)->GetHeight();
+
return true;
}
+// Scale or Blacken the frame. Returns true if successful.
bool VideoAdapter::StretchToOutputFrame(const VideoFrame* in_frame) {
int output_width = output_format_.width;
int output_height = output_format_.height;
@@ -409,37 +467,12 @@
<< " To: " << new_width << "x" << new_height;
}
-// A CPU request for new resolution
-void CoordinatedVideoAdapter::OnCpuLoadUpdated(
- int current_cpus, int max_cpus, float process_load, float system_load) {
+// A Bandwidth GD request for new resolution
+void CoordinatedVideoAdapter::OnCpuResolutionRequest(AdaptRequest request) {
talk_base::CritScope cs(&request_critical_section_);
if (!cpu_adaptation_) {
return;
}
- // Update the moving average of system load. Even if we aren't smoothing,
- // we'll still calculate this information, in case smoothing is later enabled.
- system_load_average_ = kCpuLoadWeightCoefficient * system_load +
- (1.0f - kCpuLoadWeightCoefficient) * system_load_average_;
- 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_)) {
- LOG(LS_VERBOSE) << "VAdapt CPU load high/low but do not adapt until "
- << talk_base::TimeUntil(cpu_adapt_wait_time_) << " ms";
- request = KEEP;
- }
- }
-
// Update how many times we have downgraded due to the cpu load.
switch (request) {
case DOWNGRADE:
@@ -482,13 +515,46 @@
LOG(LS_INFO) << "VAdapt CPU Request: "
<< (DOWNGRADE == request ? "down" :
(UPGRADE == request ? "up" : "keep"))
- << " Process: " << process_load
- << " System: " << system_load
<< " Steps: " << cpu_downgrade_count_
<< " Changed: " << (changed ? "true" : "false")
<< " To: " << new_width << "x" << new_height;
}
+// A CPU request for new resolution
+// TODO(fbarchard): Move outside adapter.
+void CoordinatedVideoAdapter::OnCpuLoadUpdated(
+ int current_cpus, int max_cpus, float process_load, float system_load) {
+ talk_base::CritScope cs(&request_critical_section_);
+ if (!cpu_adaptation_) {
+ return;
+ }
+ // Update the moving average of system load. Even if we aren't smoothing,
+ // we'll still calculate this information, in case smoothing is later enabled.
+ system_load_average_ = kCpuLoadWeightCoefficient * system_load +
+ (1.0f - kCpuLoadWeightCoefficient) * system_load_average_;
+ 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_)) {
+ LOG(LS_VERBOSE) << "VAdapt CPU load high/low but do not adapt until "
+ << talk_base::TimeUntil(cpu_adapt_wait_time_) << " ms";
+ request = KEEP;
+ }
+ }
+
+ OnCpuResolutionRequest(request);
+}
+
// Called by cpu adapter on up requests.
bool CoordinatedVideoAdapter::IsMinimumFormat(int pixels) {
// Find closest scale factor that matches input resolution to min_num_pixels
@@ -522,51 +588,46 @@
input = new_output;
}
int old_num_pixels = GetOutputNumPixels();
- // Find resolution that respects ViewRequest or less pixels.
- int view_desired_num_pixels = view_desired_num_pixels_;
- int min_num_pixels = view_desired_num_pixels_;
- if (!input.IsSize0x0()) {
- float scale = FindLowerScale(input.width, input.height, min_num_pixels);
- min_num_pixels = view_desired_num_pixels =
- static_cast<int>(input.width * input.height * scale * scale + .5f);
- }
- // Reduce resolution further, if necessary, based on encoder bandwidth (GD).
+ int min_num_pixels = INT_MAX;
+ adapt_reason_ = 0;
+
+ // Reduce resolution based on encoder bandwidth (GD).
if (encoder_desired_num_pixels_ &&
(encoder_desired_num_pixels_ < min_num_pixels)) {
+ adapt_reason_ |= ADAPTREASON_BANDWIDTH;
min_num_pixels = encoder_desired_num_pixels_;
}
- // Reduce resolution further, if necessary, based on CPU.
+ // Reduce resolution based on CPU.
if (cpu_adaptation_ && cpu_desired_num_pixels_ &&
- (cpu_desired_num_pixels_ < min_num_pixels)) {
+ (cpu_desired_num_pixels_ <= min_num_pixels)) {
+ if (cpu_desired_num_pixels_ < min_num_pixels) {
+ adapt_reason_ = ADAPTREASON_CPU;
+ } else {
+ adapt_reason_ |= ADAPTREASON_CPU;
+ }
min_num_pixels = cpu_desired_num_pixels_;
}
-
- // Determine which factors are keeping adapter resolution low.
- // Caveat: Does not consider framerate.
- adapt_reason_ = static_cast<AdaptReason>(0);
- if (view_desired_num_pixels == min_num_pixels) {
- adapt_reason_ |= ADAPTREASON_VIEW;
+ // Round resolution for GD or CPU to allow 1/2 to map to 9/16.
+ if (!input.IsSize0x0() && min_num_pixels != INT_MAX) {
+ float scale = FindClosestScale(input.width, input.height, min_num_pixels);
+ min_num_pixels = static_cast<int>(input.width * scale + .5f) *
+ static_cast<int>(input.height * scale + .5f);
}
- if (encoder_desired_num_pixels_ == min_num_pixels) {
- adapt_reason_ |= ADAPTREASON_BANDWIDTH;
+ // Reduce resolution based on View Request.
+ if (view_desired_num_pixels_ <= min_num_pixels) {
+ if (view_desired_num_pixels_ < min_num_pixels) {
+ adapt_reason_ = ADAPTREASON_VIEW;
+ } else {
+ adapt_reason_ |= ADAPTREASON_VIEW;
+ }
+ min_num_pixels = view_desired_num_pixels_;
}
- if (cpu_desired_num_pixels_ == min_num_pixels) {
- adapt_reason_ |= ADAPTREASON_CPU;
- }
-
- // Prevent going below QQVGA.
- if (min_num_pixels > 0 && min_num_pixels < kMinNumPixels) {
- min_num_pixels = kMinNumPixels;
- }
- SetOutputNumPixels(min_num_pixels);
-
- // Find closest scale factor that matches input resolution to min_num_pixels
- // and set that for output resolution. This is not needed for VideoAdapter,
- // but provides feedback to unittests and users on expected resolution.
- // Actual resolution is based on input frame.
+ // Snap to a scale factor.
float scale = 1.0f;
if (!input.IsSize0x0()) {
- scale = FindClosestScale(input.width, input.height, min_num_pixels);
+ scale = FindLowerScale(input.width, input.height, min_num_pixels);
+ min_num_pixels = static_cast<int>(input.width * scale + .5f) *
+ static_cast<int>(input.height * scale + .5f);
}
if (scale == 1.0f) {
adapt_reason_ = 0;
@@ -574,6 +635,8 @@
*new_width = new_output.width = static_cast<int>(input.width * scale + .5f);
*new_height = new_output.height = static_cast<int>(input.height * scale +
.5f);
+ SetOutputNumPixels(min_num_pixels);
+
new_output.interval = view_desired_interval_;
SetOutputFormat(new_output);
int new_num_pixels = GetOutputNumPixels();
diff --git a/talk/media/base/videoadapter.h b/talk/media/base/videoadapter.h
index c41ac6e..12e564d 100644
--- a/talk/media/base/videoadapter.h
+++ b/talk/media/base/videoadapter.h
@@ -65,16 +65,34 @@
// the output frame.
bool AdaptFrame(const VideoFrame* in_frame, const VideoFrame** out_frame);
+ void set_scale_third(bool enable) {
+ LOG(LS_INFO) << "Video Adapter third scaling is now "
+ << (enable ? "enabled" : "disabled");
+ scale_third_ = enable;
+ }
+ bool scale_third() const { return scale_third_; }
+
protected:
float FindClosestScale(int width, int height, int target_num_pixels);
+ float FindClosestViewScale(int width, int height, int target_num_pixels);
float FindLowerScale(int width, int height, int target_num_pixels);
private:
+ const float* GetViewScaleFactors() const;
+ float FindScale(const float* scale_factors,
+ const float upbias, int width, int height,
+ int target_num_pixels);
bool StretchToOutputFrame(const VideoFrame* in_frame);
VideoFormat input_format_;
VideoFormat output_format_;
int output_num_pixels_;
+ bool scale_third_; // True if adapter allows scaling to 1/3 and 2/3.
+ 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.
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_;
@@ -176,6 +194,8 @@
void OnOutputFormatRequest(const VideoFormat& format);
// Handle the resolution request from the encoder due to bandwidth changes.
void OnEncoderResolutionRequest(int width, int height, AdaptRequest request);
+ // Handle the resolution request for CPU overuse.
+ void OnCpuResolutionRequest(AdaptRequest request);
// Handle the CPU load provided by a CPU monitor.
void OnCpuLoadUpdated(int current_cpus, int max_cpus,
float process_load, float system_load);
diff --git a/talk/media/base/videocapturer.cc b/talk/media/base/videocapturer.cc
index 0cef81b..50467c3 100644
--- a/talk/media/base/videocapturer.cc
+++ b/talk/media/base/videocapturer.cc
@@ -107,6 +107,7 @@
SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
scaled_width_ = 0;
scaled_height_ = 0;
+ screencast_max_pixels_ = 0;
muted_ = false;
black_frame_count_down_ = kNumBlackFramesOnMute;
}
@@ -323,11 +324,16 @@
#if !defined(DISABLE_YUV)
if (IsScreencast()) {
int scaled_width, scaled_height;
- int desired_screencast_fps = capture_format_.get() ?
- VideoFormat::IntervalToFps(capture_format_->interval) :
- kDefaultScreencastFps;
- ComputeScale(captured_frame->width, captured_frame->height,
- desired_screencast_fps, &scaled_width, &scaled_height);
+ if (screencast_max_pixels_ > 0) {
+ ComputeScaleMaxPixels(captured_frame->width, captured_frame->height,
+ screencast_max_pixels_, &scaled_width, &scaled_height);
+ } else {
+ int desired_screencast_fps = capture_format_.get() ?
+ VideoFormat::IntervalToFps(capture_format_->interval) :
+ kDefaultScreencastFps;
+ ComputeScale(captured_frame->width, captured_frame->height,
+ desired_screencast_fps, &scaled_width, &scaled_height);
+ }
if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
LOG(LS_VERBOSE) << "Scaling Screencast from "
diff --git a/talk/media/base/videocapturer.h b/talk/media/base/videocapturer.h
index 3997976..dda3bd18 100644
--- a/talk/media/base/videocapturer.h
+++ b/talk/media/base/videocapturer.h
@@ -254,6 +254,17 @@
const VideoProcessors& video_processors() const { return video_processors_; }
+ // If 'screencast_max_pixels' is set greater than zero, screencasts will be
+ // scaled to be no larger than this value.
+ // If set to zero, the max pixels will be limited to
+ // Retina MacBookPro 15" resolution of 2880 x 1800.
+ // For high fps, maximum pixels limit is set based on common 24" monitor
+ // resolution of 2048 x 1280.
+ int screencast_max_pixels() const { return screencast_max_pixels_; }
+ void set_screencast_max_pixels(int p) {
+ screencast_max_pixels_ = talk_base::_max(0, p);
+ }
+
protected:
// Callback attached to SignalFrameCaptured where SignalVideoFrames is called.
void OnFrameCaptured(VideoCapturer* video_capturer,
@@ -313,6 +324,7 @@
bool enable_camera_list_;
int scaled_width_; // Current output size from ComputeScale.
int scaled_height_;
+ int screencast_max_pixels_; // Downscale screencasts further if requested.
bool muted_;
int black_frame_count_down_;
diff --git a/talk/media/base/videocapturer_unittest.cc b/talk/media/base/videocapturer_unittest.cc
index ae461a9..59bc4f7 100644
--- a/talk/media/base/videocapturer_unittest.cc
+++ b/talk/media/base/videocapturer_unittest.cc
@@ -194,6 +194,39 @@
EXPECT_EQ(33, video_frames_received());
}
+TEST_F(VideoCapturerTest, ScreencastScaledMaxPixels) {
+ capturer_.SetScreencast(true);
+
+ int kWidth = 1280;
+ int kHeight = 720;
+
+ // Screencasts usually have large weird dimensions and are ARGB.
+ std::vector<cricket::VideoFormat> formats;
+ formats.push_back(cricket::VideoFormat(kWidth, kHeight,
+ cricket::VideoFormat::FpsToInterval(5), cricket::FOURCC_ARGB));
+ formats.push_back(cricket::VideoFormat(2 * kWidth, 2 * kHeight,
+ cricket::VideoFormat::FpsToInterval(5), cricket::FOURCC_ARGB));
+ capturer_.ResetSupportedFormats(formats);
+
+
+ EXPECT_EQ(0, capturer_.screencast_max_pixels());
+ EXPECT_EQ(cricket::CS_RUNNING, capturer_.Start(cricket::VideoFormat(
+ 2 * kWidth,
+ 2 * kHeight,
+ cricket::VideoFormat::FpsToInterval(30),
+ cricket::FOURCC_ARGB)));
+ EXPECT_TRUE(capturer_.IsRunning());
+ EXPECT_EQ(0, renderer_.num_rendered_frames());
+ renderer_.SetSize(2 * kWidth, 2 * kHeight, 0);
+ EXPECT_TRUE(capturer_.CaptureFrame());
+ EXPECT_EQ(1, renderer_.num_rendered_frames());
+
+ capturer_.set_screencast_max_pixels(kWidth * kHeight);
+ renderer_.SetSize(kWidth, kHeight, 0);
+ EXPECT_TRUE(capturer_.CaptureFrame());
+ EXPECT_EQ(2, renderer_.num_rendered_frames());
+}
+
TEST_F(VideoCapturerTest, TestFourccMatch) {
cricket::VideoFormat desired(640, 480,
cricket::VideoFormat::FpsToInterval(30),
diff --git a/talk/media/base/videocommon.cc b/talk/media/base/videocommon.cc
index 12f3bb3..04bebbe 100644
--- a/talk/media/base/videocommon.cc
+++ b/talk/media/base/videocommon.cc
@@ -98,23 +98,17 @@
return kScaleFactors[best_index];
}
-// Compute a size to scale frames to that is below maximum compression
-// and rendering size with the same aspect ratio.
-void ComputeScale(int frame_width, int frame_height, int fps,
- int* scaled_width, int* scaled_height) {
+// Computes a scale less to fit in max_pixels while maintaining aspect ratio.
+void ComputeScaleMaxPixels(int frame_width, int frame_height, int max_pixels,
+ int* scaled_width, int* scaled_height) {
ASSERT(scaled_width != NULL);
ASSERT(scaled_height != NULL);
+ ASSERT(max_pixels > 0);
// For VP8 the values for max width and height can be found here
// webrtc/src/video_engine/vie_defines.h (kViEMaxCodecWidth and
// kViEMaxCodecHeight)
const int kMaxWidth = 4096;
const int kMaxHeight = 3072;
- // Maximum pixels limit is set to Retina MacBookPro 15" resolution of
- // 2880 x 1800 as of 4/18/2013.
- // For high fps, maximum pixels limit is set based on common 24" monitor
- // resolution of 2048 x 1280 as of 6/13/2013. The Retina resolution is
- // therefore reduced to 1440 x 900.
- int kMaxPixels = (fps > 5) ? 2048 * 1280 : 2880 * 1800;
int new_frame_width = frame_width;
int new_frame_height = frame_height;
@@ -129,12 +123,12 @@
new_frame_height = kMaxHeight;
}
// Limit number of pixels.
- if (new_frame_width * new_frame_height > kMaxPixels) {
+ if (new_frame_width * new_frame_height > max_pixels) {
// Compute new width such that width * height is less than maximum but
// maintains original captured frame aspect ratio.
new_frame_width = static_cast<int>(sqrtf(static_cast<float>(
- kMaxPixels) * new_frame_width / new_frame_height));
- new_frame_height = kMaxPixels / new_frame_width;
+ max_pixels) * new_frame_width / new_frame_height));
+ new_frame_height = max_pixels / new_frame_width;
}
// Snap to a scale factor that is less than or equal to target pixels.
float scale = FindLowerScale(frame_width, frame_height,
@@ -143,6 +137,20 @@
*scaled_height = static_cast<int>(frame_height * scale + .5f);
}
+// Compute a size to scale frames to that is below maximum compression
+// and rendering size with the same aspect ratio.
+void ComputeScale(int frame_width, int frame_height, int fps,
+ int* scaled_width, int* scaled_height) {
+ // Maximum pixels limit is set to Retina MacBookPro 15" resolution of
+ // 2880 x 1800 as of 4/18/2013.
+ // For high fps, maximum pixels limit is set based on common 24" monitor
+ // resolution of 2048 x 1280 as of 6/13/2013. The Retina resolution is
+ // therefore reduced to 1440 x 900.
+ int max_pixels = (fps > 5) ? 2048 * 1280 : 2880 * 1800;
+ ComputeScaleMaxPixels(
+ frame_width, frame_height, max_pixels, scaled_width, scaled_height);
+}
+
// Compute size to crop video frame to.
// If cropped_format_* is 0, return the frame_* size as is.
void ComputeCrop(int cropped_format_width,
@@ -209,6 +217,14 @@
}
}
+// Compute the frame size that makes pixels square pixel aspect ratio.
+void ComputeScaleToSquarePixels(int in_width, int in_height,
+ int pixel_width, int pixel_height,
+ int* scaled_width, int* scaled_height) {
+ *scaled_width = in_width; // Keep width the same.
+ *scaled_height = in_height * pixel_width / pixel_height;
+}
+
// The C++ standard requires a namespace-scope definition of static const
// integral types even when they are initialized in the declaration (see
// [class.static.data]/4), but MSVC with /Ze is non-conforming and treats that
diff --git a/talk/media/base/videocommon.h b/talk/media/base/videocommon.h
index 098651f..cf24f6f 100644
--- a/talk/media/base/videocommon.h
+++ b/talk/media/base/videocommon.h
@@ -25,7 +25,7 @@
//
// Common definition for video, including fourcc and VideoFormat.
-#ifndef TALK_MEDIA_BASE_VIDEOCOMMON_H_
+#ifndef TALK_MEDIA_BASE_VIDEOCOMMON_H_ // NOLINT
#define TALK_MEDIA_BASE_VIDEOCOMMON_H_
#include <string>
@@ -147,6 +147,15 @@
return name;
}
+// Computes a scale less to fit in max_pixels while maintaining aspect ratio.
+void ComputeScaleMaxPixels(int frame_width, int frame_height, int max_pixels,
+ int* scaled_width, int* scaled_height);
+
+// For low fps, max pixels limit is set to Retina MacBookPro 15" resolution of
+// 2880 x 1800 as of 4/18/2013.
+// For high fps, maximum pixels limit is set based on common 24" monitor
+// resolution of 2048 x 1280 as of 6/13/2013. The Retina resolution is
+// therefore reduced to 1440 x 900.
void ComputeScale(int frame_width, int frame_height, int fps,
int* scaled_width, int* scaled_height);
@@ -158,6 +167,11 @@
int rotation,
int* cropped_width, int* cropped_height);
+// Compute the frame size that makes pixels square pixel aspect ratio.
+void ComputeScaleToSquarePixels(int in_width, int in_height,
+ int pixel_width, int pixel_height,
+ int* scaled_width, int* scaled_height);
+
//////////////////////////////////////////////////////////////////////////////
// Definition of VideoFormat.
//////////////////////////////////////////////////////////////////////////////
@@ -239,4 +253,4 @@
} // namespace cricket
-#endif // TALK_MEDIA_BASE_VIDEOCOMMON_H_
+#endif // TALK_MEDIA_BASE_VIDEOCOMMON_H_ // NOLINT
diff --git a/talk/media/base/videocommon_unittest.cc b/talk/media/base/videocommon_unittest.cc
index e9cd26a..a4aa395 100644
--- a/talk/media/base/videocommon_unittest.cc
+++ b/talk/media/base/videocommon_unittest.cc
@@ -287,4 +287,16 @@
EXPECT_EQ(768, cropped_height);
}
+TEST(VideoCommonTest, TestComputeScaleToSquarePixels) {
+ int scaled_width, scaled_height;
+
+ // Pixel aspect ratio is 4:3. Logical aspect ratio is 16:9. Expect scale
+ // to square pixels with physical aspect ratio of 16:9.
+ ComputeScaleToSquarePixels(640, 270,
+ 4, 3, // 4 x 3 pixel aspect ratio
+ &scaled_width, &scaled_height);
+ EXPECT_EQ(640, scaled_width);
+ EXPECT_EQ(360, scaled_height);
+}
+
} // namespace cricket
diff --git a/talk/media/base/videoengine_unittest.h b/talk/media/base/videoengine_unittest.h
index 00ae4ba..d8b9bcb 100644
--- a/talk/media/base/videoengine_unittest.h
+++ b/talk/media/base/videoengine_unittest.h
@@ -23,7 +23,7 @@
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef TALK_MEDIA_BASE_VIDEOENGINE_UNITTEST_H_
+#ifndef TALK_MEDIA_BASE_VIDEOENGINE_UNITTEST_H_ // NOLINT
#define TALK_MEDIA_BASE_VIDEOENGINE_UNITTEST_H_
#include <string>
@@ -1667,4 +1667,4 @@
cricket::FakeVideoRenderer renderer2_;
};
-#endif // TALK_MEDIA_BASE_VIDEOENGINE_UNITTEST_H_
+#endif // TALK_MEDIA_BASE_VIDEOENGINE_UNITTEST_H_ NOLINT
diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc
index 873b249..5e6ca9e 100644
--- a/talk/media/webrtc/webrtcvideoengine.cc
+++ b/talk/media/webrtc/webrtcvideoengine.cc
@@ -465,6 +465,48 @@
DecoderMap registered_decoders_;
};
+#ifdef USE_WEBRTC_DEV_BRANCH
+class WebRtcOveruseObserver : public webrtc::CpuOveruseObserver {
+ public:
+ explicit WebRtcOveruseObserver(CoordinatedVideoAdapter* video_adapter)
+ : video_adapter_(video_adapter),
+ enabled_(false) {
+ }
+
+ // TODO(mflodman): Consider sending resolution as part of event, to let
+ // adapter know what resolution the request is based on. Helps eliminate stale
+ // data, race conditions.
+ virtual void OveruseDetected() OVERRIDE {
+ talk_base::CritScope cs(&crit_);
+ if (!enabled_) {
+ return;
+ }
+
+ video_adapter_->OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE);
+ }
+
+ virtual void NormalUsage() OVERRIDE {
+ talk_base::CritScope cs(&crit_);
+ if (!enabled_) {
+ return;
+ }
+
+ video_adapter_->OnCpuResolutionRequest(CoordinatedVideoAdapter::UPGRADE);
+ }
+
+ void Enable(bool enable) {
+ talk_base::CritScope cs(&crit_);
+ enabled_ = enable;
+ }
+
+ private:
+ CoordinatedVideoAdapter* video_adapter_;
+ bool enabled_;
+ talk_base::CriticalSection crit_;
+};
+
+#endif
+
class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
public:
typedef std::map<int, webrtc::VideoEncoder*> EncoderMap; // key: payload type
@@ -481,6 +523,9 @@
capturer_updated_(false),
interval_(0),
video_adapter_(new CoordinatedVideoAdapter) {
+#ifdef USE_WEBRTC_DEV_BRANCH
+ overuse_observer_.reset(new WebRtcOveruseObserver(video_adapter_.get()));
+#endif
SignalCpuAdaptationUnable.repeat(video_adapter_->SignalCpuAdaptationUnable);
if (cpu_monitor) {
cpu_monitor->SignalUpdate.connect(
@@ -534,6 +579,11 @@
int CurrentAdaptReason() const {
return video_adapter_->adapt_reason();
}
+#ifdef USE_WEBRTC_DEV_BRANCH
+ webrtc::CpuOveruseObserver* overuse_observer() {
+ return overuse_observer_.get();
+ }
+#endif
StreamParams* stream_params() { return stream_params_.get(); }
void set_stream_params(const StreamParams& sp) {
@@ -572,7 +622,7 @@
}
void ApplyCpuOptions(const VideoOptions& options) {
- bool cpu_adapt, cpu_smoothing;
+ 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);
@@ -589,7 +639,18 @@
if (options.system_high_adaptation_threshhold.Get(&high)) {
video_adapter_->set_high_system_threshold(high);
}
+ if (options.video_adapt_third.Get(&adapt_third)) {
+ video_adapter_->set_scale_third(adapt_third);
+ }
}
+
+ void SetCpuOveruseDetection(bool enable) {
+#ifdef USE_WEBRTC_DEV_BRANCH
+ overuse_observer_->Enable(enable);
+ video_adapter_->set_cpu_adaptation(enable);
+#endif
+ }
+
void ProcessFrame(const VideoFrame& original_frame, bool mute,
VideoFrame** processed_frame) {
if (!mute) {
@@ -642,6 +703,9 @@
int64 interval_;
talk_base::scoped_ptr<CoordinatedVideoAdapter> video_adapter_;
+#ifdef USE_WEBRTC_DEV_BRANCH
+ talk_base::scoped_ptr<WebRtcOveruseObserver> overuse_observer_;
+#endif
};
const WebRtcVideoEngine::VideoCodecPref
@@ -2481,6 +2545,9 @@
bool buffer_latency_changed = options.buffered_mode_latency.IsSet() &&
(options_.buffered_mode_latency != options.buffered_mode_latency);
+ bool cpu_overuse_detection_changed = options.cpu_overuse_detection.IsSet() &&
+ (options_.cpu_overuse_detection != options.cpu_overuse_detection);
+
bool conference_mode_turned_off = false;
if (options_.conference_mode.IsSet() && options.conference_mode.IsSet() &&
options_.conference_mode.GetWithDefaultIfUnset(false) &&
@@ -2558,6 +2625,15 @@
}
}
}
+ if (cpu_overuse_detection_changed) {
+ bool cpu_overuse_detection =
+ options_.cpu_overuse_detection.GetWithDefaultIfUnset(false);
+ for (SendChannelMap::iterator iter = send_channels_.begin();
+ iter != send_channels_.end(); ++iter) {
+ WebRtcVideoChannelSendInfo* send_channel = iter->second;
+ send_channel->SetCpuOveruseDetection(cpu_overuse_detection);
+ }
+ }
return true;
}
@@ -2702,8 +2778,8 @@
frame_i420.y_pitch = frame_out->GetYPitch();
frame_i420.u_pitch = frame_out->GetUPitch();
frame_i420.v_pitch = frame_out->GetVPitch();
- frame_i420.width = static_cast<unsigned short>(frame_out->GetWidth());
- frame_i420.height = static_cast<unsigned short>(frame_out->GetHeight());
+ frame_i420.width = static_cast<uint16>(frame_out->GetWidth());
+ frame_i420.height = static_cast<uint16>(frame_out->GetHeight());
int64 timestamp_ntp_ms = 0;
// TODO(justinlin): Reenable after Windows issues with clock drift are fixed.
@@ -2966,10 +3042,21 @@
new WebRtcVideoChannelSendInfo(channel_id, vie_capture,
external_capture,
engine()->cpu_monitor()));
+#ifdef USE_WEBRTC_DEV_BRANCH
+ if (engine()->vie()->base()->RegisterCpuOveruseObserver(
+ channel_id, send_channel->overuse_observer())) {
+ LOG_RTCERR1(RegisterCpuOveruseObserver, channel_id);
+ return false;
+ }
+#endif
send_channel->ApplyCpuOptions(options_);
send_channel->SignalCpuAdaptationUnable.connect(this,
&WebRtcVideoMediaChannel::OnCpuAdaptationUnable);
+ if (options_.cpu_overuse_detection.GetWithDefaultIfUnset(false)) {
+ send_channel->SetCpuOveruseDetection(true);
+ }
+
// Register encoder observer for outgoing framerate and bitrate.
if (engine()->vie()->codec()->RegisterEncoderObserver(
channel_id, *send_channel->encoder_observer()) != 0) {
diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc
index 840fcdd..86f9314 100644
--- a/talk/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc
@@ -86,8 +86,7 @@
// Test fixture to test WebRtcVideoEngine with a fake webrtc::VideoEngine.
// Useful for testing failure paths.
-class WebRtcVideoEngineTestFake :
- public testing::Test,
+class WebRtcVideoEngineTestFake : public testing::Test,
public sigslot::has_slots<> {
public:
WebRtcVideoEngineTestFake()
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index 855a9e4..580ecaa 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -1632,18 +1632,11 @@
}
bool WebRtcVoiceMediaChannel::SetSendCodecs(
- const std::vector<AudioCodec>& codecs) {
- // TODO(xians): Break down this function into SetSendCodecs(channel, codecs)
- // to support per-channel codecs.
-
- // Disable DTMF, VAD, and FEC unless we know the other side wants them.
- dtmf_allowed_ = false;
- for (ChannelMap::iterator iter = send_channels_.begin();
- iter != send_channels_.end(); ++iter) {
- engine()->voe()->codec()->SetVADStatus(iter->second.channel, false);
- engine()->voe()->rtp()->SetNACKStatus(iter->second.channel, false, 0);
- engine()->voe()->rtp()->SetFECStatus(iter->second.channel, false);
- }
+ int channel, const std::vector<AudioCodec>& codecs) {
+ // Disable VAD, and FEC unless we know the other side wants them.
+ engine()->voe()->codec()->SetVADStatus(channel, false);
+ engine()->voe()->rtp()->SetNACKStatus(channel, false, 0);
+ engine()->voe()->rtp()->SetFECStatus(channel, false);
// Scan through the list to figure out the codec to use for sending, along
// with the proper configuration for VAD and DTMF.
@@ -1700,16 +1693,11 @@
// about it.
if (_stricmp(it->name.c_str(), "telephone-event") == 0 ||
_stricmp(it->name.c_str(), "audio/telephone-event") == 0) {
- for (ChannelMap::iterator iter = send_channels_.begin();
- iter != send_channels_.end(); ++iter) {
- if (engine()->voe()->dtmf()->SetSendTelephoneEventPayloadType(
- iter->second.channel, it->id) == -1) {
- LOG_RTCERR2(SetSendTelephoneEventPayloadType,
- iter->second.channel, it->id);
- return false;
- }
+ if (engine()->voe()->dtmf()->SetSendTelephoneEventPayloadType(
+ channel, it->id) == -1) {
+ LOG_RTCERR2(SetSendTelephoneEventPayloadType, channel, it->id);
+ return false;
}
- dtmf_allowed_ = true;
}
// Turn voice activity detection/comfort noise on if supported.
@@ -1732,35 +1720,30 @@
<< " not supported.";
continue;
}
- // Loop through the existing send channels and set the CN payloadtype
- // and the VAD status.
- for (ChannelMap::iterator iter = send_channels_.begin();
- iter != send_channels_.end(); ++iter) {
- int channel = iter->second.channel;
- // The CN payload type for 8000 Hz clockrate is fixed at 13.
- if (cn_freq != webrtc::kFreq8000Hz) {
- if (engine()->voe()->codec()->SetSendCNPayloadType(
- channel, it->id, cn_freq) == -1) {
- LOG_RTCERR3(SetSendCNPayloadType, channel, it->id, cn_freq);
- // TODO(ajm): This failure condition will be removed from VoE.
- // Restore the return here when we update to a new enough webrtc.
- //
- // Not returning false because the SetSendCNPayloadType will fail if
- // the channel is already sending.
- // This can happen if the remote description is applied twice, for
- // example in the case of ROAP on top of JSEP, where both side will
- // send the offer.
- }
+ // Set the CN payloadtype and the VAD status.
+ // The CN payload type for 8000 Hz clockrate is fixed at 13.
+ if (cn_freq != webrtc::kFreq8000Hz) {
+ if (engine()->voe()->codec()->SetSendCNPayloadType(
+ channel, it->id, cn_freq) == -1) {
+ LOG_RTCERR3(SetSendCNPayloadType, channel, it->id, cn_freq);
+ // TODO(ajm): This failure condition will be removed from VoE.
+ // Restore the return here when we update to a new enough webrtc.
+ //
+ // Not returning false because the SetSendCNPayloadType will fail if
+ // the channel is already sending.
+ // This can happen if the remote description is applied twice, for
+ // example in the case of ROAP on top of JSEP, where both side will
+ // send the offer.
}
+ }
- // Only turn on VAD if we have a CN payload type that matches the
- // clockrate for the codec we are going to use.
- if (it->clockrate == send_codec.plfreq) {
- LOG(LS_INFO) << "Enabling VAD";
- if (engine()->voe()->codec()->SetVADStatus(channel, true) == -1) {
- LOG_RTCERR2(SetVADStatus, channel, true);
- return false;
- }
+ // Only turn on VAD if we have a CN payload type that matches the
+ // clockrate for the codec we are going to use.
+ if (it->clockrate == send_codec.plfreq) {
+ LOG(LS_INFO) << "Enabling VAD";
+ if (engine()->voe()->codec()->SetVADStatus(channel, true) == -1) {
+ LOG_RTCERR2(SetVADStatus, channel, true);
+ return false;
}
}
}
@@ -1780,28 +1763,22 @@
// Enable redundant encoding of the specified codec. Treat any
// failure as a fatal internal error.
LOG(LS_INFO) << "Enabling FEC";
- for (ChannelMap::iterator iter = send_channels_.begin();
- iter != send_channels_.end(); ++iter) {
- if (engine()->voe()->rtp()->SetFECStatus(iter->second.channel,
- true, it->id) == -1) {
- LOG_RTCERR3(SetFECStatus, iter->second.channel, true, it->id);
- return false;
- }
+ if (engine()->voe()->rtp()->SetFECStatus(channel, true, it->id) == -1) {
+ LOG_RTCERR3(SetFECStatus, channel, true, it->id);
+ return false;
}
} else {
send_codec = voe_codec;
nack_enabled_ = IsNackEnabled(*it);
- SetNack(send_channels_, nack_enabled_);
+ SetNack(channel, nack_enabled_);
}
first = false;
// Set the codec immediately, since SetVADStatus() depends on whether
// the current codec is mono or stereo.
- if (!SetSendCodec(send_codec))
+ if (!SetSendCodec(channel, send_codec))
return false;
}
}
- SetNack(receive_channels_, nack_enabled_);
-
// If we're being asked to set an empty list of codecs, due to a buggy client,
// choose the most common format: PCMU
@@ -1809,10 +1786,39 @@
LOG(LS_WARNING) << "Received empty list of codecs; using PCMU/8000";
AudioCodec codec(0, "PCMU", 8000, 0, 1, 0);
engine()->FindWebRtcCodec(codec, &send_codec);
- if (!SetSendCodec(send_codec))
+ if (!SetSendCodec(channel, send_codec))
return false;
}
+ // Always update the |send_codec_| to the currently set send codec.
+ send_codec_.reset(new webrtc::CodecInst(send_codec));
+
+ return true;
+}
+
+bool WebRtcVoiceMediaChannel::SetSendCodecs(
+ const std::vector<AudioCodec>& codecs) {
+ dtmf_allowed_ = false;
+ for (std::vector<AudioCodec>::const_iterator it = codecs.begin();
+ it != codecs.end(); ++it) {
+ // Find the DTMF telephone event "codec".
+ if (_stricmp(it->name.c_str(), "telephone-event") == 0 ||
+ _stricmp(it->name.c_str(), "audio/telephone-event") == 0) {
+ dtmf_allowed_ = true;
+ }
+ }
+
+ // Cache the codecs in order to configure the channel created later.
+ send_codecs_ = codecs;
+ for (ChannelMap::iterator iter = send_channels_.begin();
+ iter != send_channels_.end(); ++iter) {
+ if (!SetSendCodecs(iter->second.channel, codecs)) {
+ return false;
+ }
+ }
+
+ SetNack(receive_channels_, nack_enabled_);
+
return true;
}
@@ -1820,17 +1826,16 @@
bool nack_enabled) {
for (ChannelMap::const_iterator it = channels.begin();
it != channels.end(); ++it) {
- SetNack(it->first, it->second.channel, nack_enabled_);
+ SetNack(it->second.channel, nack_enabled);
}
}
-void WebRtcVoiceMediaChannel::SetNack(uint32 ssrc, int channel,
- bool nack_enabled) {
+void WebRtcVoiceMediaChannel::SetNack(int channel, bool nack_enabled) {
if (nack_enabled) {
- LOG(LS_INFO) << "Enabling NACK for stream " << ssrc;
+ LOG(LS_INFO) << "Enabling NACK for channel " << channel;
engine()->voe()->rtp()->SetNACKStatus(channel, true, kNackMaxPackets);
} else {
- LOG(LS_INFO) << "Disabling NACK for stream " << ssrc;
+ LOG(LS_INFO) << "Disabling NACK for channel " << channel;
engine()->voe()->rtp()->SetNACKStatus(channel, false, 0);
}
}
@@ -1845,10 +1850,6 @@
return false;
}
- // All SetSendCodec calls were successful. Update the global state
- // accordingly.
- send_codec_.reset(new webrtc::CodecInst(send_codec));
-
return true;
}
@@ -2098,8 +2099,8 @@
return false;
}
- // Set the current codec to be used for the new channel.
- if (send_codec_ && !SetSendCodec(channel, *send_codec_))
+ // Set the current codecs to be used for the new channel.
+ if (!send_codecs_.empty() && !SetSendCodecs(channel, send_codecs_))
return false;
return ChangeSend(channel, desired_send_);
@@ -2223,7 +2224,7 @@
SetPlayout(voe_channel(), false);
}
}
- SetNack(ssrc, channel, nack_enabled_);
+ SetNack(channel, nack_enabled_);
receive_channels_.insert(
std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL)));
@@ -2547,7 +2548,24 @@
// Send the event.
if (flags & cricket::DF_SEND) {
- int channel = (ssrc == 0) ? voe_channel() : GetSendChannelNum(ssrc);
+ int channel = -1;
+ if (ssrc == 0) {
+ bool default_channel_is_inuse = false;
+ for (ChannelMap::const_iterator iter = send_channels_.begin();
+ iter != send_channels_.end(); ++iter) {
+ if (IsDefaultChannel(iter->second.channel)) {
+ default_channel_is_inuse = true;
+ break;
+ }
+ }
+ if (default_channel_is_inuse) {
+ channel = voe_channel();
+ } else if (!send_channels_.empty()) {
+ channel = send_channels_.begin()->second.channel;
+ }
+ } else {
+ channel = GetSendChannelNum(ssrc);
+ }
if (channel == -1) {
LOG(LS_WARNING) << "InsertDtmf - The specified ssrc "
<< ssrc << " is not in use.";
diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h
index 0c2b613..a878711 100644
--- a/talk/media/webrtc/webrtcvoiceengine.h
+++ b/talk/media/webrtc/webrtcvoiceengine.h
@@ -377,7 +377,7 @@
struct WebRtcVoiceChannelInfo;
typedef std::map<uint32, WebRtcVoiceChannelInfo> ChannelMap;
- void SetNack(uint32 ssrc, int channel, bool nack_enabled);
+ void SetNack(int channel, bool nack_enabled);
void SetNack(const ChannelMap& channels, bool nack_enabled);
bool SetSendCodec(const webrtc::CodecInst& send_codec);
bool SetSendCodec(int channel, const webrtc::CodecInst& send_codec);
@@ -392,10 +392,12 @@
bool IsDefaultChannel(int channel_id) const {
return channel_id == voe_channel();
}
+ bool SetSendCodecs(int channel, const std::vector<AudioCodec>& codecs);
talk_base::scoped_ptr<WebRtcSoundclipStream> ringback_tone_;
std::set<int> ringback_channels_; // channels playing ringback
std::vector<AudioCodec> recv_codecs_;
+ std::vector<AudioCodec> send_codecs_;
talk_base::scoped_ptr<webrtc::CodecInst> send_codec_;
AudioOptions options_;
bool dtmf_allowed_;
diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
index 051a026..39d489a 100644
--- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -143,7 +143,18 @@
engine_.Terminate();
}
- void TestInsertDtmf(uint32 ssrc, int channel_id) {
+ void TestInsertDtmf(uint32 ssrc, bool caller) {
+ EXPECT_TRUE(engine_.Init(talk_base::Thread::Current()));
+ channel_ = engine_.CreateChannel();
+ EXPECT_TRUE(channel_ != NULL);
+ if (caller) {
+ // if this is a caller, local description will be applied and add the
+ // send stream.
+ EXPECT_TRUE(channel_->AddSendStream(
+ cricket::StreamParams::CreateLegacy(kSsrc1)));
+ }
+ int channel_id = voe_.GetLastChannel();
+
// Test we can only InsertDtmf when the other side supports telephone-event.
std::vector<cricket::AudioCodec> codecs;
codecs.push_back(kPcmuCodec);
@@ -154,6 +165,14 @@
codecs.push_back(kTelephoneEventCodec);
EXPECT_TRUE(channel_->SetSendCodecs(codecs));
EXPECT_TRUE(channel_->CanInsertDtmf());
+
+ if (!caller) {
+ // There's no active send channel yet.
+ EXPECT_FALSE(channel_->InsertDtmf(ssrc, 2, 123, cricket::DF_SEND));
+ EXPECT_TRUE(channel_->AddSendStream(
+ cricket::StreamParams::CreateLegacy(kSsrc1)));
+ }
+
// Check we fail if the ssrc is invalid.
EXPECT_FALSE(channel_->InsertDtmf(-1, 1, 111, cricket::DF_SEND));
@@ -923,8 +942,8 @@
EXPECT_EQ(200000, gcodec.rate);
}
-// Test that we can enable NACK with opus.
-TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecEnableNack) {
+// Test that we can enable NACK with opus as caller.
+TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecEnableNackAsCaller) {
EXPECT_TRUE(SetupEngine());
int channel_num = voe_.GetLastChannel();
std::vector<cricket::AudioCodec> codecs;
@@ -936,6 +955,26 @@
EXPECT_TRUE(voe_.GetNACK(channel_num));
}
+// Test that we can enable NACK with opus as callee.
+TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecEnableNackAsCallee) {
+ EXPECT_TRUE(engine_.Init(talk_base::Thread::Current()));
+ channel_ = engine_.CreateChannel();
+ EXPECT_TRUE(channel_ != NULL);
+
+ int channel_num = voe_.GetLastChannel();
+ std::vector<cricket::AudioCodec> codecs;
+ codecs.push_back(kOpusCodec);
+ codecs[0].AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack,
+ cricket::kParamValueEmpty));
+ EXPECT_FALSE(voe_.GetNACK(channel_num));
+ EXPECT_TRUE(channel_->SetSendCodecs(codecs));
+ EXPECT_FALSE(voe_.GetNACK(channel_num));
+
+ EXPECT_TRUE(channel_->AddSendStream(
+ cricket::StreamParams::CreateLegacy(kSsrc1)));
+ EXPECT_TRUE(voe_.GetNACK(channel_num));
+}
+
// Test that we can enable NACK on receive streams.
TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecEnableNackRecvStreams) {
EXPECT_TRUE(SetupEngine());
@@ -1136,8 +1175,8 @@
EXPECT_EQ(106, voe_.GetSendTelephoneEventPayloadType(channel_num));
}
-// Test that we set VAD and DTMF types correctly.
-TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMF) {
+// Test that we set VAD and DTMF types correctly as caller.
+TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCaller) {
EXPECT_TRUE(SetupEngine());
int channel_num = voe_.GetLastChannel();
std::vector<cricket::AudioCodec> codecs;
@@ -1163,6 +1202,39 @@
EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num));
}
+// Test that we set VAD and DTMF types correctly as callee.
+TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCallee) {
+ EXPECT_TRUE(engine_.Init(talk_base::Thread::Current()));
+ channel_ = engine_.CreateChannel();
+ EXPECT_TRUE(channel_ != NULL);
+
+ int channel_num = voe_.GetLastChannel();
+ std::vector<cricket::AudioCodec> codecs;
+ codecs.push_back(kIsacCodec);
+ codecs.push_back(kPcmuCodec);
+ // TODO(juberti): cn 32000
+ codecs.push_back(kCn16000Codec);
+ codecs.push_back(kCn8000Codec);
+ codecs.push_back(kTelephoneEventCodec);
+ codecs.push_back(kRedCodec);
+ codecs[0].id = 96;
+ codecs[2].id = 97; // wideband CN
+ codecs[4].id = 98; // DTMF
+ EXPECT_TRUE(channel_->SetSendCodecs(codecs));
+ EXPECT_TRUE(channel_->AddSendStream(
+ cricket::StreamParams::CreateLegacy(kSsrc1)));
+
+ webrtc::CodecInst gcodec;
+ EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec));
+ EXPECT_EQ(96, gcodec.pltype);
+ EXPECT_STREQ("ISAC", gcodec.plname);
+ EXPECT_TRUE(voe_.GetVAD(channel_num));
+ EXPECT_FALSE(voe_.GetFEC(channel_num));
+ EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false));
+ EXPECT_EQ(97, voe_.GetSendCNPayloadType(channel_num, true));
+ EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num));
+}
+
// Test that we only apply VAD if we have a CN codec that matches the
// send codec clockrate.
TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNNoMatch) {
@@ -1227,8 +1299,8 @@
EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num));
}
-// Test that we set up FEC correctly.
-TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsRED) {
+// Test that we set up FEC correctly as caller.
+TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCaller) {
EXPECT_TRUE(SetupEngine());
int channel_num = voe_.GetLastChannel();
std::vector<cricket::AudioCodec> codecs;
@@ -1247,6 +1319,31 @@
EXPECT_EQ(127, voe_.GetSendFECPayloadType(channel_num));
}
+// Test that we set up FEC correctly as callee.
+TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCallee) {
+ EXPECT_TRUE(engine_.Init(talk_base::Thread::Current()));
+ channel_ = engine_.CreateChannel();
+ EXPECT_TRUE(channel_ != NULL);
+
+ int channel_num = voe_.GetLastChannel();
+ std::vector<cricket::AudioCodec> codecs;
+ codecs.push_back(kRedCodec);
+ codecs.push_back(kIsacCodec);
+ codecs.push_back(kPcmuCodec);
+ codecs[0].id = 127;
+ codecs[0].params[""] = "96/96";
+ codecs[1].id = 96;
+ EXPECT_TRUE(channel_->SetSendCodecs(codecs));
+ EXPECT_TRUE(channel_->AddSendStream(
+ cricket::StreamParams::CreateLegacy(kSsrc1)));
+ webrtc::CodecInst gcodec;
+ EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec));
+ EXPECT_EQ(96, gcodec.pltype);
+ EXPECT_STREQ("ISAC", gcodec.plname);
+ EXPECT_TRUE(voe_.GetFEC(channel_num));
+ EXPECT_EQ(127, voe_.GetSendFECPayloadType(channel_num));
+}
+
// Test that we set up FEC correctly if params are omitted.
TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDNoParams) {
EXPECT_TRUE(SetupEngine());
@@ -1947,18 +2044,24 @@
EXPECT_EQ(0, voe_.GetNumChannels());
}
-// Test the InsertDtmf on default send stream.
-TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStream) {
- EXPECT_TRUE(SetupEngine());
- int channel_num = voe_.GetLastChannel();
- TestInsertDtmf(0, channel_num);
+// Test the InsertDtmf on default send stream as caller.
+TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStreamAsCaller) {
+ TestInsertDtmf(0, true);
}
-// Test the InsertDtmf on specified send stream.
-TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnSendStream) {
- EXPECT_TRUE(SetupEngine());
- int channel_num = voe_.GetLastChannel();
- TestInsertDtmf(kSsrc1, channel_num);
+// Test the InsertDtmf on default send stream as callee
+TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStreamAsCallee) {
+ TestInsertDtmf(0, false);
+}
+
+// Test the InsertDtmf on specified send stream as caller.
+TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnSendStreamAsCaller) {
+ TestInsertDtmf(kSsrc1, true);
+}
+
+// Test the InsertDtmf on specified send stream as callee.
+TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnSendStreamAsCallee) {
+ TestInsertDtmf(kSsrc1, false);
}
// Test that we can play a ringback tone properly in a single-stream call.
diff --git a/talk/p2p/base/p2ptransportchannel_unittest.cc b/talk/p2p/base/p2ptransportchannel_unittest.cc
index 32504de..4617179 100644
--- a/talk/p2p/base/p2ptransportchannel_unittest.cc
+++ b/talk/p2p/base/p2ptransportchannel_unittest.cc
@@ -1266,6 +1266,7 @@
DestroyChannels();
}
+
// Test what happens when we have 2 users behind the same NAT. This can lead
// to interesting behavior because the STUN server will only give out the
// address of the outermost NAT.
@@ -1503,3 +1504,4 @@
TestSendRecv(1);
}
+
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index 76fafae..e60fa35 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -55,6 +55,8 @@
MSG_SETRENDERER,
MSG_ADDRECVSTREAM,
MSG_REMOVERECVSTREAM,
+ MSG_ADDSENDSTREAM,
+ MSG_REMOVESENDSTREAM,
MSG_SETRINGBACKTONE,
MSG_PLAYRINGBACKTONE,
MSG_SETMAXSENDBANDWIDTH,
@@ -74,7 +76,7 @@
MSG_DATARECEIVED,
MSG_SETCAPTURER,
MSG_ISSCREENCASTING,
- MSG_SCREENCASTFPS,
+ MSG_GETSCREENCASTDETAILS,
MSG_SETSCREENCASTFACTORY,
MSG_FIRSTPACKETRECEIVED,
MSG_SESSION_ERROR,
@@ -334,12 +336,14 @@
bool result;
};
-struct ScreencastFpsMessageData : public talk_base::MessageData {
- explicit ScreencastFpsMessageData(uint32 s)
- : ssrc(s), result(0) {
+struct VideoChannel::ScreencastDetailsMessageData :
+ public talk_base::MessageData {
+ explicit ScreencastDetailsMessageData(uint32 s)
+ : ssrc(s), fps(0), screencast_max_pixels(0) {
}
uint32 ssrc;
- int result;
+ int fps;
+ int screencast_max_pixels;
};
struct SetScreenCaptureFactoryMessageData : public talk_base::MessageData {
@@ -480,6 +484,18 @@
return data.result;
}
+bool BaseChannel::AddSendStream(const StreamParams& sp) {
+ StreamMessageData data(sp);
+ Send(MSG_ADDSENDSTREAM, &data);
+ return data.result;
+}
+
+bool BaseChannel::RemoveSendStream(uint32 ssrc) {
+ SsrcMessageData data(ssrc);
+ Send(MSG_REMOVESENDSTREAM, &data);
+ return data.result;
+}
+
bool BaseChannel::SetLocalContent(const MediaContentDescription* content,
ContentAction action) {
SetContentData data(content, action);
@@ -1149,6 +1165,16 @@
return media_channel()->RemoveRecvStream(ssrc);
}
+bool BaseChannel::AddSendStream_w(const StreamParams& sp) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ return media_channel()->AddSendStream(sp);
+}
+
+bool BaseChannel::RemoveSendStream_w(uint32 ssrc) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ return media_channel()->RemoveSendStream(ssrc);
+}
+
bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
ContentAction action) {
if (!VERIFY(action == CA_OFFER || action == CA_ANSWER ||
@@ -1359,6 +1385,16 @@
data->result = RemoveRecvStream_w(data->ssrc);
break;
}
+ case MSG_ADDSENDSTREAM: {
+ StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
+ data->result = AddSendStream_w(data->sp);
+ break;
+ }
+ case MSG_REMOVESENDSTREAM: {
+ SsrcMessageData* data = static_cast<SsrcMessageData*>(pmsg->pdata);
+ data->result = RemoveSendStream_w(data->ssrc);
+ break;
+ }
case MSG_SETMAXSENDBANDWIDTH: {
SetBandwidthData* data = static_cast<SetBandwidthData*>(pmsg->pdata);
data->result = SetMaxSendBandwidth_w(data->value);
@@ -1964,10 +2000,16 @@
return data.result;
}
-int VideoChannel::ScreencastFps(uint32 ssrc) {
- ScreencastFpsMessageData data(ssrc);
- Send(MSG_SCREENCASTFPS, &data);
- return data.result;
+int VideoChannel::GetScreencastFps(uint32 ssrc) {
+ ScreencastDetailsMessageData data(ssrc);
+ Send(MSG_GETSCREENCASTDETAILS, &data);
+ return data.fps;
+}
+
+int VideoChannel::GetScreencastMaxPixels(uint32 ssrc) {
+ ScreencastDetailsMessageData data(ssrc);
+ Send(MSG_GETSCREENCASTDETAILS, &data);
+ return data.screencast_max_pixels;
}
bool VideoChannel::SendIntraFrame() {
@@ -2184,14 +2226,16 @@
return !screencast_capturers_.empty();
}
-int VideoChannel::ScreencastFps_w(uint32 ssrc) const {
- ScreencastMap::const_iterator iter = screencast_capturers_.find(ssrc);
+void VideoChannel::ScreencastDetails_w(
+ ScreencastDetailsMessageData* data) const {
+ ScreencastMap::const_iterator iter = screencast_capturers_.find(data->ssrc);
if (iter == screencast_capturers_.end()) {
- return 0;
+ return;
}
VideoCapturer* capturer = iter->second;
const VideoFormat* video_format = capturer->GetCaptureFormat();
- return VideoFormat::IntervalToFps(video_format->interval);
+ data->fps = VideoFormat::IntervalToFps(video_format->interval);
+ data->screencast_max_pixels = capturer->screencast_max_pixels();
}
void VideoChannel::SetScreenCaptureFactory_w(
@@ -2262,10 +2306,10 @@
data->result = IsScreencasting_w();
break;
}
- case MSG_SCREENCASTFPS: {
- ScreencastFpsMessageData* data =
- static_cast<ScreencastFpsMessageData*>(pmsg->pdata);
- data->result = ScreencastFps_w(data->ssrc);
+ case MSG_GETSCREENCASTDETAILS: {
+ ScreencastDetailsMessageData* data =
+ static_cast<ScreencastDetailsMessageData*>(pmsg->pdata);
+ ScreencastDetails_w(data);
break;
}
case MSG_SENDINTRAFRAME: {
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index eccadd3..6c17e18 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -119,6 +119,8 @@
// Multiplexing
bool AddRecvStream(const StreamParams& sp);
bool RemoveRecvStream(uint32 ssrc);
+ bool AddSendStream(const StreamParams& sp);
+ bool RemoveSendStream(uint32 ssrc);
// Monitoring
void StartConnectionMonitor(int cms);
@@ -277,6 +279,8 @@
void ChannelNotWritable_w();
bool AddRecvStream_w(const StreamParams& sp);
bool RemoveRecvStream_w(uint32 ssrc);
+ bool AddSendStream_w(const StreamParams& sp);
+ bool RemoveSendStream_w(uint32 ssrc);
virtual bool ShouldSetupDtlsSrtp() const;
// Do the DTLS key expansion and impose it on the SRTP/SRTCP filters.
// |rtcp_channel| indicates whether to set up the RTP or RTCP filter.
@@ -488,13 +492,13 @@
// TODO(pthatcher): Refactor to use a "capture id" instead of an
// ssrc here as the "key".
VideoCapturer* AddScreencast(uint32 ssrc, const ScreencastId& id);
- VideoCapturer* GetScreencastCapturer(uint32 ssrc);
bool SetCapturer(uint32 ssrc, VideoCapturer* capturer);
bool RemoveScreencast(uint32 ssrc);
// True if we've added a screencast. Doesn't matter if the capturer
// has been started or not.
bool IsScreencasting();
- int ScreencastFps(uint32 ssrc);
+ int GetScreencastFps(uint32 ssrc);
+ int GetScreencastMaxPixels(uint32 ssrc);
// Get statistics about the current media session.
bool GetStats(VideoMediaInfo* stats);
@@ -525,6 +529,7 @@
private:
typedef std::map<uint32, VideoCapturer*> ScreencastMap;
+ struct ScreencastDetailsMessageData;
// overrides from BaseChannel
virtual void ChangeState();
@@ -544,12 +549,11 @@
void SetRenderer_w(uint32 ssrc, VideoRenderer* renderer);
VideoCapturer* AddScreencast_w(uint32 ssrc, const ScreencastId& id);
- VideoCapturer* GetScreencastCapturer_w(uint32 ssrc);
bool SetCapturer_w(uint32 ssrc, VideoCapturer* capturer);
bool RemoveScreencast_w(uint32 ssrc);
void OnScreencastWindowEvent_s(uint32 ssrc, talk_base::WindowEvent we);
bool IsScreencasting_w() const;
- int ScreencastFps_w(uint32 ssrc) const;
+ void ScreencastDetails_w(ScreencastDetailsMessageData* d) const;
void SetScreenCaptureFactory_w(
ScreenCapturerFactory* screencapture_factory);
bool GetStats_w(VideoMediaInfo* stats);
@@ -590,11 +594,6 @@
~DataChannel();
bool Init();
- // downcasts a MediaChannel
- virtual DataMediaChannel* media_channel() const {
- return static_cast<DataMediaChannel*>(BaseChannel::media_channel());
- }
-
virtual bool SendData(const SendDataParams& params,
const talk_base::Buffer& payload,
SendDataResult* result);
@@ -616,6 +615,12 @@
// both local and remote descriptions are set, and the channel is unblocked.
sigslot::signal1<bool> SignalReadyToSendData;
+ protected:
+ // downcasts a MediaChannel.
+ virtual DataMediaChannel* media_channel() const {
+ return static_cast<DataMediaChannel*>(BaseChannel::media_channel());
+ }
+
private:
struct SendDataMessageData : public talk_base::MessageData {
SendDataMessageData(const SendDataParams& params,