From 4e8eabaab13d039efc4cf25b3510526f79dc5406 Mon Sep 17 00:00:00 2001 From: "stefan@webrtc.org" Date: Mon, 20 Aug 2012 14:29:52 +0000 Subject: [PATCH] Properly handle switching between simulcast and unicast streams. BUG= Review URL: https://webrtc-codereview.appspot.com/733010 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2644 4adac7df-926f-26a2-2b94-8c16560cd09d --- src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 40 +++-- .../source/vie_autotest_simulcast.cc | 141 ++++++++++++++---- src/video_engine/vie_channel.cc | 9 +- src/video_engine/vie_channel.h | 4 +- src/video_engine/vie_channel_manager.cc | 24 ++- src/video_engine/vie_channel_manager.h | 3 +- src/video_engine/vie_codec_impl.cc | 4 +- 7 files changed, 165 insertions(+), 60 deletions(-) diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 56a4236d3b..cf4af60257 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -834,11 +834,22 @@ WebRtc_Word32 ModuleRtpRtcpImpl::SendOutgoingData( int idx = 0; CriticalSectionScoped lock(_criticalSectionModulePtrs.get()); std::list::iterator it = _childModules.begin(); - for (; idx < rtpVideoHdr->simulcastIdx; idx++) { - it++; + for (; idx < rtpVideoHdr->simulcastIdx; ++it) { if (it == _childModules.end()) { return -1; } + if ((*it)->SendingMedia()) { + ++idx; + } + } + for (; it != _childModules.end(); ++it) { + if ((*it)->SendingMedia()) { + break; + } + ++idx; + } + if (it == _childModules.end()) { + return -1; } RTPSender& rtpSender = (*it)->_rtpSender; WEBRTC_TRACE(kTraceModuleCall, @@ -1632,17 +1643,20 @@ void ModuleRtpRtcpImpl::SetTargetSendBitrate(const uint32_t bitrate) { uint32_t bitrate_remainder = bitrate; std::list::iterator it = _childModules.begin(); for (int i = 0; it != _childModules.end() && - i < _sendVideoCodec.numberOfSimulcastStreams; ++it, ++i) { - RTPSender& rtpSender = (*it)->_rtpSender; - if (_sendVideoCodec.simulcastStream[i].maxBitrate * 1000 > - bitrate_remainder) { - rtpSender.SetTargetSendBitrate(bitrate_remainder); - bitrate_remainder = 0; - } else { - rtpSender.SetTargetSendBitrate( - _sendVideoCodec.simulcastStream[i].maxBitrate * 1000); - bitrate_remainder -= - _sendVideoCodec.simulcastStream[i].maxBitrate * 1000; + i < _sendVideoCodec.numberOfSimulcastStreams; ++it) { + if ((*it)->SendingMedia()) { + ++i; + RTPSender& rtpSender = (*it)->_rtpSender; + if (_sendVideoCodec.simulcastStream[i].maxBitrate * 1000 > + bitrate_remainder) { + rtpSender.SetTargetSendBitrate(bitrate_remainder); + bitrate_remainder = 0; + } else { + rtpSender.SetTargetSendBitrate( + _sendVideoCodec.simulcastStream[i].maxBitrate * 1000); + bitrate_remainder -= + _sendVideoCodec.simulcastStream[i].maxBitrate * 1000; + } } } } else { diff --git a/src/video_engine/test/auto_test/source/vie_autotest_simulcast.cc b/src/video_engine/test/auto_test/source/vie_autotest_simulcast.cc index 6d7711572b..f26340bfe7 100644 --- a/src/video_engine/test/auto_test/source/vie_autotest_simulcast.cc +++ b/src/video_engine/test/auto_test/source/vie_autotest_simulcast.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -25,6 +25,46 @@ #define VCM_RED_PAYLOAD_TYPE 96 #define VCM_ULPFEC_PAYLOAD_TYPE 97 +void InitialSingleStreamSettings(webrtc::VideoCodec* video_codec) { + video_codec->numberOfSimulcastStreams = 0; + video_codec->width = 1200; + video_codec->height = 800; +} + +void SetSimulcastSettings(webrtc::VideoCodec* video_codec) { + video_codec->width = 1280; + video_codec->height = 720; + // simulcast settings + video_codec->numberOfSimulcastStreams = 3; + video_codec->simulcastStream[0].width = 320; + video_codec->simulcastStream[0].height = 180; + video_codec->simulcastStream[0].numberOfTemporalLayers = 0; + video_codec->simulcastStream[0].maxBitrate = 100; + video_codec->simulcastStream[0].qpMax = video_codec->qpMax; + + video_codec->simulcastStream[1].width = 640; + video_codec->simulcastStream[1].height = 360; + video_codec->simulcastStream[1].numberOfTemporalLayers = 0; + video_codec->simulcastStream[1].maxBitrate = 500; + video_codec->simulcastStream[1].qpMax = video_codec->qpMax; + + video_codec->simulcastStream[2].width = 1280; + video_codec->simulcastStream[2].height = 720; + video_codec->simulcastStream[2].numberOfTemporalLayers = 0; + video_codec->simulcastStream[2].maxBitrate = 1200; + video_codec->simulcastStream[2].qpMax = video_codec->qpMax; +} + +void RuntimeSingleStreamSettings(webrtc::VideoCodec* video_codec) { + SetSimulcastSettings(video_codec); + video_codec->width = 1200; + video_codec->height = 800; + video_codec->numberOfSimulcastStreams = 3; + video_codec->simulcastStream[0].maxBitrate = 0; + video_codec->simulcastStream[1].maxBitrate = 0; + video_codec->simulcastStream[2].maxBitrate = 0; +} + int VideoEngineSimulcastTest(void* window1, void* window2) { //******************************************************** @@ -283,29 +323,16 @@ int VideoEngineSimulcastTest(void* window1, void* window2) return -1; } + bool simulcast_mode = true; + int num_streams = 1; // Set spatial resolution option - videoCodec.width = 1280; - videoCodec.height = 720; - - // simulcast settings - videoCodec.numberOfSimulcastStreams = 3; - videoCodec.simulcastStream[0].width = 320; - videoCodec.simulcastStream[0].height = 180; - videoCodec.simulcastStream[0].numberOfTemporalLayers = 0; - videoCodec.simulcastStream[0].maxBitrate = 100; - videoCodec.simulcastStream[0].qpMax = videoCodec.qpMax; - - videoCodec.simulcastStream[1].width = 640; - videoCodec.simulcastStream[1].height = 360; - videoCodec.simulcastStream[1].numberOfTemporalLayers = 0; - videoCodec.simulcastStream[1].maxBitrate = 500; - videoCodec.simulcastStream[1].qpMax = videoCodec.qpMax; - - videoCodec.simulcastStream[2].width = 1280; - videoCodec.simulcastStream[2].height = 720; - videoCodec.simulcastStream[2].numberOfTemporalLayers = 0; - videoCodec.simulcastStream[2].maxBitrate = 1200; - videoCodec.simulcastStream[2].qpMax = videoCodec.qpMax; + if (simulcast_mode) { + SetSimulcastSettings(&videoCodec); + num_streams = videoCodec.numberOfSimulcastStreams; + } else { + InitialSingleStreamSettings(&videoCodec); + num_streams = 1; + } // Set start bit rate std::string str; @@ -351,20 +378,21 @@ int VideoEngineSimulcastTest(void* window1, void* window2) // Set network delay value extTransport.SetNetworkDelay(10); - extTransport.SetSSRCFilter(3); - - for (int idx = 0; idx < 3; idx++) + for (int idx = 0; idx < num_streams; idx++) { - error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, - idx+1, // SSRC - webrtc::kViEStreamTypeNormal, - idx); + error = ptrViERtpRtcp->SetLocalSSRC( + videoChannel, + idx+1, // SSRC + webrtc::kViEStreamTypeNormal, + idx); if (error == -1) { - printf("ERROR in ViERTP_RTCP::SetLocalSSRC(idx:%d)\n", idx); + printf("ERROR in ViERTP_RTCP::SetLocalSSRC(idx:%d)\n", + idx); return -1; } } + extTransport.SetSSRCFilter(num_streams); error = ptrViEBase->StartReceive(videoChannel); if (error == -1) @@ -380,6 +408,15 @@ int VideoEngineSimulcastTest(void* window1, void* window2) return -1; } + // Create a receive channel to verify that it doesn't mess up toggling + // between single stream and simulcast. + int videoChannel2 = -1; + error = ptrViEBase->CreateReceiveChannel(videoChannel2, videoChannel); + if (error == -1) { + printf("ERROR in ViEBase::CreateReceiveChannel\n"); + return -1; + } + //******************************************************** // Engine started //******************************************************** @@ -388,13 +425,46 @@ int VideoEngineSimulcastTest(void* window1, void* window2) do { printf("Enter new SSRC filter 1,2 or 3\n"); + printf("... or 0 to switch between simulcast and a single stream\n"); printf("Press enter to stop..."); str.clear(); std::getline(std::cin, str); if (!str.empty()) { int ssrc = atoi(str.c_str()); - if (ssrc > 0 && ssrc < 4) + if (ssrc == 0) { + // Toggle between simulcast and a single stream with different + // resolution. + if (simulcast_mode) { + RuntimeSingleStreamSettings(&videoCodec); + num_streams = 1; + printf("Disabling simulcast\n"); + } else { + SetSimulcastSettings(&videoCodec); + num_streams = videoCodec.numberOfSimulcastStreams; + printf("Enabling simulcast\n"); + } + simulcast_mode = !simulcast_mode; + if (ptrViECodec->SetSendCodec(videoChannel, videoCodec) != 0) { + printf("ERROR switching between simulcast and single stream\n"); + return -1; + } + for (int idx = 0; idx < num_streams; idx++) + { + error = ptrViERtpRtcp->SetLocalSSRC( + videoChannel, + idx+1, // SSRC + webrtc::kViEStreamTypeNormal, + idx); + if (error == -1) + { + printf("ERROR in ViERTP_RTCP::SetLocalSSRC(idx:%d)\n", + idx); + return -1; + } + } + extTransport.SetSSRCFilter(num_streams); + } else if (ssrc > 0 && ssrc < 4) { extTransport.SetSSRCFilter(ssrc); } else @@ -411,6 +481,13 @@ int VideoEngineSimulcastTest(void* window1, void* window2) // Testing finished. Tear down Video Engine //******************************************************** + error = ptrViEBase->DeleteChannel(videoChannel2); + if (error == -1) + { + printf("ERROR in ViEBase::DeleteChannel\n"); + return -1; + } + error = ptrViEBase->StopReceive(videoChannel); if (error == -1) { diff --git a/src/video_engine/vie_channel.cc b/src/video_engine/vie_channel.cc index 723b88e56d..f90904317f 100644 --- a/src/video_engine/vie_channel.cc +++ b/src/video_engine/vie_channel.cc @@ -40,7 +40,8 @@ ViEChannel::ViEChannel(WebRtc_Word32 channel_id, RtcpIntraFrameObserver* intra_frame_observer, RtcpBandwidthObserver* bandwidth_observer, RemoteBitrateEstimator* remote_bitrate_estimator, - RtpRtcp* default_rtp_rtcp) + RtpRtcp* default_rtp_rtcp, + bool sender) : ViEFrameProviderBase(channel_id, engine_id), channel_id_(channel_id), engine_id_(engine_id), @@ -78,7 +79,8 @@ ViEChannel::ViEChannel(WebRtc_Word32 channel_id, color_enhancement_(false), vcm_rttreported_(TickTime::Now()), file_recorder_(channel_id), - mtu_(0) { + mtu_(0), + sender_(sender) { WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, channel_id), "ViEChannel::ViEChannel(channel_id: %d, engine_id: %d)", channel_id, engine_id); @@ -204,6 +206,9 @@ WebRtc_Word32 ViEChannel::SetSendCodec(const VideoCodec& video_codec, WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s: codec_type: %d", __FUNCTION__, video_codec.codecType); + if (!sender_) { + return 0; + } if (video_codec.codecType == kVideoCodecRED || video_codec.codecType == kVideoCodecULPFEC) { WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), diff --git a/src/video_engine/vie_channel.h b/src/video_engine/vie_channel.h index a9b90cc55b..f9c7b8d7fa 100644 --- a/src/video_engine/vie_channel.h +++ b/src/video_engine/vie_channel.h @@ -63,7 +63,8 @@ class ViEChannel RtcpIntraFrameObserver* intra_frame_observer, RtcpBandwidthObserver* bandwidth_observer, RemoteBitrateEstimator* remote_bitrate_estimator, - RtpRtcp* default_rtp_rtcp); + RtpRtcp* default_rtp_rtcp, + bool sender); ~ViEChannel(); WebRtc_Word32 Init(); @@ -396,6 +397,7 @@ class ViEChannel // User set MTU, -1 if not set. uint16_t mtu_; + const bool sender_; }; } // namespace webrtc diff --git a/src/video_engine/vie_channel_manager.cc b/src/video_engine/vie_channel_manager.cc index abced05100..50c7fdd46f 100644 --- a/src/video_engine/vie_channel_manager.cc +++ b/src/video_engine/vie_channel_manager.cc @@ -104,7 +104,7 @@ int ViEChannelManager::CreateChannel(int* channel_id) { if (!(vie_encoder->Init() && CreateChannelObject(new_channel_id, vie_encoder, bandwidth_observer, - remote_bitrate_estimator))) { + remote_bitrate_estimator, true))) { delete vie_encoder; vie_encoder = NULL; ReturnChannelId(new_channel_id); @@ -149,7 +149,8 @@ int ViEChannelManager::CreateChannel(int* channel_id, if (!(vie_encoder->Init() && CreateChannelObject(new_channel_id, vie_encoder, bandwidth_observer, - remote_bitrate_estimator))) { + remote_bitrate_estimator, + sender))) { delete vie_encoder; vie_encoder = NULL; } @@ -157,7 +158,7 @@ int ViEChannelManager::CreateChannel(int* channel_id, vie_encoder = ViEEncoderPtr(original_channel); assert(vie_encoder); if (!CreateChannelObject(new_channel_id, vie_encoder, bandwidth_observer, - remote_bitrate_estimator)) { + remote_bitrate_estimator, sender)) { vie_encoder = NULL; } } @@ -329,7 +330,8 @@ bool ViEChannelManager::CreateChannelObject( int channel_id, ViEEncoder* vie_encoder, RtcpBandwidthObserver* bandwidth_observer, - RemoteBitrateEstimator* remote_bitrate_estimator) { + RemoteBitrateEstimator* remote_bitrate_estimator, + bool sender) { // Register the channel at the encoder. RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule(); @@ -339,7 +341,8 @@ bool ViEChannelManager::CreateChannelObject( vie_encoder, bandwidth_observer, remote_bitrate_estimator, - send_rtp_rtcp_module); + send_rtp_rtcp_module, + sender); if (vie_channel->Init() != 0) { WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), "%s could not init channel", __FUNCTION__, channel_id); @@ -347,10 +350,15 @@ bool ViEChannelManager::CreateChannelObject( return false; } VideoCodec encoder; - if (vie_encoder->GetEncoder(&encoder) != 0 || - vie_channel->SetSendCodec(encoder) != 0) { + if (vie_encoder->GetEncoder(&encoder) != 0) { WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), - "%s: Could not GetEncoder or SetSendCodec.", __FUNCTION__); + "%s: Could not GetEncoder.", __FUNCTION__); + delete vie_channel; + return false; + } + if (sender && vie_channel->SetSendCodec(encoder) != 0) { + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), + "%s: Could not SetSendCodec.", __FUNCTION__); delete vie_channel; return false; } diff --git a/src/video_engine/vie_channel_manager.h b/src/video_engine/vie_channel_manager.h index d0a5d57e20..6294ab1325 100644 --- a/src/video_engine/vie_channel_manager.h +++ b/src/video_engine/vie_channel_manager.h @@ -79,7 +79,8 @@ class ViEChannelManager: private ViEManagerBase { // protected. bool CreateChannelObject(int channel_id, ViEEncoder* vie_encoder, RtcpBandwidthObserver* bandwidth_observer, - RemoteBitrateEstimator* remote_bitrate_estimator); + RemoteBitrateEstimator* remote_bitrate_estimator, + bool sender); // Used by ViEChannelScoped, forcing a manager user to use scoped. // Returns a pointer to the channel with id 'channel_id'. diff --git a/src/video_engine/vie_codec_impl.cc b/src/video_engine/vie_codec_impl.cc index 76138d08c0..dd4d37da2f 100644 --- a/src/video_engine/vie_codec_impl.cc +++ b/src/video_engine/vie_codec_impl.cc @@ -179,9 +179,7 @@ int ViECodecImpl::SetSendCodec(const int video_channel, // Make sure to generate a new SSRC if the codec type and/or resolution has // changed. This won't have any effect if the user has set an SSRC. bool new_rtp_stream = false; - if (encoder.codecType != video_codec_internal.codecType || - encoder.width != video_codec_internal.width || - encoder.height != video_codec_internal.height) { + if (encoder.codecType != video_codec_internal.codecType) { new_rtp_stream = true; }