diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-06-20 14:52:48 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2014-06-20 14:52:48 +0100 |
commit | 99bc8f39c7d7784a6d03c08add97b0f97c44db53 (patch) | |
tree | 3b4bc0bedfa949af7f8fd4ae7633f7a0870e6e86 /media | |
parent | 92d181beaac81934497a46fa15e218b0fd6569a6 (diff) | |
parent | 6de09a1dbcfa359a587b972f3e0fe30ffbd5be41 (diff) | |
download | talk-99bc8f39c7d7784a6d03c08add97b0f97c44db53.tar.gz |
Merge from Chromium at DEPS revision 278205
This commit was generated by merge_to_master.py.
Change-Id: I708b08f68e2ffaf5f127f628e6d299007754091d
Diffstat (limited to 'media')
-rw-r--r-- | media/base/mediachannel.h | 31 | ||||
-rw-r--r-- | media/base/videoengine_unittest.h | 52 | ||||
-rw-r--r-- | media/sctp/sctpdataengine.cc | 11 | ||||
-rw-r--r-- | media/webrtc/OWNERS | 3 | ||||
-rw-r--r-- | media/webrtc/fakewebrtcvoiceengine.h | 69 | ||||
-rw-r--r-- | media/webrtc/webrtcmediaengine.cc | 33 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine.cc | 143 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine.h | 1 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine2.cc | 301 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine2.h | 50 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine2_unittest.cc | 618 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine2_unittest.h | 157 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine_unittest.cc | 128 | ||||
-rw-r--r-- | media/webrtc/webrtcvoiceengine.cc | 43 | ||||
-rw-r--r-- | media/webrtc/webrtcvoiceengine_unittest.cc | 144 |
15 files changed, 1221 insertions, 563 deletions
diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h index 0bb0f04..49902ee 100644 --- a/media/base/mediachannel.h +++ b/media/base/mediachannel.h @@ -314,6 +314,10 @@ struct VideoOptions { cpu_overuse_detection.SetFrom(change.cpu_overuse_detection); cpu_underuse_threshold.SetFrom(change.cpu_underuse_threshold); cpu_overuse_threshold.SetFrom(change.cpu_overuse_threshold); + cpu_underuse_encode_rsd_threshold.SetFrom( + change.cpu_underuse_encode_rsd_threshold); + cpu_overuse_encode_rsd_threshold.SetFrom( + change.cpu_overuse_encode_rsd_threshold); cpu_overuse_encode_usage.SetFrom(change.cpu_overuse_encode_usage); conference_mode.SetFrom(change.conference_mode); process_adaptation_threshhold.SetFrom(change.process_adaptation_threshhold); @@ -350,6 +354,10 @@ struct VideoOptions { cpu_overuse_detection == o.cpu_overuse_detection && cpu_underuse_threshold == o.cpu_underuse_threshold && cpu_overuse_threshold == o.cpu_overuse_threshold && + cpu_underuse_encode_rsd_threshold == + o.cpu_underuse_encode_rsd_threshold && + cpu_overuse_encode_rsd_threshold == + o.cpu_overuse_encode_rsd_threshold && cpu_overuse_encode_usage == o.cpu_overuse_encode_usage && conference_mode == o.conference_mode && process_adaptation_threshhold == o.process_adaptation_threshhold && @@ -390,6 +398,10 @@ struct VideoOptions { ost << ToStringIfSet("cpu overuse detection", cpu_overuse_detection); ost << ToStringIfSet("cpu underuse threshold", cpu_underuse_threshold); ost << ToStringIfSet("cpu overuse threshold", cpu_overuse_threshold); + ost << ToStringIfSet("cpu underuse encode rsd threshold", + cpu_underuse_encode_rsd_threshold); + ost << ToStringIfSet("cpu overuse encode rsd threshold", + cpu_overuse_encode_rsd_threshold); ost << ToStringIfSet("cpu overuse encode usage", cpu_overuse_encode_usage); ost << ToStringIfSet("conference mode", conference_mode); @@ -443,10 +455,22 @@ struct VideoOptions { // adaptation algorithm. So this option will override the // |adapt_input_to_cpu_usage|. Settable<bool> cpu_overuse_detection; - // Low threshold for cpu overuse adaptation in ms. (Adapt up) + // Low threshold (t1) for cpu overuse adaptation. (Adapt up) + // Metric: encode usage (m1). m1 < t1 => underuse. Settable<int> cpu_underuse_threshold; - // High threshold for cpu overuse adaptation in ms. (Adapt down) + // High threshold (t1) for cpu overuse adaptation. (Adapt down) + // Metric: encode usage (m1). m1 > t1 => overuse. Settable<int> cpu_overuse_threshold; + // Low threshold (t2) for cpu overuse adaptation. (Adapt up) + // Metric: relative standard deviation of encode time (m2). + // Optional threshold. If set, (m1 < t1 && m2 < t2) => underuse. + // Note: t2 will have no effect if t1 is not set. + Settable<int> cpu_underuse_encode_rsd_threshold; + // High threshold (t2) for cpu overuse adaptation. (Adapt down) + // Metric: relative standard deviation of encode time (m2). + // Optional threshold. If set, (m1 > t1 || m2 > t2) => overuse. + // Note: t2 will have no effect if t1 is not set. + Settable<int> cpu_overuse_encode_rsd_threshold; // Use encode usage for cpu detection. Settable<bool> cpu_overuse_encode_usage; // Use conference mode? @@ -787,6 +811,7 @@ struct MediaReceiverInfo { int packets_rcvd; int packets_lost; float fraction_lost; + std::string codec_name; std::vector<SsrcReceiverInfo> local_stats; std::vector<SsrcSenderInfo> remote_stats; }; @@ -869,6 +894,7 @@ struct VideoSenderInfo : public MediaSenderInfo { capture_jitter_ms(0), avg_encode_ms(0), encode_usage_percent(0), + encode_rsd(0), capture_queue_delay_ms_per_s(0) { } @@ -889,6 +915,7 @@ struct VideoSenderInfo : public MediaSenderInfo { int capture_jitter_ms; int avg_encode_ms; int encode_usage_percent; + int encode_rsd; int capture_queue_delay_ms_per_s; VariableInfo<int> adapt_frame_drops; VariableInfo<int> effects_frame_drops; diff --git a/media/base/videoengine_unittest.h b/media/base/videoengine_unittest.h index 54b5500..382fb77 100644 --- a/media/base/videoengine_unittest.h +++ b/media/base/videoengine_unittest.h @@ -792,14 +792,45 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_FRAME_WAIT(3, codec.width, codec.height, kTimeout); EXPECT_EQ(2, renderer_.num_set_sizes()); } + void SendReceiveManyAndGetStats(const cricket::VideoCodec& codec, + int duration_sec, int fps) { + EXPECT_TRUE(SetOneCodec(codec)); + EXPECT_TRUE(SetSend(true)); + EXPECT_TRUE(channel_->SetRender(true)); + EXPECT_EQ(0, renderer_.num_rendered_frames()); + for (int i = 0; i < duration_sec; ++i) { + for (int frame = 1; frame <= fps; ++frame) { + EXPECT_TRUE(WaitAndSendFrame(1000 / fps)); + EXPECT_FRAME_WAIT(frame + i * fps, codec.width, codec.height, kTimeout); + } + cricket::VideoMediaInfo info; + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); + // For webrtc, |framerate_sent| and |framerate_rcvd| depend on periodic + // callbacks (1 sec). + // Received |fraction_lost| and |packets_lost| are from sent RTCP packet. + // One sent packet needed (sent about once per second). + // |framerate_input|, |framerate_decoded| and |framerate_output| are using + // RateTracker. RateTracker needs to be called twice (with >1 second in + // b/w calls) before a framerate is calculated. + // Therefore insert frames (and call GetStats each sec) for a few seconds + // before testing stats. + } + talk_base::scoped_ptr<const talk_base::Buffer> p(GetRtpPacket(0)); + EXPECT_EQ(codec.id, GetPayloadType(p.get())); + } + // Test that stats work properly for a 1-1 call. void GetStats() { - SendAndReceive(DefaultCodec()); + const int kDurationSec = 3; + const int kFps = 10; + SendReceiveManyAndGetStats(DefaultCodec(), kDurationSec, kFps); + cricket::VideoMediaInfo info; EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.senders.size()); // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload? + // For webrtc, bytes_sent does not include the RTP header length. EXPECT_GT(info.senders[0].bytes_sent, 0); EXPECT_EQ(NumRtpPackets(), info.senders[0].packets_sent); EXPECT_EQ(0.0, info.senders[0].fraction_lost); @@ -819,7 +850,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(NumRtpPackets(), info.receivers[0].packets_rcvd); EXPECT_EQ(0.0, info.receivers[0].fraction_lost); EXPECT_EQ(0, info.receivers[0].packets_lost); - EXPECT_EQ(0, info.receivers[0].packets_concealed); + // TODO(asapersson): Not set for webrtc. Handle missing stats. + // EXPECT_EQ(0, info.receivers[0].packets_concealed); EXPECT_EQ(0, info.receivers[0].firs_sent); EXPECT_EQ(0, info.receivers[0].plis_sent); EXPECT_EQ(0, info.receivers[0].nacks_sent); @@ -860,16 +892,11 @@ class VideoMediaChannelTest : public testing::Test, ASSERT_EQ(1U, info.senders.size()); // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload? + // For webrtc, bytes_sent does not include the RTP header length. EXPECT_GT(info.senders[0].bytes_sent, 0); EXPECT_EQ(NumRtpPackets(), info.senders[0].packets_sent); - EXPECT_EQ(0.0, info.senders[0].fraction_lost); - EXPECT_EQ(0, info.senders[0].firs_rcvd); - EXPECT_EQ(0, info.senders[0].plis_rcvd); - EXPECT_EQ(0, info.senders[0].nacks_rcvd); EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); - EXPECT_GT(info.senders[0].framerate_input, 0); - EXPECT_GT(info.senders[0].framerate_sent, 0); ASSERT_EQ(2U, info.receivers.size()); for (size_t i = 0; i < info.receivers.size(); ++i) { @@ -877,17 +904,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(i + 1, info.receivers[i].ssrcs()[0]); EXPECT_EQ(NumRtpBytes(), info.receivers[i].bytes_rcvd); EXPECT_EQ(NumRtpPackets(), info.receivers[i].packets_rcvd); - EXPECT_EQ(0.0, info.receivers[i].fraction_lost); - EXPECT_EQ(0, info.receivers[i].packets_lost); - EXPECT_EQ(0, info.receivers[i].packets_concealed); - EXPECT_EQ(0, info.receivers[i].firs_sent); - EXPECT_EQ(0, info.receivers[i].plis_sent); - EXPECT_EQ(0, info.receivers[i].nacks_sent); EXPECT_EQ(DefaultCodec().width, info.receivers[i].frame_width); EXPECT_EQ(DefaultCodec().height, info.receivers[i].frame_height); - EXPECT_GT(info.receivers[i].framerate_rcvd, 0); - EXPECT_GT(info.receivers[i].framerate_decoded, 0); - EXPECT_GT(info.receivers[i].framerate_output, 0); } } // Test that stats work properly for a conf call with multiple send streams. diff --git a/media/sctp/sctpdataengine.cc b/media/sctp/sctpdataengine.cc index 017454f..46b2ece 100644 --- a/media/sctp/sctpdataengine.cc +++ b/media/sctp/sctpdataengine.cc @@ -781,7 +781,6 @@ void SctpDataMediaChannel::OnStreamResetEvent( << ListStreams(open_streams_) << "], Q'd: [" << ListStreams(queued_reset_streams_) << "], Sent: [" << ListStreams(sent_reset_streams_) << "]"; - bool local_stream_reset_acknowledged = false; // If both sides try to reset some streams at the same time (even if they're // disjoint sets), we can get reset failures. @@ -792,7 +791,6 @@ void SctpDataMediaChannel::OnStreamResetEvent( sent_reset_streams_.begin(), sent_reset_streams_.end()); sent_reset_streams_.clear(); - local_stream_reset_acknowledged = true; } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { // Each side gets an event for each direction of a stream. That is, @@ -809,7 +807,6 @@ void SctpDataMediaChannel::OnStreamResetEvent( if (it != sent_reset_streams_.end()) { LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ << "): local sid " << stream_id << " acknowledged."; - local_stream_reset_acknowledged = true; sent_reset_streams_.erase(it); } else if ((it = open_streams_.find(stream_id)) @@ -840,11 +837,9 @@ void SctpDataMediaChannel::OnStreamResetEvent( } } - if (local_stream_reset_acknowledged) { - // This message acknowledges the last stream-reset request we sent out - // (only one can be outstanding at a time). Send out the next one. - SendQueuedStreamResets(); - } + // Always try to send the queued RESET because this call indicates that the + // last local RESET or remote RESET has made some progress. + SendQueuedStreamResets(); } // Puts the specified |param| from the codec identified by |id| into |dest| diff --git a/media/webrtc/OWNERS b/media/webrtc/OWNERS new file mode 100644 index 0000000..9a3546e --- /dev/null +++ b/media/webrtc/OWNERS @@ -0,0 +1,3 @@ +mflodman@webrtc.org +pthatcher@webrtc.org +wu@webrtc.org diff --git a/media/webrtc/fakewebrtcvoiceengine.h b/media/webrtc/fakewebrtcvoiceengine.h index 1df2195..ff2079b 100644 --- a/media/webrtc/fakewebrtcvoiceengine.h +++ b/media/webrtc/fakewebrtcvoiceengine.h @@ -36,6 +36,7 @@ #include "talk/base/gunit.h" #include "talk/base/stringutils.h" #include "talk/media/base/codec.h" +#include "talk/media/base/rtputils.h" #include "talk/media/base/voiceprocessor.h" #include "talk/media/webrtc/fakewebrtccommon.h" #include "talk/media/webrtc/webrtcvoe.h" @@ -96,7 +97,8 @@ class FakeWebRtcVoiceEngine volume_pan_right(1.0), file(false), vad(false), - fec(false), + codec_fec(false), + red(false), nack(false), media_processor_registered(false), rx_agc_enabled(false), @@ -104,7 +106,7 @@ class FakeWebRtcVoiceEngine cn8_type(13), cn16_type(105), dtmf_type(106), - fec_type(117), + red_type(117), nack_max_packets(0), vie_network(NULL), video_channel(-1), @@ -124,7 +126,8 @@ class FakeWebRtcVoiceEngine float volume_pan_right; bool file; bool vad; - bool fec; + bool codec_fec; + bool red; bool nack; bool media_processor_registered; bool rx_agc_enabled; @@ -133,7 +136,7 @@ class FakeWebRtcVoiceEngine int cn8_type; int cn16_type; int dtmf_type; - int fec_type; + int red_type; int nack_max_packets; webrtc::ViENetwork* vie_network; int video_channel; @@ -214,8 +217,11 @@ class FakeWebRtcVoiceEngine bool GetVAD(int channel) { return channels_[channel]->vad; } - bool GetFEC(int channel) { - return channels_[channel]->fec; + bool GetRED(int channel) { + return channels_[channel]->red; + } + bool GetCodecFEC(int channel) { + return channels_[channel]->codec_fec; } bool GetNACK(int channel) { return channels_[channel]->nack; @@ -243,8 +249,8 @@ class FakeWebRtcVoiceEngine int GetSendTelephoneEventPayloadType(int channel) { return channels_[channel]->dtmf_type; } - int GetSendFECPayloadType(int channel) { - return channels_[channel]->fec_type; + int GetSendREDPayloadType(int channel) { + return channels_[channel]->red_type; } bool CheckPacket(int channel, const void* data, size_t len) { bool result = !CheckNoPacket(channel); @@ -437,7 +443,26 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(RemoveSecondarySendCodec, (int channel)); WEBRTC_STUB(GetSecondarySendCodec, (int channel, webrtc::CodecInst& codec)); - WEBRTC_STUB(GetRecCodec, (int channel, webrtc::CodecInst& codec)); + WEBRTC_FUNC(GetRecCodec, (int channel, webrtc::CodecInst& codec)) { + WEBRTC_CHECK_CHANNEL(channel); + const Channel* c = channels_[channel]; + for (std::list<std::string>::const_iterator it_packet = c->packets.begin(); + it_packet != c->packets.end(); ++it_packet) { + int pltype; + if (!GetRtpPayloadType(it_packet->data(), it_packet->length(), &pltype)) { + continue; + } + for (std::vector<webrtc::CodecInst>::const_iterator it_codec = + c->recv_codecs.begin(); it_codec != c->recv_codecs.end(); + ++it_codec) { + if (it_codec->pltype == pltype) { + codec = *it_codec; + return 0; + } + } + } + return -1; + } WEBRTC_STUB(SetAMREncFormat, (int channel, webrtc::AmrMode mode)); WEBRTC_STUB(SetAMRDecFormat, (int channel, webrtc::AmrMode mode)); WEBRTC_STUB(SetAMRWbEncFormat, (int channel, webrtc::AmrMode mode)); @@ -511,6 +536,16 @@ class FakeWebRtcVoiceEngine } WEBRTC_STUB(GetVADStatus, (int channel, bool& enabled, webrtc::VadModes& mode, bool& disabledDTX)); + WEBRTC_FUNC(SetFECStatus, (int channel, bool enable)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->codec_fec = enable; + return 0; + } + WEBRTC_FUNC(GetFECStatus, (int channel, bool& enable)) { + WEBRTC_CHECK_CHANNEL(channel); + enable = channels_[channel]->codec_fec; + return 0; + } // webrtc::VoEDtmf WEBRTC_FUNC(SendTelephoneEvent, (int channel, int event_code, @@ -823,16 +858,24 @@ class FakeWebRtcVoiceEngine stats.packetsReceived = kIntStatValue; return 0; } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetREDStatus, (int channel, bool enable, int redPayloadtype)) { +#else WEBRTC_FUNC(SetFECStatus, (int channel, bool enable, int redPayloadtype)) { +#endif // USE_WEBRTC_DEV_BRANCH WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->fec = enable; - channels_[channel]->fec_type = redPayloadtype; + channels_[channel]->red = enable; + channels_[channel]->red_type = redPayloadtype; return 0; } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(GetREDStatus, (int channel, bool& enable, int& redPayloadtype)) { +#else WEBRTC_FUNC(GetFECStatus, (int channel, bool& enable, int& redPayloadtype)) { +#endif // USE_WEBRTC_DEV_BRANCH WEBRTC_CHECK_CHANNEL(channel); - enable = channels_[channel]->fec; - redPayloadtype = channels_[channel]->fec_type; + enable = channels_[channel]->red; + redPayloadtype = channels_[channel]->red_type; return 0; } WEBRTC_FUNC(SetNACKStatus, (int channel, bool enable, int maxNoPackets)) { diff --git a/media/webrtc/webrtcmediaengine.cc b/media/webrtc/webrtcmediaengine.cc index 03f7b9b..445564c 100644 --- a/media/webrtc/webrtcmediaengine.cc +++ b/media/webrtc/webrtcmediaengine.cc @@ -25,5 +25,34 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// TODO(pbos): Move CreateWebRtcMediaEngine here as soon as -// libjingle/libjingle.gyp in Chromium builds this file. +#include "talk/media/webrtc/webrtcmediaengine.h" +#include "webrtc/system_wrappers/interface/field_trial.h" + +WRME_EXPORT +cricket::MediaEngineInterface* CreateWebRtcMediaEngine( + webrtc::AudioDeviceModule* adm, + webrtc::AudioDeviceModule* adm_sc, + cricket::WebRtcVideoEncoderFactory* encoder_factory, + cricket::WebRtcVideoDecoderFactory* decoder_factory) { +#ifdef WEBRTC_CHROMIUM_BUILD + if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { + return new cricket::WebRtcMediaEngine2( + adm, adm_sc, encoder_factory, decoder_factory); + } +#endif // WEBRTC_CHROMIUM_BUILD + return new cricket::WebRtcMediaEngine( + adm, adm_sc, encoder_factory, decoder_factory); +} + +WRME_EXPORT +void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) { +#ifdef WEBRTC_CHROMIUM_BUILD + if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { + delete static_cast<cricket::WebRtcMediaEngine2*>(media_engine); + } else { +#endif // WEBRTC_CHROMIUM_BUILD + delete static_cast<cricket::WebRtcMediaEngine*>(media_engine); +#ifdef WEBRTC_CHROMIUM_BUILD + } +#endif // WEBRTC_CHROMIUM_BUILD +} diff --git a/media/webrtc/webrtcvideoengine.cc b/media/webrtc/webrtcvideoengine.cc index a029ec2..f1810bf 100644 --- a/media/webrtc/webrtcvideoengine.cc +++ b/media/webrtc/webrtcvideoengine.cc @@ -62,44 +62,6 @@ #include "talk/media/webrtc/webrtcvoiceengine.h" #include "webrtc/experiments.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" -#ifdef WEBRTC_CHROMIUM_BUILD -#include "webrtc/system_wrappers/interface/field_trial.h" -#endif - -#if !defined(LIBPEERCONNECTION_LIB) -#include "talk/media/webrtc/webrtcmediaengine.h" - -WRME_EXPORT -cricket::MediaEngineInterface* CreateWebRtcMediaEngine( - webrtc::AudioDeviceModule* adm, webrtc::AudioDeviceModule* adm_sc, - cricket::WebRtcVideoEncoderFactory* encoder_factory, - cricket::WebRtcVideoDecoderFactory* decoder_factory) { -#ifdef WEBRTC_CHROMIUM_BUILD - if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { - return new cricket::WebRtcMediaEngine2( - adm, adm_sc, encoder_factory, decoder_factory); - } else { -#endif - return new cricket::WebRtcMediaEngine( - adm, adm_sc, encoder_factory, decoder_factory); -#ifdef WEBRTC_CHROMIUM_BUILD - } -#endif -} - -WRME_EXPORT -void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) { -#ifdef WEBRTC_CHROMIUM_BUILD - if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { - delete static_cast<cricket::WebRtcMediaEngine2*>(media_engine); - } else { -#endif - delete static_cast<cricket::WebRtcMediaEngine*>(media_engine); -#ifdef WEBRTC_CHROMIUM_BUILD - } -#endif -} -#endif namespace cricket { @@ -260,11 +222,11 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { const int kVideoCodecClockratekHz = cricket::kVideoCodecClockrate / 1000; + int64 elapsed_time_ms = + (rtp_ts_wraparound_handler_.Unwrap(rtp_time_stamp) - + capture_start_rtp_time_stamp_) / kVideoCodecClockratekHz; #ifdef USE_WEBRTC_DEV_BRANCH if (ntp_time_ms > 0) { - int64 elapsed_time_ms = - (rtp_ts_wraparound_handler_.Unwrap(rtp_time_stamp) - - capture_start_rtp_time_stamp_) / kVideoCodecClockratekHz; capture_start_ntp_time_ms_ = ntp_time_ms - elapsed_time_ms; } #endif @@ -272,30 +234,31 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { if (renderer_ == NULL) { return 0; } - // Convert 90K rtp timestamp to ns timestamp. - int64 rtp_time_stamp_in_ns = (rtp_time_stamp / kVideoCodecClockratekHz) * - talk_base::kNumNanosecsPerMillisec; + // Convert elapsed_time_ms to ns timestamp. + int64 elapsed_time_ns = + elapsed_time_ms * talk_base::kNumNanosecsPerMillisec; // Convert milisecond render time to ns timestamp. - int64 render_time_stamp_in_ns = render_time * + int64 render_time_ns = render_time * talk_base::kNumNanosecsPerMillisec; - // Send the rtp timestamp to renderer as the VideoFrame timestamp. - // and the render timestamp as the VideoFrame elapsed_time. + // Note that here we send the |elapsed_time_ns| to renderer as the + // cricket::VideoFrame's elapsed_time_ and the |render_time_ns| as the + // cricket::VideoFrame's time_stamp_. if (handle == NULL) { - return DeliverBufferFrame(buffer, buffer_size, render_time_stamp_in_ns, - rtp_time_stamp_in_ns); + return DeliverBufferFrame(buffer, buffer_size, render_time_ns, + elapsed_time_ns); } else { - return DeliverTextureFrame(handle, render_time_stamp_in_ns, - rtp_time_stamp_in_ns); + return DeliverTextureFrame(handle, render_time_ns, + elapsed_time_ns); } } virtual bool IsTextureSupported() { return true; } int DeliverBufferFrame(unsigned char* buffer, int buffer_size, - int64 elapsed_time, int64 rtp_time_stamp_in_ns) { + int64 time_stamp, int64 elapsed_time) { WebRtcVideoFrame video_frame; video_frame.Alias(buffer, buffer_size, width_, height_, - 1, 1, elapsed_time, rtp_time_stamp_in_ns, 0); + 1, 1, elapsed_time, time_stamp, 0); // Sanity check on decoded frame size. if (buffer_size != static_cast<int>(VideoFrame::SizeOf(width_, height_))) { @@ -308,12 +271,10 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { return ret; } - int DeliverTextureFrame(void* handle, - int64 elapsed_time, - int64 rtp_time_stamp_in_ns) { + int DeliverTextureFrame(void* handle, int64 time_stamp, int64 elapsed_time) { WebRtcTextureVideoFrame video_frame( static_cast<webrtc::NativeHandle*>(handle), width_, height_, - elapsed_time, rtp_time_stamp_in_ns); + elapsed_time, time_stamp); return renderer_->RenderFrame(&video_frame); } @@ -925,6 +886,18 @@ static bool GetCpuOveruseOptions(const VideoOptions& options, // Use method based on encode usage. overuse_options->low_encode_usage_threshold_percent = underuse_threshold; overuse_options->high_encode_usage_threshold_percent = overuse_threshold; +#ifdef USE_WEBRTC_DEV_BRANCH + // Set optional thresholds, if configured. + int underuse_rsd_threshold = 0; + if (options.cpu_underuse_encode_rsd_threshold.Get( + &underuse_rsd_threshold)) { + overuse_options->low_encode_time_rsd_threshold = underuse_rsd_threshold; + } + int overuse_rsd_threshold = 0; + if (options.cpu_overuse_encode_rsd_threshold.Get(&overuse_rsd_threshold)) { + overuse_options->high_encode_time_rsd_threshold = overuse_rsd_threshold; + } +#endif } else { // Use default method based on capture jitter. overuse_options->low_capture_jitter_threshold_ms = @@ -2050,6 +2023,9 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { << " reuse default channel #" << vie_channel_; first_receive_ssrc_ = sp.first_ssrc(); + if (!MaybeSetRtxSsrc(sp, vie_channel_)) { + return false; + } if (render_started_) { if (engine()->vie()->render()->StartRender(vie_channel_) !=0) { LOG_RTCERR1(StartRender, vie_channel_); @@ -2088,19 +2064,8 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { } channel_iterator = recv_channels_.find(sp.first_ssrc()); - // Set the corresponding RTX SSRC. - uint32 rtx_ssrc; - bool has_rtx = sp.GetFidSsrc(sp.first_ssrc(), &rtx_ssrc); - if (has_rtx) { - LOG(LS_INFO) << "Setting rtx ssrc " << rtx_ssrc << " for stream " - << sp.first_ssrc(); - if (engine()->vie()->rtp()->SetRemoteSSRCType( - channel_id, webrtc::kViEStreamTypeRtx, rtx_ssrc) != 0) { - LOG_RTCERR3(SetRemoteSSRCType, channel_id, webrtc::kViEStreamTypeRtx, - rtx_ssrc); - return false; - } - rtx_to_primary_ssrc_[rtx_ssrc] = sp.first_ssrc(); + if (!MaybeSetRtxSsrc(sp, channel_id)) { + return false; } // Get the default renderer. @@ -2129,6 +2094,24 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { return true; } +bool WebRtcVideoMediaChannel::MaybeSetRtxSsrc(const StreamParams& sp, + int channel_id) { + uint32 rtx_ssrc; + bool has_rtx = sp.GetFidSsrc(sp.first_ssrc(), &rtx_ssrc); + if (has_rtx) { + LOG(LS_INFO) << "Setting rtx ssrc " << rtx_ssrc << " for stream " + << sp.first_ssrc(); + if (engine()->vie()->rtp()->SetRemoteSSRCType( + channel_id, webrtc::kViEStreamTypeRtx, rtx_ssrc) != 0) { + LOG_RTCERR3(SetRemoteSSRCType, channel_id, webrtc::kViEStreamTypeRtx, + rtx_ssrc); + return false; + } + rtx_to_primary_ssrc_[rtx_ssrc] = sp.first_ssrc(); + } + return true; +} + bool WebRtcVideoMediaChannel::RemoveRecvStream(uint32 ssrc) { if (ssrc == 0) { LOG(LS_ERROR) << "RemoveRecvStream with 0 ssrc is not supported."; @@ -2523,6 +2506,7 @@ bool WebRtcVideoMediaChannel::GetStats(const StatsOptions& options, sinfo.capture_jitter_ms = metrics.capture_jitter_ms; sinfo.avg_encode_ms = metrics.avg_encode_time_ms; sinfo.encode_usage_percent = metrics.encode_usage_percent; + sinfo.encode_rsd = metrics.encode_rsd; sinfo.capture_queue_delay_ms_per_s = metrics.capture_queue_delay_ms_per_s; #else sinfo.capture_jitter_ms = -1; @@ -3056,10 +3040,13 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { if (leaky_bucket_changed) { bool enable_leaky_bucket = - options_.video_leaky_bucket.GetWithDefaultIfUnset(false); + options_.video_leaky_bucket.GetWithDefaultIfUnset(true); LOG(LS_INFO) << "Leaky bucket is enabled? " << enable_leaky_bucket; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { + // TODO(holmer): This API will be removed as we move to the new + // webrtc::Call API. We should clean up this experiment when that is + // happening. if (engine()->vie()->rtp()->SetTransmissionSmoothingStatus( it->second->channel_id(), enable_leaky_bucket) != 0) { LOG_RTCERR2(SetTransmissionSmoothingStatus, it->second->channel_id(), @@ -3602,7 +3589,7 @@ bool WebRtcVideoMediaChannel::ConfigureSending(int channel_id, return false; } - if (options_.video_leaky_bucket.GetWithDefaultIfUnset(false)) { + if (options_.video_leaky_bucket.GetWithDefaultIfUnset(true)) { if (engine()->vie()->rtp()->SetTransmissionSmoothingStatus(channel_id, true) != 0) { LOG_RTCERR2(SetTransmissionSmoothingStatus, channel_id, true); @@ -3927,9 +3914,13 @@ int WebRtcVideoMediaChannel::GetRecvChannelNum(uint32 ssrc) { // Check if we have an RTX stream registered on this SSRC. SsrcMap::iterator rtx_it = rtx_to_primary_ssrc_.find(ssrc); if (rtx_it != rtx_to_primary_ssrc_.end()) { - it = recv_channels_.find(rtx_it->second); - assert(it != recv_channels_.end()); - recv_channel = it->second->channel_id(); + if (rtx_it->second == first_receive_ssrc_) { + recv_channel = vie_channel_; + } else { + it = recv_channels_.find(rtx_it->second); + assert(it != recv_channels_.end()); + recv_channel = it->second->channel_id(); + } } } else { recv_channel = it->second->channel_id(); @@ -3990,7 +3981,7 @@ bool WebRtcVideoMediaChannel::MaybeResetVieSendCodec( options_.video_noise_reduction.GetWithDefaultIfUnset(false); int screencast_min_bitrate = options_.screencast_min_bitrate.GetWithDefaultIfUnset(0); - bool leaky_bucket = options_.video_leaky_bucket.GetWithDefaultIfUnset(false); + bool leaky_bucket = options_.video_leaky_bucket.GetWithDefaultIfUnset(true); bool denoising = !is_screencast && enable_denoising; bool reset_send_codec = target_width != cur_width || target_height != cur_height || diff --git a/media/webrtc/webrtcvideoengine.h b/media/webrtc/webrtcvideoengine.h index 3928542..775f4e4 100644 --- a/media/webrtc/webrtcvideoengine.h +++ b/media/webrtc/webrtcvideoengine.h @@ -345,6 +345,7 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, bool SetReceiveCodecs(WebRtcVideoChannelRecvInfo* info); // Returns the channel number that receives the stream with SSRC |ssrc|. int GetRecvChannelNum(uint32 ssrc); + bool MaybeSetRtxSsrc(const StreamParams& sp, int channel_id); // Given captured video frame size, checks if we need to reset vie send codec. // |reset| is set to whether resetting has happened on vie or not. // Returns false on error. diff --git a/media/webrtc/webrtcvideoengine2.cc b/media/webrtc/webrtcvideoengine2.cc index 723ef1d..d6d1354 100644 --- a/media/webrtc/webrtcvideoengine2.cc +++ b/media/webrtc/webrtcvideoengine2.cc @@ -161,6 +161,22 @@ static bool FindBestVideoFormat(int max_width, return false; } +static void AddDefaultFeedbackParams(VideoCodec* codec) { + const FeedbackParam kFir(kRtcpFbParamCcm, kRtcpFbCcmParamFir); + codec->AddFeedbackParam(kFir); + const FeedbackParam kNack(kRtcpFbParamNack, kParamValueEmpty); + codec->AddFeedbackParam(kNack); + const FeedbackParam kPli(kRtcpFbParamNack, kRtcpFbNackParamPli); + codec->AddFeedbackParam(kPli); + const FeedbackParam kRemb(kRtcpFbParamRemb, kParamValueEmpty); + codec->AddFeedbackParam(kRemb); +} + +static bool IsNackEnabled(const VideoCodec& codec) { + return codec.HasFeedbackParam( + FeedbackParam(kRtcpFbParamNack, kParamValueEmpty)); +} + static VideoCodec DefaultVideoCodec() { VideoCodec default_codec(kDefaultVideoCodecPref.payload_type, kDefaultVideoCodecPref.name, @@ -168,6 +184,7 @@ static VideoCodec DefaultVideoCodec() { kDefaultVideoFormat.height, kDefaultFramerate, 0); + AddDefaultFeedbackParams(&default_codec); return default_codec; } @@ -195,54 +212,47 @@ static std::vector<VideoCodec> DefaultVideoCodecs() { WebRtcVideoEncoderFactory2::~WebRtcVideoEncoderFactory2() { } -class DefaultVideoEncoderFactory : public WebRtcVideoEncoderFactory2 { - public: - virtual bool CreateEncoderSettings( - webrtc::VideoSendStream::Config::EncoderSettings* encoder_settings, - const VideoOptions& options, - const VideoCodec& codec, - size_t num_streams) OVERRIDE { - if (num_streams != 1) { - LOG(LS_ERROR) << "Unsupported number of streams: " << num_streams; - return false; - } - if (!SupportsCodec(codec)) { - LOG(LS_ERROR) << "Can't create encoder settings for unsupported codec: '" - << codec.name << "'"; - return false; - } - - *encoder_settings = webrtc::VideoSendStream::Config::EncoderSettings(); - - webrtc::VideoStream stream; - stream.width = codec.width; - stream.height = codec.height; - stream.max_framerate = - codec.framerate != 0 ? codec.framerate : kDefaultFramerate; +std::vector<webrtc::VideoStream> WebRtcVideoEncoderFactory2::CreateVideoStreams( + const VideoCodec& codec, + const VideoOptions& options, + size_t num_streams) { + assert(SupportsCodec(codec)); + if (num_streams != 1) { + LOG(LS_ERROR) << "Unsupported number of streams: " << num_streams; + return std::vector<webrtc::VideoStream>(); + } - int min_bitrate = kMinVideoBitrate; - codec.GetParam(kCodecParamMinBitrate, &min_bitrate); - int max_bitrate = kMaxVideoBitrate; - codec.GetParam(kCodecParamMaxBitrate, &max_bitrate); - stream.min_bitrate_bps = min_bitrate * 1000; - stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate * 1000; + webrtc::VideoStream stream; + stream.width = codec.width; + stream.height = codec.height; + stream.max_framerate = + codec.framerate != 0 ? codec.framerate : kDefaultFramerate; - int max_qp = 56; - codec.GetParam(kCodecParamMaxQuantization, &max_qp); - stream.max_qp = max_qp; - encoder_settings->streams.push_back(stream); + int min_bitrate = kMinVideoBitrate; + codec.GetParam(kCodecParamMinBitrate, &min_bitrate); + int max_bitrate = kMaxVideoBitrate; + codec.GetParam(kCodecParamMaxBitrate, &max_bitrate); + stream.min_bitrate_bps = min_bitrate * 1000; + stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate * 1000; - encoder_settings->encoder = webrtc::VP8Encoder::Create(); - encoder_settings->payload_type = kDefaultVideoCodecPref.payload_type; - encoder_settings->payload_name = kDefaultVideoCodecPref.name; + int max_qp = 56; + codec.GetParam(kCodecParamMaxQuantization, &max_qp); + stream.max_qp = max_qp; + std::vector<webrtc::VideoStream> streams; + streams.push_back(stream); + return streams; +} - return true; - } +webrtc::VideoEncoder* WebRtcVideoEncoderFactory2::CreateVideoEncoder( + const VideoCodec& codec, + const VideoOptions& options) { + assert(SupportsCodec(codec)); + return webrtc::VP8Encoder::Create(); +} - virtual bool SupportsCodec(const VideoCodec& codec) OVERRIDE { - return _stricmp(codec.name.c_str(), kVp8PayloadName) == 0; - } -}; +bool WebRtcVideoEncoderFactory2::SupportsCodec(const VideoCodec& codec) { + return _stricmp(codec.name.c_str(), kVp8PayloadName) == 0; +} WebRtcVideoEngine2::WebRtcVideoEngine2() { // Construct without a factory or voice engine. @@ -268,7 +278,6 @@ void WebRtcVideoEngine2::Construct(WebRtcVideoChannelFactory* channel_factory, video_codecs_ = DefaultVideoCodecs(); default_codec_format_ = VideoFormat(kDefaultVideoFormat); - default_video_encoder_factory_.reset(new DefaultVideoEncoderFactory()); } WebRtcVideoEngine2::~WebRtcVideoEngine2() { @@ -337,6 +346,7 @@ WebRtcVideoChannel2* WebRtcVideoEngine2::CreateChannel( delete channel; return NULL; } + channel->SetRecvCodecs(video_codecs_); return channel; } @@ -467,15 +477,15 @@ bool WebRtcVideoEngine2::ShouldIgnoreTrace(const std::string& trace) { return false; } -WebRtcVideoEncoderFactory2* WebRtcVideoEngine2::GetVideoEncoderFactory() const { - return default_video_encoder_factory_.get(); +WebRtcVideoEncoderFactory2* WebRtcVideoEngine2::GetVideoEncoderFactory() { + return &default_video_encoder_factory_; } -// Thin map between cricket::VideoFrame and an existing webrtc::I420VideoFrame +// Thin map between VideoFrame and an existing webrtc::I420VideoFrame // to avoid having to copy the rendered VideoFrame prematurely. // This implementation is only safe to use in a const context and should never // be written to. -class WebRtcVideoRenderFrame : public cricket::VideoFrame { +class WebRtcVideoRenderFrame : public VideoFrame { public: explicit WebRtcVideoRenderFrame(const webrtc::I420VideoFrame* frame) : frame_(frame) {} @@ -733,15 +743,6 @@ bool WebRtcVideoChannel2::Init() { return true; } namespace { -static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) { - for (size_t i = 0; i < codecs.size(); ++i) { - if (!codecs[i].ValidateCodecFormat()) { - return false; - } - } - return true; -} - static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) { std::stringstream out; out << '{'; @@ -755,6 +756,24 @@ static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) { return out.str(); } +static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) { + bool has_video = false; + for (size_t i = 0; i < codecs.size(); ++i) { + if (!codecs[i].ValidateCodecFormat()) { + return false; + } + if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) { + has_video = true; + } + } + if (!has_video) { + LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: " + << CodecVectorToString(codecs); + return false; + } + return true; +} + } // namespace bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) { @@ -860,30 +879,37 @@ static bool ConfigureSendSsrcs(webrtc::VideoSendStream::Config* config, return false; } - const SsrcGroup* sim_group = sp.get_ssrc_group(kSimSsrcGroupSemantics); - if (sim_group == NULL) { - LOG(LS_ERROR) << "Grouped StreamParams without regular SSRC group: " - << sp.ToString(); - return false; - } - // Map RTX SSRCs. + std::vector<uint32_t> ssrcs; std::vector<uint32_t> rtx_ssrcs; - for (size_t i = 0; i < sim_group->ssrcs.size(); ++i) { + const SsrcGroup* sim_group = sp.get_ssrc_group(kSimSsrcGroupSemantics); + if (sim_group == NULL) { + ssrcs.push_back(sp.first_ssrc()); uint32_t rtx_ssrc; - if (!sp.GetFidSsrc(sim_group->ssrcs[i], &rtx_ssrc)) { - continue; + if (!sp.GetFidSsrc(sp.first_ssrc(), &rtx_ssrc)) { + LOG(LS_ERROR) << "Could not find FID ssrc for primary SSRC '" + << sp.first_ssrc() << "':" << sp.ToString(); + return false; } rtx_ssrcs.push_back(rtx_ssrc); + } else { + ssrcs = sim_group->ssrcs; + for (size_t i = 0; i < sim_group->ssrcs.size(); ++i) { + uint32_t rtx_ssrc; + if (!sp.GetFidSsrc(sim_group->ssrcs[i], &rtx_ssrc)) { + continue; + } + rtx_ssrcs.push_back(rtx_ssrc); + } } - if (!rtx_ssrcs.empty() && sim_group->ssrcs.size() != rtx_ssrcs.size()) { + if (!rtx_ssrcs.empty() && ssrcs.size() != rtx_ssrcs.size()) { LOG(LS_ERROR) << "RTX SSRCs exist, but don't cover all SSRCs (unsupported): " << sp.ToString(); return false; } config->rtp.rtx.ssrcs = rtx_ssrcs; - config->rtp.ssrcs = sim_group->ssrcs; + config->rtp.ssrcs = ssrcs; return true; } @@ -924,25 +950,35 @@ bool WebRtcVideoChannel2::AddSendStream(const StreamParams& sp) { // CreateEncoderSettings will allocate a suitable VideoEncoder instance // matching current settings. - if (!encoder_factory_->CreateEncoderSettings(&config.encoder_settings, - options_, - codec_settings.codec, - config.rtp.ssrcs.size())) { - LOG(LS_ERROR) << "Failed to create suitable encoder settings."; + std::vector<webrtc::VideoStream> video_streams = + encoder_factory_->CreateVideoStreams( + codec_settings.codec, options_, config.rtp.ssrcs.size()); + if (video_streams.empty()) { return false; } + config.encoder_settings.encoder = + encoder_factory_->CreateVideoEncoder(codec_settings.codec, options_); + config.encoder_settings.payload_name = codec_settings.codec.name; + config.encoder_settings.payload_type = codec_settings.codec.id; config.rtp.c_name = sp.cname; config.rtp.fec = codec_settings.fec; if (!config.rtp.rtx.ssrcs.empty()) { config.rtp.rtx.payload_type = codec_settings.rtx_payload_type; } - config.rtp.nack.rtp_history_ms = kNackHistoryMs; + if (IsNackEnabled(codec_settings.codec)) { + config.rtp.nack.rtp_history_ms = kNackHistoryMs; + } config.rtp.max_packet_size = kVideoMtu; WebRtcVideoSendStream* stream = - new WebRtcVideoSendStream(call_.get(), config, encoder_factory_); + new WebRtcVideoSendStream(call_.get(), + config, + options_, + codec_settings.codec, + video_streams, + encoder_factory_); send_streams_[ssrc] = stream; if (rtcp_receiver_report_ssrc_ == kDefaultRtcpReceiverReportSsrc) { @@ -1006,15 +1042,10 @@ bool WebRtcVideoChannel2::AddRecvStream(const StreamParams& sp) { webrtc::VideoReceiveStream::Config config = call_->GetDefaultReceiveConfig(); config.rtp.remote_ssrc = ssrc; config.rtp.local_ssrc = rtcp_receiver_report_ssrc_; - uint32 rtx_ssrc = 0; - if (sp.GetFidSsrc(ssrc, &rtx_ssrc)) { - // TODO(pbos): Right now, VideoReceiveStream accepts any rtx payload, this - // should use the actual codec payloads that may be received. - // (for each receive payload, set rtx[payload].ssrc = rtx_ssrc. - config.rtp.rtx[0].ssrc = rtx_ssrc; - } - config.rtp.nack.rtp_history_ms = kNackHistoryMs; + if (IsNackEnabled(recv_codecs_.begin()->codec)) { + config.rtp.nack.rtp_history_ms = kNackHistoryMs; + } config.rtp.remb = true; // TODO(pbos): This protection is against setting the same local ssrc as // remote which is not permitted by the lower-level API. RTCP requires a @@ -1068,7 +1099,9 @@ bool WebRtcVideoChannel2::AddRecvStream(const StreamParams& sp) { for (size_t i = 0; i < recv_codecs_.size(); ++i) { if (recv_codecs_[i].codec.id == codec.plType) { config.rtp.fec = recv_codecs_[i].fec; - if (recv_codecs_[i].rtx_payload_type != -1 && rtx_ssrc != 0) { + uint32 rtx_ssrc; + if (recv_codecs_[i].rtx_payload_type != -1 && + sp.GetFidSsrc(ssrc, &rtx_ssrc)) { config.rtp.rtx[codec.plType].ssrc = rtx_ssrc; config.rtp.rtx[codec.plType].payload_type = recv_codecs_[i].rtx_payload_type; @@ -1339,21 +1372,35 @@ void WebRtcVideoChannel2::SetCodecForAllSendStreams( } } +WebRtcVideoChannel2::WebRtcVideoSendStream::VideoSendStreamParameters:: + VideoSendStreamParameters( + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams) + : config(config), + options(options), + codec(codec), + video_streams(video_streams) { +} + WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream( webrtc::Call* call, const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams, WebRtcVideoEncoderFactory2* encoder_factory) : call_(call), - config_(config), + parameters_(config, options, codec, video_streams), encoder_factory_(encoder_factory), capturer_(NULL), stream_(NULL), sending_(false), muted_(false), - format_(static_cast<int>(config.encoder_settings.streams.back().height), - static_cast<int>(config.encoder_settings.streams.back().width), - VideoFormat::FpsToInterval( - config.encoder_settings.streams.back().max_framerate), + format_(static_cast<int>(video_streams.back().height), + static_cast<int>(video_streams.back().width), + VideoFormat::FpsToInterval(video_streams.back().max_framerate), FOURCC_I420) { RecreateWebRtcStream(); } @@ -1361,7 +1408,7 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream( WebRtcVideoChannel2::WebRtcVideoSendStream::~WebRtcVideoSendStream() { DisconnectCapturer(); call_->DestroyVideoSendStream(stream_); - delete config_.encoder_settings.encoder; + delete parameters_.config.encoder_settings.encoder; } static void SetWebRtcFrameToBlack(webrtc::I420VideoFrame* video_frame) { @@ -1428,8 +1475,8 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::InputFrame( } LOG(LS_VERBOSE) << "SwapFrame: " << video_frame_.width() << "x" << video_frame_.height() << " -> (codec) " - << config_.encoder_settings.streams.back().width << "x" - << config_.encoder_settings.streams.back().height; + << parameters_.video_streams.back().width << "x" + << parameters_.video_streams.back().height; stream_->Input()->SwapFrame(&video_frame_); } @@ -1480,10 +1527,10 @@ bool WebRtcVideoChannel2::WebRtcVideoSendStream::SetVideoFormat( if (format.width == 0 && format.height == 0) { LOG(LS_INFO) << "0x0 resolution selected. Captured frames will be dropped for ssrc: " - << config_.rtp.ssrcs[0] << "."; + << parameters_.config.rtp.ssrcs[0] << "."; } else { // TODO(pbos): Fix me, this only affects the last stream! - config_.encoder_settings.streams.back().max_framerate = + parameters_.video_streams.back().max_framerate = VideoFormat::IntervalToFps(format.interval); SetDimensions(format.width, format.height); } @@ -1513,44 +1560,46 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodec( const VideoOptions& options, const VideoCodecSettings& codec) { talk_base::CritScope cs(&lock_); - webrtc::VideoEncoder* old_encoder = config_.encoder_settings.encoder; - if (!encoder_factory_->CreateEncoderSettings( - &config_.encoder_settings, - options, - codec.codec, - config_.encoder_settings.streams.size())) { - LOG(LS_ERROR) << "Could not create encoder settings for: '" - << codec.codec.name - << "'. This is most definitely a bug as SetCodec should only " - "receive codecs which the encoder factory claims to " - "support."; + + std::vector<webrtc::VideoStream> video_streams = + encoder_factory_->CreateVideoStreams( + codec.codec, options, parameters_.video_streams.size()); + if (video_streams.empty()) { return; } + parameters_.video_streams = video_streams; format_ = VideoFormat(codec.codec.width, codec.codec.height, VideoFormat::FpsToInterval(30), FOURCC_I420); - config_.rtp.fec = codec.fec; + + webrtc::VideoEncoder* old_encoder = + parameters_.config.encoder_settings.encoder; + parameters_.config.encoder_settings.encoder = + encoder_factory_->CreateVideoEncoder(codec.codec, options); + parameters_.config.rtp.fec = codec.fec; // TODO(pbos): Should changing RTX payload type be allowed? + parameters_.codec = codec.codec; + parameters_.options = options; RecreateWebRtcStream(); delete old_encoder; } void WebRtcVideoChannel2::WebRtcVideoSendStream::SetDimensions(int width, - int height) { - assert(!config_.encoder_settings.streams.empty()); + int height) { + assert(!parameters_.video_streams.empty()); LOG(LS_VERBOSE) << "SetDimensions: " << width << "x" << height; - if (config_.encoder_settings.streams.back().width == width && - config_.encoder_settings.streams.back().height == height) { + if (parameters_.video_streams.back().width == width && + parameters_.video_streams.back().height == height) { return; } // TODO(pbos): Fix me, this only affects the last stream! - config_.encoder_settings.streams.back().width = width; - config_.encoder_settings.streams.back().height = height; - // TODO(pbos): Last parameter shouldn't always be NULL? - if (!stream_->ReconfigureVideoEncoder(config_.encoder_settings.streams, - NULL)) { + parameters_.video_streams.back().width = width; + parameters_.video_streams.back().height = height; + + // TODO(pbos): Wire up encoder_parameters, webrtc:3424. + if (!stream_->ReconfigureVideoEncoder(parameters_.video_streams, NULL)) { LOG(LS_WARNING) << "Failed to reconfigure video encoder for dimensions: " << width << "x" << height; return; @@ -1573,7 +1622,10 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::RecreateWebRtcStream() { if (stream_ != NULL) { call_->DestroyVideoSendStream(stream_); } - stream_ = call_->CreateVideoSendStream(config_); + + // TODO(pbos): Wire up encoder_parameters, webrtc:3424. + stream_ = call_->CreateVideoSendStream( + parameters_.config, parameters_.video_streams, NULL); if (sending_) { stream_->Start(); } @@ -1588,6 +1640,7 @@ WebRtcVideoChannel2::MapCodecs(const std::vector<VideoCodec>& codecs) { std::vector<VideoCodecSettings> video_codecs; std::map<int, bool> payload_used; + std::map<int, VideoCodec::CodecType> payload_codec_type; std::map<int, int> rtx_mapping; // video payload type -> rtx payload type. webrtc::FecConfig fec_settings; @@ -1602,6 +1655,7 @@ WebRtcVideoChannel2::MapCodecs(const std::vector<VideoCodec>& codecs) { return std::vector<VideoCodecSettings>(); } payload_used[payload_type] = true; + payload_codec_type[payload_type] = in_codec.GetCodecType(); switch (in_codec.GetCodecType()) { case VideoCodec::CODEC_RED: { @@ -1642,6 +1696,19 @@ WebRtcVideoChannel2::MapCodecs(const std::vector<VideoCodec>& codecs) { // parameters into this code is a logic error. assert(!video_codecs.empty()); + for (std::map<int, int>::const_iterator it = rtx_mapping.begin(); + it != rtx_mapping.end(); + ++it) { + if (!payload_used[it->first]) { + LOG(LS_ERROR) << "RTX mapped to payload not in codec list."; + return std::vector<VideoCodecSettings>(); + } + if (payload_codec_type[it->first] != VideoCodec::CODEC_VIDEO) { + LOG(LS_ERROR) << "RTX not mapped to regular video codec."; + return std::vector<VideoCodecSettings>(); + } + } + // TODO(pbos): Write tests that figure out that I have not verified that RTX // codecs aren't mapped to bogus payloads. for (size_t i = 0; i < video_codecs.size(); ++i) { diff --git a/media/webrtc/webrtcvideoengine2.h b/media/webrtc/webrtcvideoengine2.h index 10a1608..d1a784d 100644 --- a/media/webrtc/webrtcvideoengine2.h +++ b/media/webrtc/webrtcvideoengine2.h @@ -83,12 +83,16 @@ class WebRtcVideoChannel2; class WebRtcVideoEncoderFactory2 { public: virtual ~WebRtcVideoEncoderFactory2(); - virtual bool CreateEncoderSettings( - webrtc::VideoSendStream::Config::EncoderSettings* encoder_settings, + virtual std::vector<webrtc::VideoStream> CreateVideoStreams( + const VideoCodec& codec, const VideoOptions& options, - const cricket::VideoCodec& codec, - size_t num_streams) = 0; - virtual bool SupportsCodec(const cricket::VideoCodec& codec) = 0; + size_t num_streams); + + virtual webrtc::VideoEncoder* CreateVideoEncoder( + const VideoCodec& codec, + const VideoOptions& options); + + virtual bool SupportsCodec(const cricket::VideoCodec& codec); }; // WebRtcVideoEngine2 is used for the new native WebRTC Video API (webrtc:1667). @@ -140,7 +144,7 @@ class WebRtcVideoEngine2 : public sigslot::has_slots<> { talk_base::CpuMonitor* cpu_monitor() { return cpu_monitor_.get(); } - virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory() const; + virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory(); private: void Construct(WebRtcVideoChannelFactory* channel_factory, @@ -163,8 +167,7 @@ class WebRtcVideoEngine2 : public sigslot::has_slots<> { talk_base::scoped_ptr<talk_base::CpuMonitor> cpu_monitor_; WebRtcVideoChannelFactory* channel_factory_; - talk_base::scoped_ptr<WebRtcVideoEncoderFactory2> - default_video_encoder_factory_; + WebRtcVideoEncoderFactory2 default_video_encoder_factory_; }; // Adapter between webrtc::VideoRenderer and cricket::VideoRenderer. @@ -258,7 +261,7 @@ class WebRtcVideoChannel2 : public talk_base::MessageHandler, struct VideoCodecSettings { VideoCodecSettings(); - cricket::VideoCodec codec; + VideoCodec codec; webrtc::FecConfig fec; int rtx_payload_type; }; @@ -266,8 +269,11 @@ class WebRtcVideoChannel2 : public talk_base::MessageHandler, class WebRtcVideoSendStream : public sigslot::has_slots<> { public: WebRtcVideoSendStream(webrtc::Call* call, - const webrtc::VideoSendStream::Config& config, - WebRtcVideoEncoderFactory2* encoder_factory); + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams, + WebRtcVideoEncoderFactory2* encoder_factory); ~WebRtcVideoSendStream(); void SetCodec(const VideoOptions& options, const VideoCodecSettings& codec); @@ -281,6 +287,25 @@ class WebRtcVideoChannel2 : public talk_base::MessageHandler, void Stop(); private: + // Parameters needed to reconstruct the underlying stream. + // webrtc::VideoSendStream doesn't support setting a lot of options on the + // fly, so when those need to be changed we tear down and reconstruct with + // similar parameters depending on which options changed etc. + struct VideoSendStreamParameters { + VideoSendStreamParameters( + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams); + webrtc::VideoSendStream::Config config; + VideoOptions options; + VideoCodec codec; + // Sent resolutions + bitrates etc. by the underlying VideoSendStream, + // typically changes when setting a new resolution or reconfiguring + // bitrates. + std::vector<webrtc::VideoStream> video_streams; + }; + void RecreateWebRtcStream(); void SetDimensions(int width, int height); @@ -289,7 +314,8 @@ class WebRtcVideoChannel2 : public talk_base::MessageHandler, talk_base::CriticalSection lock_; webrtc::VideoSendStream* stream_ GUARDED_BY(lock_); - webrtc::VideoSendStream::Config config_ GUARDED_BY(lock_); + VideoSendStreamParameters parameters_ GUARDED_BY(lock_); + VideoCapturer* capturer_ GUARDED_BY(lock_); bool sending_ GUARDED_BY(lock_); bool muted_ GUARDED_BY(lock_); diff --git a/media/webrtc/webrtcvideoengine2_unittest.cc b/media/webrtc/webrtcvideoengine2_unittest.cc index bba1455..c9ff182 100644 --- a/media/webrtc/webrtcvideoengine2_unittest.cc +++ b/media/webrtc/webrtcvideoengine2_unittest.cc @@ -26,13 +26,14 @@ */ #include <map> +#include <vector> #include "talk/base/gunit.h" #include "talk/media/base/testutils.h" #include "talk/media/base/videoengine_unittest.h" #include "talk/media/webrtc/webrtcvideoengine2.h" +#include "talk/media/webrtc/webrtcvideoengine2_unittest.h" #include "talk/media/webrtc/webrtcvideochannelfactory.h" -#include "webrtc/call.h" namespace { static const cricket::VideoCodec kVp8Codec720p(100, "VP8", 1280, 720, 30, 0); @@ -47,250 +48,259 @@ static const cricket::VideoCodec kUlpfecCodec(117, "ulpfec", 0, 0, 0, 0); static const uint32 kSsrcs1[] = {1}; static const uint32 kRtxSsrcs1[] = {4}; + +void VerifyCodecHasDefaultFeedbackParams(const cricket::VideoCodec& codec) { + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamNack, cricket::kParamValueEmpty))); + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli))); + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty))); + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir))); +} + } // namespace namespace cricket { -class FakeVideoSendStream : public webrtc::VideoSendStream { - public: - explicit FakeVideoSendStream(const webrtc::VideoSendStream::Config& config) - : sending_(false) { - config_ = config; - } +FakeVideoSendStream::FakeVideoSendStream( + const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams) + : sending_(false), config_(config), video_streams_(video_streams) { +} - webrtc::VideoSendStream::Config GetConfig() { return config_; } +webrtc::VideoSendStream::Config FakeVideoSendStream::GetConfig() { + return config_; +} - bool IsSending() { return sending_; } +std::vector<webrtc::VideoStream> FakeVideoSendStream::GetVideoStreams() { + return video_streams_; +} - private: - virtual webrtc::VideoSendStream::Stats GetStats() const OVERRIDE { - return webrtc::VideoSendStream::Stats(); - } +bool FakeVideoSendStream::IsSending() { + return sending_; +} - virtual bool ReconfigureVideoEncoder( - const std::vector<webrtc::VideoStream>& streams, - void* encoder_specific) OVERRIDE { - // TODO(pbos): Store encoder_specific ptr? - config_.encoder_settings.streams = streams; - return true; - } +webrtc::VideoSendStream::Stats FakeVideoSendStream::GetStats() const { + return webrtc::VideoSendStream::Stats(); +} - virtual webrtc::VideoSendStreamInput* Input() OVERRIDE { - // TODO(pbos): Fix. - return NULL; - } +bool FakeVideoSendStream::ReconfigureVideoEncoder( + const std::vector<webrtc::VideoStream>& streams, + const void* encoder_specific) { + video_streams_ = streams; + return true; +} - virtual void Start() OVERRIDE { sending_ = true; } +webrtc::VideoSendStreamInput* FakeVideoSendStream::Input() { + // TODO(pbos): Fix. + return NULL; +} - virtual void Stop() OVERRIDE { sending_ = false; } +void FakeVideoSendStream::Start() { + sending_ = true; +} - bool sending_; - webrtc::VideoSendStream::Config config_; -}; +void FakeVideoSendStream::Stop() { + sending_ = false; +} -class FakeVideoReceiveStream : public webrtc::VideoReceiveStream { - public: - explicit FakeVideoReceiveStream( - const webrtc::VideoReceiveStream::Config& config) - : config_(config), receiving_(false) {} +FakeVideoReceiveStream::FakeVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config) + : config_(config), receiving_(false) { +} - webrtc::VideoReceiveStream::Config GetConfig() { return config_; } +webrtc::VideoReceiveStream::Config FakeVideoReceiveStream::GetConfig() { + return config_; +} - private: - virtual webrtc::VideoReceiveStream::Stats GetStats() const OVERRIDE { - return webrtc::VideoReceiveStream::Stats(); - } +webrtc::VideoReceiveStream::Stats FakeVideoReceiveStream::GetStats() const { + return webrtc::VideoReceiveStream::Stats(); +} - virtual void Start() OVERRIDE { receiving_ = true; } - virtual void Stop() OVERRIDE { receiving_ = false; } - virtual void GetCurrentReceiveCodec(webrtc::VideoCodec* codec) OVERRIDE {} +void FakeVideoReceiveStream::Start() { + receiving_ = true; +} +void FakeVideoReceiveStream::Stop() { + receiving_ = false; +} +void FakeVideoReceiveStream::GetCurrentReceiveCodec(webrtc::VideoCodec* codec) { +} - webrtc::VideoReceiveStream::Config config_; - bool receiving_; -}; +FakeCall::FakeCall() { SetVideoCodecs(GetDefaultVideoCodecs()); } -class FakeCall : public webrtc::Call { - public: - FakeCall() { SetVideoCodecs(GetDefaultVideoCodecs()); } +FakeCall::~FakeCall() { + EXPECT_EQ(0u, video_send_streams_.size()); + EXPECT_EQ(0u, video_receive_streams_.size()); +} - ~FakeCall() { - EXPECT_EQ(0u, video_send_streams_.size()); - EXPECT_EQ(0u, video_receive_streams_.size()); - } +void FakeCall::SetVideoCodecs(const std::vector<webrtc::VideoCodec> codecs) { + codecs_ = codecs; +} - void SetVideoCodecs(const std::vector<webrtc::VideoCodec> codecs) { - codecs_ = codecs; - } +std::vector<FakeVideoSendStream*> FakeCall::GetVideoSendStreams() { + return video_send_streams_; +} - std::vector<FakeVideoSendStream*> GetVideoSendStreams() { - return video_send_streams_; - } +std::vector<FakeVideoReceiveStream*> FakeCall::GetVideoReceiveStreams() { + return video_receive_streams_; +} - std::vector<FakeVideoReceiveStream*> GetVideoReceiveStreams() { - return video_receive_streams_; - } +webrtc::VideoCodec FakeCall::GetEmptyVideoCodec() { + webrtc::VideoCodec codec; + codec.minBitrate = 300; + codec.startBitrate = 800; + codec.maxBitrate = 1500; + codec.maxFramerate = 10; + codec.width = 640; + codec.height = 480; + codec.qpMax = 56; - webrtc::VideoCodec GetEmptyVideoCodec() { - webrtc::VideoCodec codec; - codec.minBitrate = 300; - codec.startBitrate = 800; - codec.maxBitrate = 1500; - codec.maxFramerate = 10; - codec.width = 640; - codec.height = 480; - codec.qpMax = 56; - - return codec; - } + return codec; +} - webrtc::VideoCodec GetVideoCodecVp8() { - webrtc::VideoCodec vp8_codec = GetEmptyVideoCodec(); - vp8_codec.codecType = webrtc::kVideoCodecVP8; - strcpy(vp8_codec.plName, kVp8Codec.name.c_str()); - vp8_codec.plType = kVp8Codec.id; +webrtc::VideoCodec FakeCall::GetVideoCodecVp8() { + webrtc::VideoCodec vp8_codec = GetEmptyVideoCodec(); + vp8_codec.codecType = webrtc::kVideoCodecVP8; + strcpy(vp8_codec.plName, kVp8Codec.name.c_str()); + vp8_codec.plType = kVp8Codec.id; - return vp8_codec; - } + return vp8_codec; +} - webrtc::VideoCodec GetVideoCodecVp9() { - webrtc::VideoCodec vp9_codec = GetEmptyVideoCodec(); - // TODO(pbos): Add a correct codecType when webrtc has one. - vp9_codec.codecType = webrtc::kVideoCodecVP8; - strcpy(vp9_codec.plName, kVp9Codec.name.c_str()); - vp9_codec.plType = kVp9Codec.id; +webrtc::VideoCodec FakeCall::GetVideoCodecVp9() { + webrtc::VideoCodec vp9_codec = GetEmptyVideoCodec(); + // TODO(pbos): Add a correct codecType when webrtc has one. + vp9_codec.codecType = webrtc::kVideoCodecVP8; + strcpy(vp9_codec.plName, kVp9Codec.name.c_str()); + vp9_codec.plType = kVp9Codec.id; - return vp9_codec; - } + return vp9_codec; +} - std::vector<webrtc::VideoCodec> GetDefaultVideoCodecs() { - std::vector<webrtc::VideoCodec> codecs; - codecs.push_back(GetVideoCodecVp8()); - // codecs.push_back(GetVideoCodecVp9()); +std::vector<webrtc::VideoCodec> FakeCall::GetDefaultVideoCodecs() { + std::vector<webrtc::VideoCodec> codecs; + codecs.push_back(GetVideoCodecVp8()); + // codecs.push_back(GetVideoCodecVp9()); - return codecs; - } + return codecs; +} - private: - virtual webrtc::VideoSendStream::Config GetDefaultSendConfig() OVERRIDE { - webrtc::VideoSendStream::Config config; - // TODO(pbos): Encoder settings. - // config.codec = GetVideoCodecVp8(); - return config; - } +webrtc::VideoSendStream::Config FakeCall::GetDefaultSendConfig() { + webrtc::VideoSendStream::Config config; + // TODO(pbos): Encoder settings. + // config.codec = GetVideoCodecVp8(); + return config; +} - virtual webrtc::VideoSendStream* CreateVideoSendStream( - const webrtc::VideoSendStream::Config& config) OVERRIDE { - FakeVideoSendStream* fake_stream = new FakeVideoSendStream(config); - video_send_streams_.push_back(fake_stream); - return fake_stream; - } +webrtc::VideoSendStream* FakeCall::CreateVideoSendStream( + const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams, + const void* encoder_settings) { + FakeVideoSendStream* fake_stream = + new FakeVideoSendStream(config, video_streams); + video_send_streams_.push_back(fake_stream); + return fake_stream; +} - virtual void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) - OVERRIDE { - FakeVideoSendStream* fake_stream = - static_cast<FakeVideoSendStream*>(send_stream); - for (size_t i = 0; i < video_send_streams_.size(); ++i) { - if (video_send_streams_[i] == fake_stream) { - delete video_send_streams_[i]; - video_send_streams_.erase(video_send_streams_.begin() + i); - return; - } +void FakeCall::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { + FakeVideoSendStream* fake_stream = + static_cast<FakeVideoSendStream*>(send_stream); + for (size_t i = 0; i < video_send_streams_.size(); ++i) { + if (video_send_streams_[i] == fake_stream) { + delete video_send_streams_[i]; + video_send_streams_.erase(video_send_streams_.begin() + i); + return; } - ADD_FAILURE() << "DestroyVideoSendStream called with unknown paramter."; } + ADD_FAILURE() << "DestroyVideoSendStream called with unknown paramter."; +} - virtual webrtc::VideoReceiveStream::Config GetDefaultReceiveConfig() - OVERRIDE { - return webrtc::VideoReceiveStream::Config(); - } +webrtc::VideoReceiveStream::Config FakeCall::GetDefaultReceiveConfig() { + return webrtc::VideoReceiveStream::Config(); +} - virtual webrtc::VideoReceiveStream* CreateVideoReceiveStream( - const webrtc::VideoReceiveStream::Config& config) OVERRIDE { - video_receive_streams_.push_back(new FakeVideoReceiveStream(config)); - return video_receive_streams_[video_receive_streams_.size() - 1]; - } +webrtc::VideoReceiveStream* FakeCall::CreateVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config) { + video_receive_streams_.push_back(new FakeVideoReceiveStream(config)); + return video_receive_streams_[video_receive_streams_.size() - 1]; +} - virtual void DestroyVideoReceiveStream( - webrtc::VideoReceiveStream* receive_stream) OVERRIDE { - FakeVideoReceiveStream* fake_stream = - static_cast<FakeVideoReceiveStream*>(receive_stream); - for (size_t i = 0; i < video_receive_streams_.size(); ++i) { - if (video_receive_streams_[i] == fake_stream) { - delete video_receive_streams_[i]; - video_receive_streams_.erase(video_receive_streams_.begin() + i); - return; - } +void FakeCall::DestroyVideoReceiveStream( + webrtc::VideoReceiveStream* receive_stream) { + FakeVideoReceiveStream* fake_stream = + static_cast<FakeVideoReceiveStream*>(receive_stream); + for (size_t i = 0; i < video_receive_streams_.size(); ++i) { + if (video_receive_streams_[i] == fake_stream) { + delete video_receive_streams_[i]; + video_receive_streams_.erase(video_receive_streams_.begin() + i); + return; } - ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown paramter."; } + ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown paramter."; +} - virtual webrtc::PacketReceiver* Receiver() OVERRIDE { - // TODO(pbos): Fix this. - return NULL; - } - - virtual uint32_t SendBitrateEstimate() OVERRIDE { return 0; } - - virtual uint32_t ReceiveBitrateEstimate() OVERRIDE { return 0; } - - private: - std::vector<webrtc::VideoCodec> codecs_; - std::vector<FakeVideoSendStream*> video_send_streams_; - std::vector<FakeVideoReceiveStream*> video_receive_streams_; -}; +webrtc::PacketReceiver* FakeCall::Receiver() { + // TODO(pbos): Fix this. + return NULL; +} -class FakeWebRtcVideoChannel2 : public WebRtcVideoChannel2 { - public: - FakeWebRtcVideoChannel2(FakeCall* call, - WebRtcVideoEngine2* engine, - VoiceMediaChannel* voice_channel) - : WebRtcVideoChannel2(call, engine, engine->GetVideoEncoderFactory()), - fake_call_(call), - voice_channel_(voice_channel) {} +uint32_t FakeCall::SendBitrateEstimate() { + return 0; +} - virtual ~FakeWebRtcVideoChannel2() {} +uint32_t FakeCall::ReceiveBitrateEstimate() { + return 0; +} - VoiceMediaChannel* GetVoiceChannel() { return voice_channel_; } - FakeCall* GetFakeCall() { return fake_call_; } +FakeWebRtcVideoChannel2::FakeWebRtcVideoChannel2( + FakeCall* call, + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) + : WebRtcVideoChannel2(call, engine, engine->GetVideoEncoderFactory()), + fake_call_(call), + voice_channel_(voice_channel) { +} - private: - FakeCall* fake_call_; - VoiceMediaChannel* voice_channel_; -}; +FakeWebRtcVideoChannel2::~FakeWebRtcVideoChannel2() { +} -class FakeWebRtcVideoMediaChannelFactory : public WebRtcVideoChannelFactory { - public: - FakeWebRtcVideoChannel2* GetFakeChannel(VideoMediaChannel* channel) { - return channel_map_[channel]; - } +VoiceMediaChannel* FakeWebRtcVideoChannel2::GetVoiceChannel() { + return voice_channel_; +} +FakeCall* FakeWebRtcVideoChannel2::GetFakeCall() { + return fake_call_; +} - private: - virtual WebRtcVideoChannel2* Create(WebRtcVideoEngine2* engine, - VoiceMediaChannel* voice_channel) - OVERRIDE { - FakeWebRtcVideoChannel2* channel = - new FakeWebRtcVideoChannel2(new FakeCall(), engine, voice_channel); - channel_map_[channel] = channel; - return channel; - } +FakeWebRtcVideoChannel2* FakeWebRtcVideoMediaChannelFactory::GetFakeChannel( + VideoMediaChannel* channel) { + return channel_map_[channel]; +} - std::map<VideoMediaChannel*, FakeWebRtcVideoChannel2*> channel_map_; -}; +WebRtcVideoChannel2* FakeWebRtcVideoMediaChannelFactory::Create( + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) { + FakeWebRtcVideoChannel2* channel = + new FakeWebRtcVideoChannel2(new FakeCall(), engine, voice_channel); + channel_map_[channel] = channel; + return channel; +} class WebRtcVideoEngine2Test : public testing::Test { public: - WebRtcVideoEngine2Test() - : engine_(&factory_), engine_codecs_(engine_.codecs()) { - assert(!engine_codecs_.empty()); + WebRtcVideoEngine2Test() : engine_(&factory_) { + std::vector<VideoCodec> engine_codecs = engine_.codecs(); + assert(!engine_codecs.empty()); bool codec_set = false; - for (size_t i = 0; i < engine_codecs_.size(); ++i) { - if (engine_codecs_[i].name == "red") { - default_red_codec_ = engine_codecs_[i]; - } else if (engine_codecs_[i].name == "ulpfec") { - default_ulpfec_codec_ = engine_codecs_[i]; - } else if (engine_codecs_[i].name == "rtx") { - default_rtx_codec_ = engine_codecs_[i]; + for (size_t i = 0; i < engine_codecs.size(); ++i) { + if (engine_codecs[i].name == "red") { + default_red_codec_ = engine_codecs[i]; + } else if (engine_codecs[i].name == "ulpfec") { + default_ulpfec_codec_ = engine_codecs[i]; + } else if (engine_codecs[i].name == "rtx") { + default_rtx_codec_ = engine_codecs[i]; } else if (!codec_set) { - default_codec_ = engine_codecs_[i]; + default_codec_ = engine_codecs[i]; codec_set = true; } } @@ -305,8 +315,6 @@ class WebRtcVideoEngine2Test : public testing::Test { VideoCodec default_red_codec_; VideoCodec default_ulpfec_codec_; VideoCodec default_rtx_codec_; - // TODO(pbos): Remove engine_codecs_ unless used a lot. - std::vector<VideoCodec> engine_codecs_; }; TEST_F(WebRtcVideoEngine2Test, CreateChannel) { @@ -332,6 +340,62 @@ TEST_F(WebRtcVideoEngine2Test, CreateChannelWithVoiceEngine) { << "Different VoiceChannel set than the provided one."; } +TEST_F(WebRtcVideoEngine2Test, FindCodec) { + const std::vector<cricket::VideoCodec>& c = engine_.codecs(); + EXPECT_EQ(4U, c.size()); + + cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8)); + + cricket::VideoCodec vp8_ci(104, "vp8", 320, 200, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8)); + + cricket::VideoCodec vp8_diff_fr_diff_pref(104, "VP8", 320, 200, 50, 50); + EXPECT_TRUE(engine_.FindCodec(vp8_diff_fr_diff_pref)); + + cricket::VideoCodec vp8_diff_id(95, "VP8", 320, 200, 30, 0); + EXPECT_FALSE(engine_.FindCodec(vp8_diff_id)); + vp8_diff_id.id = 97; + EXPECT_TRUE(engine_.FindCodec(vp8_diff_id)); + + cricket::VideoCodec vp8_diff_res(104, "VP8", 320, 111, 30, 0); + EXPECT_FALSE(engine_.FindCodec(vp8_diff_res)); + + // PeerConnection doesn't negotiate the resolution at this point. + // Test that FindCodec can handle the case when width/height is 0. + cricket::VideoCodec vp8_zero_res(104, "VP8", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8_zero_res)); + + cricket::VideoCodec red(101, "RED", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(red)); + + cricket::VideoCodec red_ci(101, "red", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(red)); + + cricket::VideoCodec fec(102, "ULPFEC", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(fec)); + + cricket::VideoCodec fec_ci(102, "ulpfec", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(fec)); + + cricket::VideoCodec rtx(96, "rtx", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(rtx)); +} + +TEST_F(WebRtcVideoEngine2Test, DefaultRtxCodecHasAssociatedPayloadTypeSet) { + std::vector<VideoCodec> engine_codecs = engine_.codecs(); + for (size_t i = 0; i < engine_codecs.size(); ++i) { + if (engine_codecs[i].name != kRtxCodecName) + continue; + int associated_payload_type; + EXPECT_TRUE(engine_codecs[i].GetParam(kCodecParamAssociatedPayloadType, + &associated_payload_type)); + EXPECT_EQ(default_codec_.id, associated_payload_type); + return; + } + FAIL() << "No RTX codec found among default codecs."; +} + class WebRtcVideoChannel2BaseTest : public VideoMediaChannelTest<WebRtcVideoEngine2, WebRtcVideoChannel2> { protected: @@ -515,13 +579,10 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test { FakeVideoSendStream* stream = AddSendStream(); - webrtc::VideoSendStream::Config::EncoderSettings encoder_settings = - stream->GetConfig().encoder_settings; - ASSERT_EQ(1u, encoder_settings.streams.size()); - EXPECT_EQ(atoi(min_bitrate), - encoder_settings.streams.back().min_bitrate_bps / 1000); - EXPECT_EQ(atoi(max_bitrate), - encoder_settings.streams.back().max_bitrate_bps / 1000); + std::vector<webrtc::VideoStream> video_streams = stream->GetVideoStreams(); + ASSERT_EQ(1u, video_streams.size()); + EXPECT_EQ(atoi(min_bitrate), video_streams.back().min_bitrate_bps / 1000); + EXPECT_EQ(atoi(max_bitrate), video_streams.back().max_bitrate_bps / 1000); VideoCodec codec; EXPECT_TRUE(channel_->GetSendCodec(&codec)); @@ -556,15 +617,14 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_StartSendBitrate) { const unsigned int kVideoTargetSendBitrateKbps = 300; const unsigned int kVideoMaxSendBitrateKbps = 2000; FakeVideoSendStream* stream = AddSendStream(); - webrtc::VideoSendStream::Config::EncoderSettings encoder_settings = - stream->GetConfig().encoder_settings; - ASSERT_EQ(1u, encoder_settings.streams.size()); + std::vector<webrtc::VideoStream> video_streams = stream->GetVideoStreams(); + ASSERT_EQ(1u, video_streams.size()); EXPECT_EQ(kVideoMinSendBitrateKbps, - encoder_settings.streams.back().min_bitrate_bps / 1000); + video_streams.back().min_bitrate_bps / 1000); EXPECT_EQ(kVideoTargetSendBitrateKbps, - encoder_settings.streams.back().target_bitrate_bps / 1000); + video_streams.back().target_bitrate_bps / 1000); EXPECT_EQ(kVideoMaxSendBitrateKbps, - encoder_settings.streams.back().max_bitrate_bps / 1000); + video_streams.back().max_bitrate_bps / 1000); #if 0 // TODO(pbos): un-#if VerifyVP8SendCodec(send_channel, kVP8Codec.width, kVP8Codec.height, 0, @@ -615,7 +675,7 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_RembEnabledOnReceiveChannels) { FAIL() << "Not implemented."; // TODO(pbos): Implement. } -TEST_F(WebRtcVideoChannel2Test, RecvStreamWithRtx) { +TEST_F(WebRtcVideoChannel2Test, RecvStreamWithSimAndRtx) { EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); EXPECT_TRUE(channel_->SetSend(true)); cricket::VideoOptions options; @@ -644,12 +704,23 @@ TEST_F(WebRtcVideoChannel2Test, RecvStreamWithRtx) { // TODO(pbos): Make sure we set the RTX for correct payloads etc. } -TEST_F(WebRtcVideoChannel2Test, DISABLED_RecvStreamWithRtxOnMultiplePayloads) { - FAIL() << "Not implemented."; +TEST_F(WebRtcVideoChannel2Test, RecvStreamWithRtx) { + // Setup one channel with an associated RTX stream. + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs1[0]); + params.AddFidSsrc(kSsrcs1[0], kRtxSsrcs1[0]); + FakeVideoReceiveStream* recv_stream = AddRecvStream(params); + ASSERT_EQ(1u, recv_stream->GetConfig().rtp.rtx.size()); + EXPECT_EQ(kRtxSsrcs1[0], + recv_stream->GetConfig().rtp.rtx.begin()->second.ssrc); } -TEST_F(WebRtcVideoChannel2Test, DISABLED_RecvStreamNoRtx) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. +TEST_F(WebRtcVideoChannel2Test, RecvStreamNoRtx) { + // Setup one channel without an associated RTX stream. + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs1[0]); + FakeVideoReceiveStream* recv_stream = AddRecvStream(params); + ASSERT_TRUE(recv_stream->GetConfig().rtp.rtx.empty()); } TEST_F(WebRtcVideoChannel2Test, DISABLED_RtpTimestampOffsetHeaderExtensions) { @@ -685,7 +756,9 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_RembOnOff) { FAIL() << "Not implemented."; // TODO(pbos): Implement. } -TEST_F(WebRtcVideoChannel2Test, NackIsEnabled) { +TEST_F(WebRtcVideoChannel2Test, NackIsEnabledByDefault) { + VerifyCodecHasDefaultFeedbackParams(default_codec_); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); EXPECT_TRUE(channel_->SetSend(true)); @@ -704,6 +777,23 @@ TEST_F(WebRtcVideoChannel2Test, NackIsEnabled) { recv_stream->GetConfig().rtp.nack.rtp_history_ms); } +TEST_F(WebRtcVideoChannel2Test, NackCanBeDisabled) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + + // Send side. + ASSERT_TRUE(channel_->SetSendCodecs(codecs)); + FakeVideoSendStream* send_stream = + AddSendStream(cricket::StreamParams::CreateLegacy(1)); + EXPECT_EQ(0, send_stream->GetConfig().rtp.nack.rtp_history_ms); + + // Receiver side. + ASSERT_TRUE(channel_->SetRecvCodecs(codecs)); + FakeVideoReceiveStream* recv_stream = + AddRecvStream(cricket::StreamParams::CreateLegacy(1)); + EXPECT_EQ(0, recv_stream->GetConfig().rtp.nack.rtp_history_ms); +} + TEST_F(WebRtcVideoChannel2Test, DISABLED_VideoProtectionInterop) { FAIL() << "Not implemented."; // TODO(pbos): Implement. } @@ -785,67 +875,12 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_WebRtcShouldNotLog) { FAIL() << "Not implemented."; // TODO(pbos): Implement. } -TEST_F(WebRtcVideoEngine2Test, FindCodec) { - const std::vector<cricket::VideoCodec>& c = engine_.codecs(); - EXPECT_EQ(4U, c.size()); - - cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0); - EXPECT_TRUE(engine_.FindCodec(vp8)); - - cricket::VideoCodec vp8_ci(104, "vp8", 320, 200, 30, 0); - EXPECT_TRUE(engine_.FindCodec(vp8)); - - cricket::VideoCodec vp8_diff_fr_diff_pref(104, "VP8", 320, 200, 50, 50); - EXPECT_TRUE(engine_.FindCodec(vp8_diff_fr_diff_pref)); - - cricket::VideoCodec vp8_diff_id(95, "VP8", 320, 200, 30, 0); - EXPECT_FALSE(engine_.FindCodec(vp8_diff_id)); - vp8_diff_id.id = 97; - EXPECT_TRUE(engine_.FindCodec(vp8_diff_id)); - - cricket::VideoCodec vp8_diff_res(104, "VP8", 320, 111, 30, 0); - EXPECT_FALSE(engine_.FindCodec(vp8_diff_res)); - - // PeerConnection doesn't negotiate the resolution at this point. - // Test that FindCodec can handle the case when width/height is 0. - cricket::VideoCodec vp8_zero_res(104, "VP8", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(vp8_zero_res)); - - cricket::VideoCodec red(101, "RED", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(red)); - - cricket::VideoCodec red_ci(101, "red", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(red)); - - cricket::VideoCodec fec(102, "ULPFEC", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(fec)); - - cricket::VideoCodec fec_ci(102, "ulpfec", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(fec)); - - cricket::VideoCodec rtx(96, "rtx", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(rtx)); -} - -TEST_F(WebRtcVideoEngine2Test, DefaultRtxCodecHasAssociatedPayloadTypeSet) { - for (size_t i = 0; i < engine_codecs_.size(); ++i) { - if (engine_codecs_[i].name != kRtxCodecName) - continue; - int associated_payload_type; - EXPECT_TRUE(engine_codecs_[i].GetParam(kCodecParamAssociatedPayloadType, - &associated_payload_type)); - EXPECT_EQ(default_codec_.id, associated_payload_type); - return; - } - FAIL() << "No RTX codec found among default codecs."; -} - TEST_F(WebRtcVideoChannel2Test, SetDefaultSendCodecs) { - ASSERT_TRUE(channel_->SetSendCodecs(engine_codecs_)); + ASSERT_TRUE(channel_->SetSendCodecs(engine_.codecs())); VideoCodec codec; EXPECT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_TRUE(codec.Matches(engine_codecs_[0])); + EXPECT_TRUE(codec.Matches(engine_.codecs()[0])); // Using a RTX setup to verify that the default RTX payload type is good. const std::vector<uint32> ssrcs = MAKE_VECTOR(kSsrcs1); @@ -854,7 +889,7 @@ TEST_F(WebRtcVideoChannel2Test, SetDefaultSendCodecs) { cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs)); webrtc::VideoSendStream::Config config = stream->GetConfig(); // TODO(pbos): Replace ExpectEqualCodecs. - // ExpectEqualCodecs(engine_codecs_[0], config.codec); + // ExpectEqualCodecs(engine_.codecs()[0], config.codec); // Make sure NACK and FEC are enabled on the correct payload types. EXPECT_EQ(1000, config.rtp.nack.rtp_history_ms); @@ -910,9 +945,10 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMinMaxBitrate) { } TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectsMaxLessThanMinBitrate) { - engine_codecs_[0].params[kCodecParamMinBitrate] = "30"; - engine_codecs_[0].params[kCodecParamMaxBitrate] = "20"; - EXPECT_FALSE(channel_->SetSendCodecs(engine_codecs_)); + std::vector<VideoCodec> video_codecs = engine_.codecs(); + video_codecs[0].params[kCodecParamMinBitrate] = "30"; + video_codecs[0].params[kCodecParamMaxBitrate] = "20"; + EXPECT_FALSE(channel_->SetSendCodecs(video_codecs)); } TEST_F(WebRtcVideoChannel2Test, SetSendCodecsAcceptLargeMinMaxBitrate) { @@ -925,9 +961,8 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMaxQuantization) { codecs.push_back(kVp8Codec); codecs[0].params[kCodecParamMaxQuantization] = kMaxQuantization; EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - EXPECT_EQ( - static_cast<unsigned int>(atoi(kMaxQuantization)), - AddSendStream()->GetConfig().encoder_settings.streams.back().max_qp); + EXPECT_EQ(static_cast<unsigned int>(atoi(kMaxQuantization)), + AddSendStream()->GetVideoStreams().back().max_qp); VideoCodec codec; EXPECT_TRUE(channel_->GetSendCodec(&codec)); @@ -983,6 +1018,31 @@ TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsWithOnlyVp8) { EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); } +// Test that we set our inbound RTX codecs properly. +TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsWithRtx) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + cricket::VideoCodec rtx_codec(96, "rtx", 0, 0, 0, 0); + codecs.push_back(rtx_codec); + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)) + << "RTX codec without associated payload should be rejected."; + + codecs[1].SetParam("apt", kVp8Codec.id + 1); + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)) + << "RTX codec with invalid associated payload type should be rejected."; + + codecs[1].SetParam("apt", kVp8Codec.id); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); + + cricket::VideoCodec rtx_codec2(97, "rtx", 0, 0, 0, 0); + rtx_codec2.SetParam("apt", rtx_codec.id); + codecs.push_back(rtx_codec2); + + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)) << "RTX codec with another RTX " + "as associated payload type " + "should be rejected."; +} + TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsDifferentPayloadType) { std::vector<cricket::VideoCodec> codecs; codecs.push_back(kVp8Codec); diff --git a/media/webrtc/webrtcvideoengine2_unittest.h b/media/webrtc/webrtcvideoengine2_unittest.h new file mode 100644 index 0000000..879b4f4 --- /dev/null +++ b/media/webrtc/webrtcvideoengine2_unittest.h @@ -0,0 +1,157 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_ +#define TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_ + +#include <map> +#include <vector> + +#include "webrtc/call.h" +#include "webrtc/video_receive_stream.h" +#include "webrtc/video_send_stream.h" + +namespace cricket { +class FakeVideoSendStream : public webrtc::VideoSendStream { + public: + FakeVideoSendStream(const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams); + webrtc::VideoSendStream::Config GetConfig(); + std::vector<webrtc::VideoStream> GetVideoStreams(); + + bool IsSending(); + + private: + virtual webrtc::VideoSendStream::Stats GetStats() const OVERRIDE; + + virtual bool ReconfigureVideoEncoder( + const std::vector<webrtc::VideoStream>& streams, + const void* encoder_specific); + + virtual webrtc::VideoSendStreamInput* Input() OVERRIDE; + + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + + bool sending_; + webrtc::VideoSendStream::Config config_; + std::vector<webrtc::VideoStream> video_streams_; +}; + +class FakeVideoReceiveStream : public webrtc::VideoReceiveStream { + public: + explicit FakeVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config); + + webrtc::VideoReceiveStream::Config GetConfig(); + + private: + virtual webrtc::VideoReceiveStream::Stats GetStats() const OVERRIDE; + + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void GetCurrentReceiveCodec(webrtc::VideoCodec* codec); + + webrtc::VideoReceiveStream::Config config_; + bool receiving_; +}; + +class FakeCall : public webrtc::Call { + public: + FakeCall(); + ~FakeCall(); + + void SetVideoCodecs(const std::vector<webrtc::VideoCodec> codecs); + + std::vector<FakeVideoSendStream*> GetVideoSendStreams(); + std::vector<FakeVideoReceiveStream*> GetVideoReceiveStreams(); + + webrtc::VideoCodec GetEmptyVideoCodec(); + + webrtc::VideoCodec GetVideoCodecVp8(); + webrtc::VideoCodec GetVideoCodecVp9(); + + std::vector<webrtc::VideoCodec> GetDefaultVideoCodecs(); + + private: + virtual webrtc::VideoSendStream::Config GetDefaultSendConfig() OVERRIDE; + + virtual webrtc::VideoSendStream* CreateVideoSendStream( + const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams, + const void* encoder_settings) OVERRIDE; + + virtual void DestroyVideoSendStream( + webrtc::VideoSendStream* send_stream) OVERRIDE; + + virtual webrtc::VideoReceiveStream::Config GetDefaultReceiveConfig() OVERRIDE; + + virtual webrtc::VideoReceiveStream* CreateVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config) OVERRIDE; + + virtual void DestroyVideoReceiveStream( + webrtc::VideoReceiveStream* receive_stream) OVERRIDE; + virtual webrtc::PacketReceiver* Receiver() OVERRIDE; + + virtual uint32_t SendBitrateEstimate() OVERRIDE; + virtual uint32_t ReceiveBitrateEstimate() OVERRIDE; + + std::vector<webrtc::VideoCodec> codecs_; + std::vector<FakeVideoSendStream*> video_send_streams_; + std::vector<FakeVideoReceiveStream*> video_receive_streams_; +}; + +class FakeWebRtcVideoChannel2 : public WebRtcVideoChannel2 { + public: + FakeWebRtcVideoChannel2(FakeCall* call, + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel); + virtual ~FakeWebRtcVideoChannel2(); + + VoiceMediaChannel* GetVoiceChannel(); + FakeCall* GetFakeCall(); + + private: + FakeCall* fake_call_; + VoiceMediaChannel* voice_channel_; +}; + +class FakeWebRtcVideoMediaChannelFactory : public WebRtcVideoChannelFactory { + public: + FakeWebRtcVideoChannel2* GetFakeChannel(VideoMediaChannel* channel); + + private: + virtual WebRtcVideoChannel2* Create( + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) OVERRIDE; + + std::map<VideoMediaChannel*, FakeWebRtcVideoChannel2*> channel_map_; +}; + + +} // namespace cricket +#endif // TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_ diff --git a/media/webrtc/webrtcvideoengine_unittest.cc b/media/webrtc/webrtcvideoengine_unittest.cc index 804b70f..307e594 100644 --- a/media/webrtc/webrtcvideoengine_unittest.cc +++ b/media/webrtc/webrtcvideoengine_unittest.cc @@ -74,7 +74,7 @@ static const unsigned int kMaxBandwidthKbps = 2000; static const uint32 kSsrcs1[] = {1}; static const uint32 kSsrcs2[] = {1, 2}; static const uint32 kSsrcs3[] = {1, 2, 3}; -static const uint32 kRtxSsrc1[] = {4}; +static const uint32 kRtxSsrcs1[] = {4}; static const uint32 kRtxSsrcs3[] = {4, 5, 6}; @@ -749,8 +749,44 @@ TEST_F(WebRtcVideoEngineTestFake, SetRecvCodecsWithRtx) { EXPECT_EQ(rtx_codec.id, vie_.GetRtxRecvPayloadType(channel_num)); } +// Test that RTX packets are routed to the default video channel if +// there's only one recv stream. +TEST_F(WebRtcVideoEngineTestFake, TestReceiveRtxOneStream) { + EXPECT_TRUE(SetupEngine()); + + // Setup one channel with an associated RTX stream. + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs1[0]); + params.AddFidSsrc(kSsrcs1[0], kRtxSsrcs1[0]); + EXPECT_TRUE(channel_->AddRecvStream(params)); + int channel_num = vie_.GetLastChannel(); + EXPECT_EQ(static_cast<int>(kRtxSsrcs1[0]), + vie_.GetRemoteRtxSsrc(channel_num)); + + // Register codecs. + std::vector<cricket::VideoCodec> codec_list; + codec_list.push_back(kVP8Codec720p); + cricket::VideoCodec rtx_codec(96, "rtx", 0, 0, 0, 0); + rtx_codec.SetParam("apt", kVP8Codec.id); + codec_list.push_back(rtx_codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codec_list)); + + // Construct a fake RTX packet and verify that it is passed to the + // right WebRTC channel. + const size_t kDataLength = 12; + uint8_t data[kDataLength]; + memset(data, 0, sizeof(data)); + data[0] = 0x80; + data[1] = rtx_codec.id; + talk_base::SetBE32(&data[8], kRtxSsrcs1[0]); + talk_base::Buffer packet(data, kDataLength); + talk_base::PacketTime packet_time; + channel_->OnPacketReceived(&packet, packet_time); + EXPECT_EQ(rtx_codec.id, vie_.GetLastRecvdPayloadType(channel_num)); +} + // Test that RTX packets are routed to the correct video channel. -TEST_F(WebRtcVideoEngineTestFake, TestReceiveRtx) { +TEST_F(WebRtcVideoEngineTestFake, TestReceiveRtxThreeStreams) { EXPECT_TRUE(SetupEngine()); // Setup three channels with associated RTX streams. @@ -880,7 +916,7 @@ TEST_F(WebRtcVideoEngineTestFake, RecvStreamWithRtx) { EXPECT_TRUE(channel_->AddRecvStream( cricket::CreateSimWithRtxStreamParams("cname", MAKE_VECTOR(kSsrcs1), - MAKE_VECTOR(kRtxSsrc1)))); + MAKE_VECTOR(kRtxSsrcs1)))); int new_channel_num = vie_.GetLastChannel(); EXPECT_NE(default_channel, new_channel_num); EXPECT_EQ(4, vie_.GetRemoteRtxSsrc(new_channel_num)); @@ -923,17 +959,17 @@ TEST_F(WebRtcVideoEngineTestFake, RecvAbsoluteSendTimeHeaderExtensions) { TEST_F(WebRtcVideoEngineTestFake, LeakyBucketTest) { EXPECT_TRUE(SetupEngine()); - // Verify this is off by default. + // Verify this is on by default. EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); int first_send_channel = vie_.GetLastChannel(); - EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); - // Enable the experiment and verify. + // Disable the experiment and verify. cricket::VideoOptions options; options.conference_mode.Set(true); - options.video_leaky_bucket.Set(true); + options.video_leaky_bucket.Set(false); EXPECT_TRUE(channel_->SetOptions(options)); - EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); // Add a receive channel and verify leaky bucket isn't enabled. EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2))); @@ -941,10 +977,16 @@ TEST_F(WebRtcVideoEngineTestFake, LeakyBucketTest) { EXPECT_NE(first_send_channel, recv_channel_num); EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(recv_channel_num)); - // Add a new send stream and verify leaky bucket is enabled from start. + // Add a new send stream and verify leaky bucket is disabled from start. EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); int second_send_channel = vie_.GetLastChannel(); EXPECT_NE(first_send_channel, second_send_channel); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(second_send_channel)); + + // Reenable leaky bucket. + options.video_leaky_bucket.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(second_send_channel)); } @@ -1026,12 +1068,12 @@ TEST_F(WebRtcVideoEngineTestFake, AdditiveVideoOptions) { EXPECT_TRUE(channel_->SetOptions(options1)); EXPECT_EQ(100, vie_.GetSenderTargetDelay(first_send_channel)); EXPECT_EQ(100, vie_.GetReceiverTargetDelay(first_send_channel)); - EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); cricket::VideoOptions options2; - options2.video_leaky_bucket.Set(true); + options2.video_leaky_bucket.Set(false); EXPECT_TRUE(channel_->SetOptions(options2)); - EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); // The buffered_mode_latency still takes effect. EXPECT_EQ(100, vie_.GetSenderTargetDelay(first_send_channel)); EXPECT_EQ(100, vie_.GetReceiverTargetDelay(first_send_channel)); @@ -1041,7 +1083,7 @@ TEST_F(WebRtcVideoEngineTestFake, AdditiveVideoOptions) { EXPECT_EQ(50, vie_.GetSenderTargetDelay(first_send_channel)); EXPECT_EQ(50, vie_.GetReceiverTargetDelay(first_send_channel)); // The video_leaky_bucket still takes effect. - EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); } TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithCaptureJitterMethod) { @@ -1144,6 +1186,11 @@ TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithEncodeUsageMethod) { EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); EXPECT_FALSE(cpu_option.enable_capture_jitter_method); EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + // Verify that optional encode rsd thresholds are not set. + EXPECT_EQ(-1, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(-1, cpu_option.high_encode_time_rsd_threshold); +#endif // Add a new send stream and verify that cpu options are set from start. EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); @@ -1154,6 +1201,51 @@ TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithEncodeUsageMethod) { EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); EXPECT_FALSE(cpu_option.enable_capture_jitter_method); EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + // Verify that optional encode rsd thresholds are not set. + EXPECT_EQ(-1, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(-1, cpu_option.high_encode_time_rsd_threshold); +#endif +} + +TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithEncodeRsdThresholds) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + int first_send_channel = vie_.GetLastChannel(); + + // Set optional encode rsd thresholds and verify cpu options. + cricket::VideoOptions options; + options.conference_mode.Set(true); + options.cpu_underuse_threshold.Set(10); + options.cpu_overuse_threshold.Set(20); + options.cpu_underuse_encode_rsd_threshold.Set(30); + options.cpu_overuse_encode_rsd_threshold.Set(40); + options.cpu_overuse_encode_usage.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + webrtc::CpuOveruseOptions cpu_option = + vie_.GetCpuOveruseOptions(first_send_channel); + EXPECT_EQ(10, cpu_option.low_encode_usage_threshold_percent); + EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + EXPECT_EQ(30, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(40, cpu_option.high_encode_time_rsd_threshold); +#endif + + // Add a new send stream and verify that cpu options are set from start. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); + int second_send_channel = vie_.GetLastChannel(); + EXPECT_NE(first_send_channel, second_send_channel); + cpu_option = vie_.GetCpuOveruseOptions(second_send_channel); + EXPECT_EQ(10, cpu_option.low_encode_usage_threshold_percent); + EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + EXPECT_EQ(30, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(40, cpu_option.high_encode_time_rsd_threshold); +#endif } // Test that AddRecvStream doesn't create new channel for 1:1 call. @@ -2235,13 +2327,19 @@ TEST_F(WebRtcVideoMediaChannelTest, DISABLED_SendVp8HdAndReceiveAdaptedVp8Vga) { EXPECT_FRAME_WAIT(1, codec.width, codec.height, kTimeout); } -// TODO(juberti): Fix this test to tolerate missing stats. +#ifdef USE_WEBRTC_DEV_BRANCH +TEST_F(WebRtcVideoMediaChannelTest, GetStats) { +#else TEST_F(WebRtcVideoMediaChannelTest, DISABLED_GetStats) { +#endif Base::GetStats(); } -// TODO(juberti): Fix this test to tolerate missing stats. +#ifdef USE_WEBRTC_DEV_BRANCH +TEST_F(WebRtcVideoMediaChannelTest, GetStatsMultipleRecvStreams) { +#else TEST_F(WebRtcVideoMediaChannelTest, DISABLED_GetStatsMultipleRecvStreams) { +#endif Base::GetStatsMultipleRecvStreams(); } diff --git a/media/webrtc/webrtcvoiceengine.cc b/media/webrtc/webrtcvoiceengine.cc index 1bd2ffe..785cdf1 100644 --- a/media/webrtc/webrtcvoiceengine.cc +++ b/media/webrtc/webrtcvoiceengine.cc @@ -426,6 +426,16 @@ static int GetOpusBitrateFromParams(const AudioCodec& codec) { return bitrate; } +// True if params["useinbandfec"] == "1" +static bool IsOpusFecEnabled(const AudioCodec& codec) { + CodecParameterMap::const_iterator param = + codec.params.find(kCodecParamUseInbandFec); + if (param == codec.params.end()) + return false; + + return param->second == kParamValueTrue; +} + void WebRtcVoiceEngine::ConstructCodecs() { LOG(LS_INFO) << "WebRtc VoiceEngine codecs:"; int ncodecs = voe_wrapper_->codec()->NumOfCodecs(); @@ -1943,10 +1953,16 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( bool WebRtcVoiceMediaChannel::SetSendCodecs( int channel, const std::vector<AudioCodec>& codecs) { - // Disable VAD, and FEC unless we know the other side wants them. + // Disable VAD, FEC, and RED unless we know the other side wants them. engine()->voe()->codec()->SetVADStatus(channel, false); engine()->voe()->rtp()->SetNACKStatus(channel, false, 0); +#ifdef USE_WEBRTC_DEV_BRANCH + engine()->voe()->rtp()->SetREDStatus(channel, false); + engine()->voe()->codec()->SetFECStatus(channel, false); +#else + // TODO(minyue): Remove code under #else case after new WebRTC roll. engine()->voe()->rtp()->SetFECStatus(channel, false); +#endif // USE_WEBRTC_DEV_BRANCH // Scan through the list to figure out the codec to use for sending, along // with the proper configuration for VAD and DTMF. @@ -2005,11 +2021,24 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( if (bitrate_from_params != 0) { voe_codec.rate = bitrate_from_params; } + + // If FEC is enabled. + if (IsOpusFecEnabled(*it)) { + LOG(LS_INFO) << "Enabling Opus FEC on channel " << channel; +#ifdef USE_WEBRTC_DEV_BRANCH + if (engine()->voe()->codec()->SetFECStatus(channel, true) == -1) { + // Enable in-band FEC of the Opus codec. Treat any failure as a fatal + // internal error. + LOG_RTCERR2(SetFECStatus, channel, true); + return false; + } +#endif // USE_WEBRTC_DEV_BRANCH + } } // We'll use the first codec in the list to actually send audio data. // Be sure to use the payload type requested by the remote side. - // "red", for FEC audio, is a special case where the actual codec to be + // "red", for RED audio, is a special case where the actual codec to be // used is specified in params. if (IsRedCodec(it->name)) { // Parse out the RED parameters. If we fail, just ignore RED; @@ -2020,9 +2049,16 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // Enable redundant encoding of the specified codec. Treat any // failure as a fatal internal error. +#ifdef USE_WEBRTC_DEV_BRANCH + LOG(LS_INFO) << "Enabling RED on channel " << channel; + if (engine()->voe()->rtp()->SetREDStatus(channel, true, it->id) == -1) { + LOG_RTCERR3(SetREDStatus, channel, true, it->id); +#else + // TODO(minyue): Remove code under #else case after new WebRTC roll. LOG(LS_INFO) << "Enabling FEC"; if (engine()->voe()->rtp()->SetFECStatus(channel, true, it->id) == -1) { LOG_RTCERR3(SetFECStatus, channel, true, it->id); +#endif // USE_WEBRTC_DEV_BRANCH return false; } } else { @@ -3270,6 +3306,9 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { #ifdef USE_WEBRTC_DEV_BRANCH rinfo.capture_start_ntp_time_ms = cs.capture_start_ntp_time_ms_; #endif + if (codec.pltype != -1) { + rinfo.codec_name = codec.plname; + } // Convert samples to milliseconds. if (codec.plfreq / 1000 > 0) { rinfo.jitter_ms = cs.jitterSamples / (codec.plfreq / 1000); diff --git a/media/webrtc/webrtcvoiceengine_unittest.cc b/media/webrtc/webrtcvoiceengine_unittest.cc index cbc0007..80a50c5 100644 --- a/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/media/webrtc/webrtcvoiceengine_unittest.cc @@ -745,7 +745,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecs) { EXPECT_EQ(48000, gcodec.rate); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_FALSE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false)); EXPECT_EQ(105, voe_.GetSendCNPayloadType(channel_num, true)); EXPECT_EQ(106, voe_.GetSendTelephoneEventPayloadType(channel_num)); @@ -1144,6 +1144,81 @@ TEST_F(WebRtcVoiceEngineTestFake, AddRecvStreamEnableNack) { EXPECT_TRUE(voe_.GetNACK(channel_num)); } +#ifdef USE_WEBRTC_DEV_BRANCH +// Test that without useinbandfec, Opus FEC is off. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecNoOpusFEC) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetCodecFEC(channel_num)); +} + +// Test that with useinbandfec=0, Opus FEC is off. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusDisableFEC) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + codecs[0].params["useinbandfec"] = "0"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetCodecFEC(channel_num)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_STREQ("opus", gcodec.plname); + EXPECT_EQ(1, gcodec.channels); + EXPECT_EQ(32000, gcodec.rate); +} + +// Test that with useinbandfec=1, Opus FEC is on. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusEnableFEC) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + codecs[0].params["useinbandfec"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(voe_.GetCodecFEC(channel_num)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_STREQ("opus", gcodec.plname); + EXPECT_EQ(1, gcodec.channels); + EXPECT_EQ(32000, gcodec.rate); +} + +// Test that with useinbandfec=1, stereo=1, Opus FEC is on. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusEnableFECStereo) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + codecs[0].params["stereo"] = "1"; + codecs[0].params["useinbandfec"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(voe_.GetCodecFEC(channel_num)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_STREQ("opus", gcodec.plname); + EXPECT_EQ(2, gcodec.channels); + EXPECT_EQ(64000, gcodec.rate); +} + +// Test that with non-Opus, codec FEC is off. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecIsacNoFEC) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kIsacCodec); + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetCodecFEC(channel_num)); +} +#endif // USE_WEBRTC_DEV_BRANCH + // Test that we can apply CELT with stereo mode but fail with mono mode. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCelt) { EXPECT_TRUE(SetupEngine()); @@ -1315,7 +1390,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCaller) { EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_TRUE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(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)); @@ -1348,7 +1423,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCallee) { EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_TRUE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(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)); @@ -1412,13 +1487,13 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCaseInsensitive) { EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_TRUE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(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 set up FEC correctly as caller. +// Test that we set up RED correctly as caller. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCaller) { EXPECT_TRUE(SetupEngine()); int channel_num = voe_.GetLastChannel(); @@ -1434,11 +1509,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCaller) { 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)); + EXPECT_TRUE(voe_.GetRED(channel_num)); + EXPECT_EQ(127, voe_.GetSendREDPayloadType(channel_num)); } -// Test that we set up FEC correctly as callee. +// Test that we set up RED correctly as callee. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCallee) { EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); channel_ = engine_.CreateChannel(); @@ -1459,11 +1534,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCallee) { 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)); + EXPECT_TRUE(voe_.GetRED(channel_num)); + EXPECT_EQ(127, voe_.GetSendREDPayloadType(channel_num)); } -// Test that we set up FEC correctly if params are omitted. +// Test that we set up RED correctly if params are omitted. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDNoParams) { EXPECT_TRUE(SetupEngine()); int channel_num = voe_.GetLastChannel(); @@ -1478,8 +1553,8 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDNoParams) { 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)); + EXPECT_TRUE(voe_.GetRED(channel_num)); + EXPECT_EQ(127, voe_.GetSendREDPayloadType(channel_num)); } // Test that we ignore RED if the parameters aren't named the way we expect. @@ -1498,7 +1573,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED1) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it uses different primary/secondary encoding. @@ -1517,7 +1592,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED2) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it uses more than 2 encodings. @@ -1536,7 +1611,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED3) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it has bogus codec ids. @@ -1555,7 +1630,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED4) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it refers to a codec that is not present. @@ -1574,7 +1649,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED5) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test support for audio level header extension. @@ -1726,11 +1801,15 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStatsWithMultipleSendStreams) { EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(kSsrcs4[i]))); } - + // Create a receive stream to check that none of the send streams end up in + // the receive stream stats. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc2))); // We need send codec to be set to get all stats. std::vector<cricket::AudioCodec> codecs; codecs.push_back(kPcmuCodec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); cricket::VoiceMediaInfo info; EXPECT_EQ(true, channel_->GetStats(&info)); @@ -1747,9 +1826,19 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStatsWithMultipleSendStreams) { EXPECT_EQ(cricket::kIntStatValue, info.senders[i].ext_seqnum); EXPECT_EQ(cricket::kIntStatValue, info.senders[i].rtt_ms); EXPECT_EQ(cricket::kIntStatValue, info.senders[i].jitter_ms); + EXPECT_EQ(kPcmuCodec.name, info.senders[i].codec_name); } + EXPECT_EQ(0u, info.receivers.size()); + DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame)); + EXPECT_EQ(true, channel_->GetStats(&info)); + EXPECT_EQ(1u, info.receivers.size()); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].bytes_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_lost); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].ext_seqnum); + EXPECT_EQ(kPcmuCodec.name, info.receivers[0].codec_name); } // Test that we can add and remove receive streams, and do proper send/playout. @@ -2036,9 +2125,14 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) { TEST_F(WebRtcVoiceEngineTestFake, GetStats) { // Setup. We need send codec to be set to get all stats. EXPECT_TRUE(SetupEngine()); + // SetupEngine adds a send stream with kSsrc1, so the receive stream has to + // use a different SSRC. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc2))); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kPcmuCodec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); cricket::VoiceMediaInfo info; EXPECT_EQ(true, channel_->GetStats(&info)); @@ -2052,6 +2146,7 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStats) { EXPECT_EQ(cricket::kIntStatValue, info.senders[0].ext_seqnum); EXPECT_EQ(cricket::kIntStatValue, info.senders[0].rtt_ms); EXPECT_EQ(cricket::kIntStatValue, info.senders[0].jitter_ms); + EXPECT_EQ(kPcmuCodec.name, info.senders[0].codec_name); // TODO(sriniv): Add testing for more fields. These are not populated // in FakeWebrtcVoiceEngine yet. // EXPECT_EQ(cricket::kIntStatValue, info.senders[0].audio_level); @@ -2061,8 +2156,17 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStats) { // EXPECT_EQ(cricket::kIntStatValue, // info.senders[0].echo_return_loss_enhancement); + EXPECT_EQ(0u, info.receivers.size()); + DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame)); + EXPECT_EQ(true, channel_->GetStats(&info)); EXPECT_EQ(1u, info.receivers.size()); - // TODO(sriniv): Add testing for receiver fields. + + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].bytes_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_lost); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].ext_seqnum); + EXPECT_EQ(kPcmuCodec.name, info.receivers[0].codec_name); + // TODO(sriniv): Add testing for more receiver fields. } // Test that we can set the outgoing SSRC properly with multiple streams. |