diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index e2f870c3b3..d25436d39e 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -1662,7 +1662,7 @@ WebRtc_Word32 ModuleRtpRtcpImpl::SetREMBData(const WebRtc_UWord32 bitrate, } WebRtc_Word32 ModuleRtpRtcpImpl::SetMaximumBitrateEstimate( - const WebRtc_UWord32 bitrate) { + const WebRtc_UWord32 bitrate) { if (_defaultModule) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "SetMaximumBitrateEstimate - Should be called on default " diff --git a/src/video_engine/include/vie_base.h b/src/video_engine/include/vie_base.h index 2757a78592..5c7759d6b8 100644 --- a/src/video_engine/include/vie_base.h +++ b/src/video_engine/include/vie_base.h @@ -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 @@ -85,10 +85,22 @@ class WEBRTC_DLLEXPORT ViEBase { // synchronization. virtual int SetVoiceEngine(VoiceEngine* voice_engine) = 0; - // Creates a new channel, either with a new encoder instance or by sharing - // encoder instance with an already created channel. + // Creates a new channel. virtual int CreateChannel(int& video_channel) = 0; - virtual int CreateChannel(int& video_channel, int original_channel) = 0; + + // Creates a new channel grouped together with |original_channel|. The channel + // can both send and receive video. It is assumed the channel is sending + // and/or receiving video to the same end-point. + // Note: |CreateReceiveChannel| will give better performance and network + // properties for receive only channels. + virtual int CreateChannel(int& video_channel, + int original_channel) = 0; + + // Creates a new channel grouped together with |original_channel|. The channel + // can only receive video and it is assumed the remote end-point is the same + // as for |original_channel|. + virtual int CreateReceiveChannel(int& video_channel, + int original_channel) = 0; // Deletes an existing channel and releases the utilized resources. virtual int DeleteChannel(const int video_channel) = 0; diff --git a/src/video_engine/include/vie_errors.h b/src/video_engine/include/vie_errors.h index 63100b51d9..16c9299f57 100644 --- a/src/video_engine/include/vie_errors.h +++ b/src/video_engine/include/vie_errors.h @@ -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 @@ -21,6 +21,7 @@ enum ViEErrors { kViEBaseInvalidArgument, kViEBaseAlreadySending, // StartSend called on channel that is already sending. kViEBaseNotSending, // StopSend called on channel that is not sending. + kViEBaseReceiveOnlyChannel, // Can't send on a receive only channel. kViEBaseAlreadyReceiving, // StartReceive called on channel that is already receiving. kViEBaseObserverAlreadyRegistered, // RegisterObserver- an observer has already been set. kViEBaseObserverNotRegistered, // DeregisterObserver - no observer has been registered. @@ -33,6 +34,7 @@ enum ViEErrors { kViECodecInvalidCodec, // SetSendCodec,SetReceiveCodec- The codec structure is invalid. kViECodecInvalidChannelId, // The channel does not exist. kViECodecInUse, // SetSendCodec- Can't change codec size or type when multiple channels use the same encoder. + kViECodecReceiveOnlyChannel, // SetSendCodec, can't change receive only channel. kViECodecUnknownError, // An unknown error has occurred. Check the log file. // ViERender. diff --git a/src/video_engine/test/auto_test/source/vie_autotest_base.cc b/src/video_engine/test/auto_test/source/vie_autotest_base.cc index 38c0c5e9f2..32dbe7c44f 100644 --- a/src/video_engine/test/auto_test/source/vie_autotest_base.cc +++ b/src/video_engine/test/auto_test/source/vie_autotest_base.cc @@ -133,6 +133,9 @@ void ViEAutoTest::ViEBaseAPITest() { ptrViEBase = webrtc::ViEBase::GetInterface(ptrViE); EXPECT_TRUE(NULL != ptrViEBase); + webrtc::ViENetwork* ptrVieNetwork = webrtc::ViENetwork::GetInterface(ptrViE); + EXPECT_TRUE(ptrVieNetwork != NULL); + // *************************************************************** // Engine ready. Begin testing class // *************************************************************** @@ -148,16 +151,35 @@ void ViEAutoTest::ViEBaseAPITest() { EXPECT_EQ(0, ptrViEBase->CreateChannel(videoChannel)); int videoChannel2 = -1; + int videoChannel3 = -1; EXPECT_EQ(0, ptrViEBase->CreateChannel(videoChannel2)); EXPECT_NE(videoChannel, videoChannel2) << "Should allocate new number for independent channel"; EXPECT_EQ(0, ptrViEBase->DeleteChannel(videoChannel2)); - EXPECT_EQ(-1, ptrViEBase->CreateChannel(videoChannel2, videoChannel + 1)) << - "Should fail since neither channel exists (the second must)"; + EXPECT_EQ(-1, ptrViEBase->CreateChannel(videoChannel2, videoChannel + 1)) + << "Should fail since neither channel exists (the second must)"; - EXPECT_EQ(0, ptrViEBase->CreateChannel(videoChannel2, videoChannel)); + // Create a receive only channel and a send channel. Verify we can't send on + // the receive only channel. + EXPECT_EQ(0, ptrViEBase->CreateReceiveChannel(videoChannel2, videoChannel)); + EXPECT_EQ(0, ptrViEBase->CreateChannel(videoChannel3, videoChannel)); + + const char* ipAddress = "127.0.0.1\0"; + const int sendPort = 1234; + EXPECT_EQ(0, ptrVieNetwork->SetSendDestination(videoChannel, ipAddress, + sendPort)); + EXPECT_EQ(0, ptrVieNetwork->SetSendDestination(videoChannel2,ipAddress, + sendPort + 2)); + EXPECT_EQ(0, ptrVieNetwork->SetSendDestination(videoChannel3,ipAddress, + sendPort + 4)); + + EXPECT_EQ(0, ptrViEBase->StartSend(videoChannel)); + EXPECT_EQ(-1, ptrViEBase->StartSend(videoChannel2)); + EXPECT_EQ(0, ptrViEBase->StartSend(videoChannel3)); + EXPECT_EQ(0, ptrViEBase->StopSend(videoChannel)); + EXPECT_EQ(0, ptrViEBase->StopSend(videoChannel3)); // Test Voice Engine integration with Video Engine. webrtc::VoiceEngine* ptrVoE = NULL; @@ -191,6 +213,7 @@ void ViEAutoTest::ViEBaseAPITest() { EXPECT_EQ(0, ptrViEBase->DisconnectAudioChannel(videoChannel)); // Clean up voice engine + EXPECT_EQ(0, ptrVieNetwork->Release()); EXPECT_EQ(0, ptrViEBase->SetVoiceEngine(NULL)); EXPECT_EQ(0, ptrVoEBase->Release()); EXPECT_TRUE(webrtc::VoiceEngine::Delete(ptrVoE)); diff --git a/src/video_engine/test/auto_test/source/vie_autotest_codec.cc b/src/video_engine/test/auto_test/source/vie_autotest_codec.cc index ed8f20bc58..50a7db560a 100644 --- a/src/video_engine/test/auto_test/source/vie_autotest_codec.cc +++ b/src/video_engine/test/auto_test/source/vie_autotest_codec.cc @@ -139,99 +139,116 @@ void ViEAutoTest::ViECodecExtendedTest() } // - // Default channel + // Multiple send channels. // { - // Create VIE + // Create two channels, where the second channel is created from the + // first channel. Send different resolutions on the channels and verify + // the received streams. + TbInterfaces ViE("ViECodecExtendedTest2"); - // Create a capture device TbCaptureDevice tbCapture(ViE); - // Create channel 1 + // Create channel 1. int videoChannel1 = -1; EXPECT_EQ(0, ViE.base->CreateChannel(videoChannel1)); + // Create channel 2 based on the first channel. + int videoChannel2 = -1; + EXPECT_EQ(0, ViE.base->CreateChannel(videoChannel2, videoChannel1)); + EXPECT_NE(videoChannel1, videoChannel2) << + "Channel 2 should be unique."; + unsigned short rtpPort1 = 12000; + unsigned short rtpPort2 = 13000; EXPECT_EQ(0, ViE.network->SetLocalReceiver( videoChannel1, rtpPort1)); EXPECT_EQ(0, ViE.network->SetSendDestination( videoChannel1, "127.0.0.1", rtpPort1)); + EXPECT_EQ(0, ViE.network->SetLocalReceiver( + videoChannel2, rtpPort2)); + EXPECT_EQ(0, ViE.network->SetSendDestination( + videoChannel2, "127.0.0.1", rtpPort2)); + tbCapture.ConnectTo(videoChannel1); + tbCapture.ConnectTo(videoChannel2); EXPECT_EQ(0, ViE.rtp_rtcp->SetKeyFrameRequestMethod( videoChannel1, webrtc::kViEKeyFrameRequestPliRtcp)); + EXPECT_EQ(0, ViE.rtp_rtcp->SetKeyFrameRequestMethod( + videoChannel2, webrtc::kViEKeyFrameRequestPliRtcp)); EXPECT_EQ(0, ViE.render->AddRenderer( videoChannel1, _window1, 0, 0.0, 0.0, 1.0, 1.0)); EXPECT_EQ(0, ViE.render->StartRender(videoChannel1)); - - ViEAutotestCodecObserver codecObserver1; - EXPECT_EQ(0, ViE.codec->RegisterEncoderObserver( - videoChannel1, codecObserver1)); - EXPECT_EQ(0, ViE.codec->RegisterDecoderObserver( - videoChannel1, codecObserver1)); + EXPECT_EQ(0, ViE.render->AddRenderer( + videoChannel2, _window2, 0, 0.0, 0.0, 1.0, 1.0)); + EXPECT_EQ(0, ViE.render->StartRender(videoChannel2)); // Set Send codec - unsigned short codecWidth = 176; - unsigned short codecHeight = 144; + unsigned short codecWidth = 320; + unsigned short codecHeight = 240; bool codecSet = false; webrtc::VideoCodec videoCodec; + webrtc::VideoCodec sendCodec1; + webrtc::VideoCodec sendCodec2; for (int idx = 0; idx < ViE.codec->NumberOfCodecs(); idx++) { EXPECT_EQ(0, ViE.codec->GetCodec(idx, videoCodec)); EXPECT_EQ(0, ViE.codec->SetReceiveCodec(videoChannel1, videoCodec)); if (videoCodec.codecType == webrtc::kVideoCodecVP8) { - videoCodec.width = codecWidth; - videoCodec.height = codecHeight; - videoCodec.startBitrate = 200; - videoCodec.maxBitrate = 300; - EXPECT_EQ(0, ViE.codec->SetSendCodec( - videoChannel1, videoCodec)); + memcpy(&sendCodec1, &videoCodec, sizeof(videoCodec)); + sendCodec1.width = codecWidth; + sendCodec1.height = codecHeight; + EXPECT_EQ(0, ViE.codec->SetSendCodec(videoChannel1, + sendCodec1)); + memcpy(&sendCodec2, &videoCodec, sizeof(videoCodec)); + sendCodec2.width = 2 * codecWidth; + sendCodec2.height = 2 * codecHeight; + EXPECT_EQ(0, ViE.codec->SetSendCodec(videoChannel2, + sendCodec2)); codecSet = true; break; } } EXPECT_TRUE(codecSet); - webrtc::VideoCodec send_codec; - memcpy(&send_codec, &videoCodec, sizeof(videoCodec)); - EXPECT_EQ(0, ViE.base->StartSend(videoChannel1)); + + // We need to verify using render effect filter since we won't trigger + // a decode reset in loopback (due to using the same SSRC). + RenderFilter filter1; + RenderFilter filter2; + EXPECT_EQ(0, + ViE.image_process->RegisterRenderEffectFilter(videoChannel1, + filter1)); + EXPECT_EQ(0, + ViE.image_process->RegisterRenderEffectFilter(videoChannel2, + filter2)); + EXPECT_EQ(0, ViE.base->StartReceive(videoChannel1)); - - // Create channel 2, based on channel 1 - int videoChannel2 = -1; - EXPECT_EQ(0, ViE.base->CreateChannel(videoChannel2, videoChannel1)); - EXPECT_NE(videoChannel1, videoChannel2) << - "Channel 2 should be seop"; - - EXPECT_EQ(0, ViE.rtp_rtcp->SetKeyFrameRequestMethod( - videoChannel2, webrtc::kViEKeyFrameRequestPliRtcp)); - - // Prepare receive codecs - for (int idx = 0; idx < ViE.codec->NumberOfCodecs(); idx++) - { - EXPECT_EQ(0, ViE.codec->GetCodec(idx, videoCodec)); - EXPECT_EQ(0, ViE.codec->SetReceiveCodec(videoChannel2, videoCodec)); - } - - ViEAutotestCodecObserver codecObserver2; - EXPECT_EQ(0, ViE.codec->RegisterDecoderObserver( - videoChannel2, codecObserver2)); - EXPECT_EQ(0, ViE.render->AddRenderer( - videoChannel2, _window2, 0, 0.0, 0.0, 1.0, 1.0)); - EXPECT_EQ(0, ViE.render->StartRender(videoChannel2)); - - unsigned short rtpPort2 = 13000; - EXPECT_EQ(0, ViE.network->SetLocalReceiver(videoChannel2, rtpPort2)); - EXPECT_EQ(0, ViE.network->SetSendDestination( - videoChannel2, "127.0.0.1", rtpPort2)); - + EXPECT_EQ(0, ViE.base->StartSend(videoChannel1)); EXPECT_EQ(0, ViE.base->StartReceive(videoChannel2)); - EXPECT_EQ(-1, ViE.base->StartSend(videoChannel2)); + EXPECT_EQ(0, ViE.base->StartSend(videoChannel2)); + + + AutoTestSleep(KAutoTestSleepTimeMs); + + EXPECT_EQ(0, ViE.base->StopReceive(videoChannel1)); + EXPECT_EQ(0, ViE.base->StopSend(videoChannel1)); + EXPECT_EQ(0, ViE.base->StopReceive(videoChannel2)); + EXPECT_EQ(0, ViE.base->StopSend(videoChannel2)); + + EXPECT_EQ(0, ViE.image_process->DeregisterRenderEffectFilter( + videoChannel1)); + EXPECT_EQ(0, ViE.image_process->DeregisterRenderEffectFilter( + videoChannel2)); + EXPECT_EQ(sendCodec1.width, filter1.last_render_width_); + EXPECT_EQ(sendCodec1.height, filter1.last_render_height_); + EXPECT_EQ(sendCodec2.width, filter2.last_render_width_); + EXPECT_EQ(sendCodec2.height, filter2.last_render_height_); EXPECT_EQ(0, ViE.base->DeleteChannel(videoChannel1)); EXPECT_EQ(0, ViE.base->DeleteChannel(videoChannel2)); - } } diff --git a/src/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc b/src/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc index 4ea8383b98..901198e3a5 100644 --- a/src/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc +++ b/src/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc @@ -112,7 +112,6 @@ void ViEAutoTest::ViERtpRtcpStandardTest() // *************************************************************** // Engine ready. Begin testing class // *************************************************************** - unsigned short startSequenceNumber = 12345; ViETest::Log("Set start sequence number: %u", startSequenceNumber); EXPECT_EQ(0, ViE.rtp_rtcp->SetStartSequenceNumber( @@ -279,13 +278,14 @@ void ViEAutoTest::ViERtpRtcpStandardTest() // __FUNCTION__, __LINE__); EXPECT_EQ(0, ViE.base->StopReceive(tbChannel.videoChannel)); + EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel)); EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, false)); + // // Keepalive // ViETest::Log("Testing RTP keep alive...\n"); - EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel)); EXPECT_EQ(0, ViE.base->StartReceive(tbChannel.videoChannel)); myTransport.SetPacketLoss(0); @@ -375,6 +375,94 @@ void ViEAutoTest::ViERtpRtcpStandardTest() // Deregister external transport EXPECT_EQ(0, ViE.network->DeregisterSendTransport(tbChannel.videoChannel)); + { + // Create three channels. 1 and 2 are grouped together and will get a + // common REMB packet. 3 is in its own group and will get a separate REMB + // packet. To verify we receive a REMB, set a higher start bitrate for 2 + // than 1 and verify the estimated send bitrate for 2 is lowered. Also, + // verify that the bitrate estimate for 3 is kept high and not lowered + // due to 1 and 2. + const unsigned int start_rate_1_bps = 100000; + const unsigned int start_rate_2_bps = 300000; + const unsigned int start_rate_3_bps = 1000000; + + int channel_1 = -1; + int channel_2 = -1; + int channel_3 = -1; + EXPECT_EQ(0, ViE.base->CreateChannel(channel_1)); + EXPECT_EQ(0, ViE.base->CreateChannel(channel_2, channel_1)); + EXPECT_EQ(0, ViE.base->CreateChannel(channel_3)); + + //TbCaptureDevice tbCapture(ViE); + tbCapture.ConnectTo(channel_1); + tbCapture.ConnectTo(channel_2); + tbCapture.ConnectTo(channel_3); + + TbExternalTransport transport_1(*(ViE.network)); + TbExternalTransport transport_2(*(ViE.network)); + TbExternalTransport transport_3(*(ViE.network)); + + EXPECT_EQ(0, ViE.network->RegisterSendTransport(channel_1, transport_1)); + EXPECT_EQ(0, ViE.network->RegisterSendTransport(channel_2, transport_2)); + EXPECT_EQ(0, ViE.network->RegisterSendTransport(channel_3, transport_3)); + + webrtc::VideoCodec video_codec; + for (int idx = 0; idx < ViE.codec->NumberOfCodecs(); ++idx) { + ViE.codec->GetCodec(idx, video_codec); + if (video_codec.codecType == webrtc::kVideoCodecVP8) { + break; + } + } + EXPECT_EQ(0, ViE.codec->SetReceiveCodec(channel_1, video_codec)); + EXPECT_EQ(0, ViE.codec->SetReceiveCodec(channel_2, video_codec)); + EXPECT_EQ(0, ViE.codec->SetReceiveCodec(channel_3, video_codec)); + + video_codec.startBitrate = start_rate_1_bps / 1000; + EXPECT_EQ(0, ViE.codec->SetSendCodec(channel_1, video_codec)); + video_codec.startBitrate = start_rate_2_bps / 1000; + EXPECT_EQ(0, ViE.codec->SetSendCodec(channel_2, video_codec)); + video_codec.startBitrate = start_rate_3_bps / 1000; + EXPECT_EQ(0, ViE.codec->SetSendCodec(channel_3, video_codec)); + + EXPECT_EQ(0, ViE.rtp_rtcp->SetRembStatus(channel_1, true, true)); + EXPECT_EQ(0, ViE.rtp_rtcp->SetRembStatus(channel_2, true, true)); + EXPECT_EQ(0, ViE.rtp_rtcp->SetRembStatus(channel_3, true, true)); + + EXPECT_EQ(0, ViE.base->StartReceive(channel_1)); + EXPECT_EQ(0, ViE.base->StartReceive(channel_2)); + EXPECT_EQ(0, ViE.base->StartReceive(channel_3)); + EXPECT_EQ(0, ViE.base->StartSend(channel_1)); + EXPECT_EQ(0, ViE.base->StartSend(channel_2)); + EXPECT_EQ(0, ViE.base->StartSend(channel_3)); + + AutoTestSleep(KAutoTestSleepTimeMs); + + EXPECT_EQ(0, ViE.base->StopReceive(channel_1)); + EXPECT_EQ(0, ViE.base->StopReceive(channel_2)); + EXPECT_EQ(0, ViE.base->StopReceive(channel_3)); + EXPECT_EQ(0, ViE.base->StopSend(channel_1)); + EXPECT_EQ(0, ViE.base->StopSend(channel_2)); + EXPECT_EQ(0, ViE.base->StopSend(channel_3)); + + unsigned int bw_estimate_1 = 0; + unsigned int bw_estimate_2 = 0; + unsigned int bw_estimate_3 = 0; + ViE.rtp_rtcp->GetEstimatedSendBandwidth(channel_1, &bw_estimate_1); + ViE.rtp_rtcp->GetEstimatedSendBandwidth(channel_2, &bw_estimate_2); + ViE.rtp_rtcp->GetEstimatedSendBandwidth(channel_3, &bw_estimate_3); + + EXPECT_LT(bw_estimate_1, start_rate_2_bps); + EXPECT_LT(bw_estimate_2, start_rate_2_bps); + EXPECT_NE(bw_estimate_1, start_rate_1_bps); + + // Add some margin to avoid flaky test runs. + EXPECT_GT(bw_estimate_3, 0.75 * start_rate_3_bps); + + EXPECT_EQ(0, ViE.base->DeleteChannel(channel_1)); + EXPECT_EQ(0, ViE.base->DeleteChannel(channel_2)); + EXPECT_EQ(0, ViE.base->DeleteChannel(channel_3)); + } + //*************************************************************** // Testing finished. Tear down Video Engine //*************************************************************** diff --git a/src/video_engine/video_engine_core.gypi b/src/video_engine/video_engine_core.gypi index 38015af0e6..bbc4f6e7dc 100644 --- a/src/video_engine/video_engine_core.gypi +++ b/src/video_engine/video_engine_core.gypi @@ -78,6 +78,7 @@ 'vie_shared_data.h', 'vie_capturer.h', 'vie_channel.h', + 'vie_channel_group.h', 'vie_channel_manager.h', 'vie_encoder.h', 'vie_file_image.h', @@ -109,6 +110,7 @@ 'vie_shared_data.cc', 'vie_capturer.cc', 'vie_channel.cc', + 'vie_channel_group.cc', 'vie_channel_manager.cc', 'vie_encoder.cc', 'vie_file_image.cc', diff --git a/src/video_engine/vie_base_impl.cc b/src/video_engine/vie_base_impl.cc index 4f0eae2acf..a90827c6a3 100644 --- a/src/video_engine/vie_base_impl.cc +++ b/src/video_engine/vie_base_impl.cc @@ -124,34 +124,12 @@ int ViEBaseImpl::CreateChannel(int& video_channel) { } int ViEBaseImpl::CreateChannel(int& video_channel, int original_channel) { - if (!(shared_data_.Initialized())) { - shared_data_.SetLastError(kViENotInitialized); - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s - ViE instance %d not initialized", __FUNCTION__, - shared_data_.instance_id()); - return -1; - } + return CreateChannel(video_channel, original_channel, true); +} - ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); - if (!cs.Channel(original_channel)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s - original_channel does not exist.", __FUNCTION__, - shared_data_.instance_id()); - shared_data_.SetLastError(kViEBaseInvalidChannelId); - return -1; - } - - if (shared_data_.channel_manager()->CreateChannel(video_channel, - original_channel) == -1) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: Could not create channel", __FUNCTION__); - video_channel = -1; - shared_data_.SetLastError(kViEBaseChannelCreationFailed); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: channel created: %d", __FUNCTION__, video_channel); - return 0; +int ViEBaseImpl::CreateReceiveChannel(int& video_channel, + int original_channel) { + return CreateChannel(video_channel, original_channel, false); } int ViEBaseImpl::DeleteChannel(const int video_channel) { @@ -270,27 +248,13 @@ int ViEBaseImpl::StartSend(const int video_channel) { return -1; } - // Verify no other channel using the same encoder is sending. - ChannelList channels; - cs.ChannelsUsingViEEncoder(video_channel, &channels); - for (ChannelList::iterator it = channels.begin(); it != channels.end(); - ++it) { - if ((*it)->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "A channel using this encoder is already synding"); - shared_data_.SetLastError(kViEBaseAlreadySending); - return -1; - } - } - ViEEncoder* vie_encoder = cs.Encoder(video_channel); - if (!vie_encoder) { - assert(false); + assert(vie_encoder != NULL); + if (vie_encoder->Owner() != video_channel) { WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id(), video_channel), - "%s: Could not find encoder for channel %d", __FUNCTION__, - video_channel); + "Can't start ssend on a receive only channel."); + shared_data_.SetLastError(kViEBaseReceiveOnlyChannel); return -1; } @@ -491,4 +455,37 @@ WebRtc_Word32 ViEBaseImpl::AddExternalTransportBuild(char* str) const { #endif } +int ViEBaseImpl::CreateChannel(int& video_channel, int original_channel, + bool sender) { + if (!(shared_data_.Initialized())) { + shared_data_.SetLastError(kViENotInitialized); + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), + "%s - ViE instance %d not initialized", __FUNCTION__, + shared_data_.instance_id()); + return -1; + } + + ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); + if (!cs.Channel(original_channel)) { + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), + "%s - original_channel does not exist.", __FUNCTION__, + shared_data_.instance_id()); + shared_data_.SetLastError(kViEBaseInvalidChannelId); + return -1; + } + + if (shared_data_.channel_manager()->CreateChannel(video_channel, + original_channel, + sender) == -1) { + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), + "%s: Could not create channel", __FUNCTION__); + video_channel = -1; + shared_data_.SetLastError(kViEBaseChannelCreationFailed); + return -1; + } + WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(shared_data_.instance_id()), + "%s: channel created: %d", __FUNCTION__, video_channel); + return 0; +} + } // namespace webrtc diff --git a/src/video_engine/vie_base_impl.h b/src/video_engine/vie_base_impl.h index a950300465..1a3c2ceeb2 100644 --- a/src/video_engine/vie_base_impl.h +++ b/src/video_engine/vie_base_impl.h @@ -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 @@ -27,52 +27,23 @@ class ViEBaseImpl public: virtual int Release(); - // Initializes VideoEngine and must be called before any other API is called. + // Implements ViEBase. virtual int Init(); - - // Connects ViE to a VoE instance. Pass in NULL to forget about a previously - // set voice engine and release all resources we allocated from it. virtual int SetVoiceEngine(VoiceEngine* voice_engine); - - // Creates a new ViE channel. virtual int CreateChannel(int& video_channel); - - // Creates a new ViE channel that will use the same capture device and encoder - // as |original_channel|. virtual int CreateChannel(int& video_channel, int original_channel); - - // Deletes a ViE channel. + virtual int CreateReceiveChannel(int& video_channel, int original_channel); virtual int DeleteChannel(const int video_channel); - - // Connects a ViE channel with a VoE channel. virtual int ConnectAudioChannel(const int video_channel, const int audio_channel); - - // Disconnects a video/voice channel pair. virtual int DisconnectAudioChannel(const int video_channel); - - // Starts sending on video_channel and also starts the encoder. virtual int StartSend(const int video_channel); - - // Stops sending on the specified channel. virtual int StopSend(const int video_channel); - - // Starts receiving on the channel and also start decoding. virtual int StartReceive(const int video_channel); - - // Stops receiving on the specified channel. virtual int StopReceive(const int video_channel); - - // Registers a customer implemented observer. virtual int RegisterObserver(ViEBaseObserver& observer); - - // Deregisters the observer. virtual int DeregisterObserver(); - - // Prints version information into |version|. virtual int GetVersion(char version[1024]); - - // Returns the error code for the last registered error. virtual int LastError(); protected: @@ -87,6 +58,8 @@ class ViEBaseImpl WebRtc_Word32 AddBuildInfo(char* str) const; WebRtc_Word32 AddExternalTransportBuild(char* str) const; + int CreateChannel(int& video_channel, int original_channel, bool sender); + // ViEBaseImpl owns ViESharedData used by all interface implementations. ViESharedData shared_data_; }; diff --git a/src/video_engine/vie_capture_impl.cc b/src/video_engine/vie_capture_impl.cc index d5ce489b02..fb941b6a68 100644 --- a/src/video_engine/vie_capture_impl.cc +++ b/src/video_engine/vie_capture_impl.cc @@ -219,6 +219,13 @@ int ViECaptureImpl::ConnectCaptureDevice(const int capture_id, shared_data_->SetLastError(kViECaptureDeviceInvalidChannelId); return -1; } + if (vie_encoder->Owner() != video_channel) { + WEBRTC_TRACE(kTraceError, kTraceVideo, + ViEId(shared_data_->instance_id(), video_channel), + "Can't connect capture device to a receive only channel."); + shared_data_->SetLastError(kViECaptureDeviceInvalidChannelId); + return -1; + } // Check if the encoder already has a connected frame provider if (is.FrameProvider(vie_encoder) != NULL) { WEBRTC_TRACE(kTraceError, kTraceVideo, diff --git a/src/video_engine/vie_channel_group.cc b/src/video_engine/vie_channel_group.cc new file mode 100644 index 0000000000..830ba6485d --- /dev/null +++ b/src/video_engine/vie_channel_group.cc @@ -0,0 +1,79 @@ +/* + * 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 + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "video_engine/vie_channel_group.h" + +#include "modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "video_engine/vie_channel.h" +#include "video_engine/vie_encoder.h" +#include "video_engine/vie_remb.h" + +namespace webrtc { + +ChannelGroup::ChannelGroup(ProcessThread* process_thread) + : remb_(new VieRemb(process_thread)) {} + +ChannelGroup::~ChannelGroup() { + assert(channels_.empty()); + assert(!remb_->InUse()); +} +void ChannelGroup::AddChannel(int channel_id) { + channels_.insert(channel_id); +} + +void ChannelGroup::RemoveChannel(int channel_id) { + channels_.erase(channel_id); +} + +bool ChannelGroup::HasChannel(int channel_id) { + return channels_.find(channel_id) != channels_.end(); +} + +bool ChannelGroup::Empty() { + return channels_.empty(); +} + +bool ChannelGroup::SetChannelRembStatus(int channel_id, + bool sender, + bool receiver, + ViEChannel* channel, + ViEEncoder* encoder) { + // Update the channel state. + if (sender || receiver) { + if (!channel->EnableRemb(true)) { + return false; + } + } else if (channel) { + channel->EnableRemb(false); + } + + // Update the remb instance with necesary RTp modules. + RtpRtcp* rtp_module = channel->rtp_rtcp(); + if (sender) { + remb_->AddRembSender(rtp_module); + remb_->AddSendChannel(encoder->SendRtpRtcpModule()); + } else { + remb_->RemoveRembSender(rtp_module); + remb_->RemoveSendChannel(encoder->SendRtpRtcpModule()); + } + if (receiver) { + remb_->AddReceiveChannel(rtp_module); + } else { + remb_->RemoveReceiveChannel(rtp_module); + } + if (sender || receiver) { + rtp_module->SetRemoteBitrateObserver(remb_.get()); + } else { + rtp_module->SetRemoteBitrateObserver(NULL); + } + return true; +} + +} // namespace webrtc diff --git a/src/video_engine/vie_channel_group.h b/src/video_engine/vie_channel_group.h new file mode 100644 index 0000000000..1dce893362 --- /dev/null +++ b/src/video_engine/vie_channel_group.h @@ -0,0 +1,52 @@ +/* + * 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 + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_VIDEO_ENGINE_VIE_CHANNEL_GROUP_H_ +#define WEBRTC_VIDEO_ENGINE_VIE_CHANNEL_GROUP_H_ + +#include + +#include "system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class ProcessThread; +class ViEChannel; +class ViEEncoder; +class VieRemb; + +// Channel group contains data common for several channels. All channels in the +// group are assumed to send/receive data to the same end-point. +class ChannelGroup { + public: + explicit ChannelGroup(ProcessThread* process_thread); + ~ChannelGroup(); + + void AddChannel(int channel_id); + void RemoveChannel(int channel_id); + bool HasChannel(int channel_id); + bool Empty(); + + bool SetChannelRembStatus(int channel_id, + bool sender, + bool receiver, + ViEChannel* channel, + ViEEncoder* encoder); + + private: + typedef std::set ChannelSet; + + scoped_ptr remb_; + ChannelSet channels_; +}; + +} // namespace webrtc + +#endif // WEBRTC_VIDEO_ENGINE_VIE_CHANNEL_GROUP_H_ diff --git a/src/video_engine/vie_channel_manager.cc b/src/video_engine/vie_channel_manager.cc index 268bc18193..ebd6436f6b 100644 --- a/src/video_engine/vie_channel_manager.cc +++ b/src/video_engine/vie_channel_manager.cc @@ -35,7 +35,6 @@ ViEChannelManager::ViEChannelManager( free_channel_ids_(new bool[kViEMaxNumberOfChannels]), free_channel_ids_size_(kViEMaxNumberOfChannels), voice_sync_interface_(NULL), - remb_(new VieRemb(engine_id)), voice_engine_(NULL), module_process_thread_(NULL) { WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id), @@ -50,7 +49,6 @@ ViEChannelManager::~ViEChannelManager() { WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_), "ViEChannelManager Destructor, engine_id: %d", engine_id_); - module_process_thread_->DeRegisterModule(remb_.get()); while (channel_map_.size() > 0) { ChannelMap::iterator it = channel_map_.begin(); // DeleteChannel will erase this channel from the map and invalidate |it|. @@ -69,131 +67,83 @@ ViEChannelManager::~ViEChannelManager() { free_channel_ids_ = NULL; free_channel_ids_size_ = 0; } + assert(channel_groups_.empty()); + assert(channel_map_.empty()); + assert(vie_encoder_map_.empty()); } void ViEChannelManager::SetModuleProcessThread( ProcessThread& module_process_thread) { assert(!module_process_thread_); module_process_thread_ = &module_process_thread; - module_process_thread_->RegisterModule(remb_.get()); } int ViEChannelManager::CreateChannel(int& channel_id) { CriticalSectionScoped cs(*channel_id_critsect_); - // Get a free id for the new channel. - if (!GetFreeChannelId(channel_id)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "Max number of channels reached: %d", channel_map_.size()); + // Get a new channel id. + int new_channel_id = FreeChannelId(); + if (new_channel_id == -1) { return -1; } - ViEChannel* vie_channel = new ViEChannel(channel_id, engine_id_, + // Create a new channel group and add this channel. + ChannelGroup* group = new ChannelGroup(module_process_thread_); + ViEEncoder* vie_encoder = new ViEEncoder(engine_id_, new_channel_id, number_of_cores_, *module_process_thread_); - if (!vie_channel) { - ReturnChannelId(channel_id); - return -1; - } - if (vie_channel->Init() != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s could not init channel", __FUNCTION__, channel_id); - ReturnChannelId(channel_id); - delete vie_channel; - vie_channel = NULL; - return -1; + if (!CreateChannelObject(new_channel_id, vie_encoder)) { + delete vie_encoder; + vie_encoder = NULL; + ReturnChannelId(new_channel_id); + delete group; } - // There is no ViEEncoder for this channel, create one with default settings. - ViEEncoder* vie_encoder = new ViEEncoder(engine_id_, channel_id, - number_of_cores_, - *module_process_thread_); - if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s(video_channel_id: %d) - Could not create a new encoder", - __FUNCTION__, channel_id); - delete vie_channel; - return -1; - } - - vie_encoder_map_[channel_id] = vie_encoder; - channel_map_[channel_id] = vie_channel; - - // Register the channel at the encoder. - RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule(); - if (vie_channel->RegisterSendRtpRtcpModule(*send_rtp_rtcp_module) != 0) { - assert(false); - vie_encoder_map_.erase(channel_id); - channel_map_.erase(channel_id); - ReturnChannelId(channel_id); - delete vie_channel; - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), - "%s: Could not register rtp module %d", __FUNCTION__, - channel_id); - return -1; - } + channel_id = new_channel_id; + group->AddChannel(channel_id); + channel_groups_.push_back(group); return 0; } -int ViEChannelManager::CreateChannel(int& channel_id, int original_channel) { +int ViEChannelManager::CreateChannel(int& channel_id, + int original_channel, + bool sender) { CriticalSectionScoped cs(*channel_id_critsect_); - // Check that original_channel already exists. - ViEEncoder* vie_encoder = ViEEncoderPtr(original_channel); + ChannelGroup* channel_group = FindGroup(original_channel); + if (!channel_group) { + return -1; + } + + int new_channel_id = FreeChannelId(); + if (new_channel_id == -1) { + return -1; + } + + ViEEncoder* vie_encoder = NULL; + if (sender) { + // We need to create a new ViEEncoder. + vie_encoder = new ViEEncoder(engine_id_, new_channel_id, number_of_cores_, + *module_process_thread_); + if (!CreateChannelObject(new_channel_id, vie_encoder)) { + delete vie_encoder; + vie_encoder = NULL; + } + } else { + vie_encoder = ViEEncoderPtr(original_channel); + assert(vie_encoder); + if (!CreateChannelObject(new_channel_id, vie_encoder)) { + vie_encoder = NULL; + } + } + if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s: Original channel doesn't exist", __FUNCTION__, - original_channel); + ReturnChannelId(new_channel_id); return -1; } - VideoCodec video_codec; - vie_encoder->GetEncoder(video_codec); - // Get a free id for the new channel. - if (GetFreeChannelId(channel_id) == false) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "Max number of channels reached: %d", channel_map_.size()); - return -1; - } - ViEChannel* vie_channel = new ViEChannel(channel_id, engine_id_, - number_of_cores_, - *module_process_thread_); - if (!vie_channel) { - ReturnChannelId(channel_id); - return -1; - } - if (vie_channel->Init() != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s could not init channel", __FUNCTION__, channel_id); - ReturnChannelId(channel_id); - delete vie_channel; - vie_channel = NULL; - return -1; - } - vie_encoder_map_[channel_id] = vie_encoder; - - // Set the same encoder settings for the channel as used by the master - // channel. Do this before attaching rtp module to ensure all rtp children has - // the same codec type. - VideoCodec encoder; - if (vie_encoder->GetEncoder(encoder) == 0) { - vie_channel->SetSendCodec(encoder); - } - channel_map_[channel_id] = vie_channel; - - // Register the channel at the encoder. - RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule(); - if (vie_channel->RegisterSendRtpRtcpModule(*send_rtp_rtcp_module) != 0) { - assert(false); - vie_encoder_map_.erase(channel_id); - channel_map_.erase(channel_id); - ReturnChannelId(channel_id); - delete vie_channel; - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), - "%s: Could not register rtp module %d", __FUNCTION__, - channel_id); - return -1; - } + channel_id = new_channel_id; + channel_group->AddChannel(channel_id); return 0; } @@ -204,8 +154,9 @@ int ViEChannelManager::DeleteChannel(int channel_id) { // Write lock to make sure no one is using the channel. ViEManagerWriteScoped wl(*this); - // Protect the map. + // Protect the maps. CriticalSectionScoped cs(*channel_id_critsect_); + ChannelMap::iterator c_it = channel_map_.find(channel_id); if (c_it == channel_map_.end()) { // No such channel. @@ -216,11 +167,6 @@ int ViEChannelManager::DeleteChannel(int channel_id) { vie_channel = c_it->second; channel_map_.erase(c_it); - // Deregister possible remb modules. - RtpRtcp* rtp_module = vie_channel->rtp_rtcp(); - remb_->RemoveRembSender(rtp_module); - remb_->RemoveReceiveChannel(rtp_module); - // Deregister the channel from the ViEEncoder to stop the media flow. vie_channel->DeregisterSendRtpRtcpModule(); ReturnChannelId(channel_id); @@ -230,7 +176,14 @@ int ViEChannelManager::DeleteChannel(int channel_id) { assert(e_it != vie_encoder_map_.end()); vie_encoder = e_it->second; - remb_->RemoveSendChannel(vie_encoder->SendRtpRtcpModule()); + ChannelGroup* group = FindGroup(channel_id); + group->SetChannelRembStatus(channel_id, false, false, vie_channel, + vie_encoder); + group->RemoveChannel(channel_id); + if (group->Empty()) { + channel_groups_.remove(group); + delete group; + } // Check if other channels are using the same encoder. if (ChannelUsingViEEncoder(channel_id)) { @@ -327,38 +280,49 @@ VoiceEngine* ViEChannelManager::GetVoiceEngine() { bool ViEChannelManager::SetRembStatus(int channel_id, bool sender, bool receiver) { CriticalSectionScoped cs(*channel_id_critsect_); + ChannelGroup* group = FindGroup(channel_id); + if (!group) { + return -1; + } ViEChannel* channel = ViEChannelPtr(channel_id); - if (!channel) { + assert(channel); + ViEEncoder* encoder = ViEEncoderPtr(channel_id); + assert(encoder); + + return group->SetChannelRembStatus(channel_id, sender, receiver, channel, + encoder); +} + +bool ViEChannelManager::CreateChannelObject(int channel_id, + ViEEncoder* vie_encoder) { + ViEChannel* vie_channel = new ViEChannel(channel_id, engine_id_, + number_of_cores_, + *module_process_thread_); + if (vie_channel->Init() != 0) { + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), + "%s could not init channel", __FUNCTION__, channel_id); + delete vie_channel; return false; } - ViEEncoder* encoder = ViEEncoderPtr(channel_id); + VideoCodec encoder; + vie_encoder->GetEncoder(encoder); + if (vie_channel->SetSendCodec(encoder) != 0) { + vie_encoder = NULL; + } - if (sender || receiver) { - if (!channel->EnableRemb(true)) { - return false; - } - } else { - channel->EnableRemb(false); - } - RtpRtcp* rtp_module = channel->rtp_rtcp(); - if (sender) { - remb_->AddRembSender(rtp_module); - remb_->AddSendChannel(encoder->SendRtpRtcpModule()); - } else { - remb_->RemoveRembSender(rtp_module); - remb_->RemoveSendChannel(encoder->SendRtpRtcpModule()); - } - if (receiver) { - remb_->AddReceiveChannel(rtp_module); - } else { - remb_->RemoveReceiveChannel(rtp_module); - } - if (sender || receiver) { - rtp_module->SetRemoteBitrateObserver(remb_.get()); - } else { - rtp_module->SetRemoteBitrateObserver(NULL); + // Register the channel at the encoder. + RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule(); + if (vie_channel->RegisterSendRtpRtcpModule(*send_rtp_rtcp_module) != 0) { + delete vie_channel; + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), + "%s: Could not register RTP module", __FUNCTION__); + return false; } + + // Store the channel, add it to the channel group and save the vie_encoder. + channel_map_[channel_id] = vie_channel; + vie_encoder_map_[channel_id] = vie_encoder; return true; } @@ -395,21 +359,19 @@ ViEEncoder* ViEChannelManager::ViEEncoderPtr(int video_channel_id) const { return it->second; } -bool ViEChannelManager::GetFreeChannelId(int& free_channel_id) { - CriticalSectionScoped cs(*channel_id_critsect_); +int ViEChannelManager::FreeChannelId() { int idx = 0; while (idx < free_channel_ids_size_) { if (free_channel_ids_[idx] == true) { // We've found a free id, allocate it and return. free_channel_ids_[idx] = false; - free_channel_id = idx + kViEChannelIdBase; - return true; + return idx + kViEChannelIdBase; } idx++; } - // No free channel id. - free_channel_id = -1; - return false; + WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), + "Max number of channels reached: %d", channel_map_.size()); + return -1; } void ViEChannelManager::ReturnChannelId(int channel_id) { @@ -419,10 +381,20 @@ void ViEChannelManager::ReturnChannelId(int channel_id) { free_channel_ids_[channel_id - kViEChannelIdBase] = true; } +ChannelGroup* ViEChannelManager::FindGroup(int channel_id) { + for (ChannelGroups::iterator it = channel_groups_.begin(); + it != channel_groups_.end(); ++it) { + if ((*it)->HasChannel(channel_id)) { + return *it; + } + } + return NULL; +} + bool ViEChannelManager::ChannelUsingViEEncoder(int channel_id) const { CriticalSectionScoped cs(*channel_id_critsect_); EncoderMap::const_iterator orig_it = vie_encoder_map_.find(channel_id); - if(orig_it == vie_encoder_map_.end()) { + if (orig_it == vie_encoder_map_.end()) { // No ViEEncoder for this channel. return false; } diff --git a/src/video_engine/vie_channel_manager.h b/src/video_engine/vie_channel_manager.h index f0fa34813f..595afa7b73 100644 --- a/src/video_engine/vie_channel_manager.h +++ b/src/video_engine/vie_channel_manager.h @@ -17,8 +17,10 @@ #include "engine_configurations.h" #include "system_wrappers/interface/scoped_ptr.h" #include "typedefs.h" +#include "video_engine/vie_channel_group.h" #include "video_engine/vie_defines.h" #include "video_engine/vie_manager_base.h" +#include "video_engine/vie_remb.h" namespace webrtc { @@ -28,10 +30,10 @@ class ProcessThread; class ViEChannel; class ViEEncoder; class ViEPerformanceMonitor; -class VieRemb; class VoEVideoSync; class VoiceEngine; +typedef std::list ChannelGroups; typedef std::list ChannelList; typedef std::map ChannelMap; typedef std::map EncoderMap; @@ -49,8 +51,10 @@ class ViEChannelManager: private ViEManagerBase { // Creates a new channel. 'channelId' will be the id of the created channel. int CreateChannel(int& channel_id); - // Creates a channel and attaches to an already existing ViEEncoder. - int CreateChannel(int& channel_id, int original_channel); + // Creates a new channel grouped with |original_channel|. The new channel + // will get its own |ViEEncoder| if |sender| is set to true. It will be a + // receive only channel, without an own |ViEEncoder| if |sender| is false. + int CreateChannel(int& channel_id, int original_channel, bool sender); // Deletes a channel. int DeleteChannel(int channel_id); @@ -70,6 +74,10 @@ class ViEChannelManager: private ViEManagerBase { bool SetRembStatus(int channel_id, bool sender, bool receiver); private: + // Creates a channel object connected to |vie_encoder|. Assumed to be called + // protected. + bool CreateChannelObject(int channel_id, ViEEncoder* vie_encoder); + // Used by ViEChannelScoped, forcing a manager user to use scoped. // Returns a pointer to the channel with id 'channelId'. ViEChannel* ViEChannelPtr(int channel_id) const; @@ -81,13 +89,15 @@ class ViEChannelManager: private ViEManagerBase { // Gets the ViEEncoder used as input for video_channel_id ViEEncoder* ViEEncoderPtr(int video_channel_id) const; - // Returns true if we found a new channel id, free_channel_id, false - // otherwise. - bool GetFreeChannelId(int& free_channel_id); + // Returns a free channel id, -1 if failing. + int FreeChannelId(); // Returns a previously allocated channel id. void ReturnChannelId(int channel_id); + // Returns the iterator to the ChannelGroup containing |channel_id|. + ChannelGroup* FindGroup(int channel_id); + // Returns true if at least one other channels uses the same ViEEncoder as // channel_id. bool ChannelUsingViEEncoder(int channel_id) const; @@ -98,14 +108,20 @@ class ViEChannelManager: private ViEManagerBase { int engine_id_; int number_of_cores_; ViEPerformanceMonitor& vie_performance_monitor_; + + // TODO(mflodman) Make part of channel group. ChannelMap channel_map_; bool* free_channel_ids_; int free_channel_ids_size_; + // List with all channel groups. + std::list channel_groups_; + + // TODO(mflodman) Make part of channel group. // Maps Channel id -> ViEEncoder. EncoderMap vie_encoder_map_; VoEVideoSync* voice_sync_interface_; - scoped_ptr remb_; + VoiceEngine* voice_engine_; ProcessThread* module_process_thread_; }; diff --git a/src/video_engine/vie_codec_impl.cc b/src/video_engine/vie_codec_impl.cc index f995117900..d69ab0c0d7 100644 --- a/src/video_engine/vie_codec_impl.cc +++ b/src/video_engine/vie_codec_impl.cc @@ -144,6 +144,16 @@ int ViECodecImpl::SetSendCodec(const int video_channel, return -1; } + ViEEncoder* vie_encoder = cs.Encoder(video_channel); + assert(vie_encoder); + if (vie_encoder->Owner() != video_channel) { + WEBRTC_TRACE(kTraceError, kTraceVideo, + ViEId(shared_data_->instance_id(), video_channel), + "%s: Receive only channel %d", __FUNCTION__, video_channel); + shared_data_->SetLastError(kViECodecReceiveOnlyChannel); + return -1; + } + // Set a max_bitrate if the user hasn't set one. VideoCodec video_codec_internal; memcpy(&video_codec_internal, &video_codec, sizeof(VideoCodec)); @@ -163,28 +173,9 @@ int ViECodecImpl::SetSendCodec(const int video_channel, video_codec_internal.maxBitrate); } - ViEEncoder* vie_encoder = cs.Encoder(video_channel); - if (!vie_encoder) { - assert(false); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder found for channel %d", __FUNCTION__); - shared_data_->SetLastError(kViECodecInvalidChannelId); - return -1; - } - VideoCodec encoder; vie_encoder->GetEncoder(encoder); - if (encoder.codecType != video_codec_internal.codecType && - cs.ChannelUsingViEEncoder(video_channel)) { - // We don't allow changing codec type when several channels share encoder. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Settings differs from other channels using encoder", - __FUNCTION__); - shared_data_->SetLastError(kViECodecInUse); - return -1; - } + // 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; @@ -193,17 +184,6 @@ int ViECodecImpl::SetSendCodec(const int video_channel, encoder.height != video_codec_internal.height) { new_rtp_stream = true; } - if (video_codec_internal.numberOfSimulcastStreams > 1) { - if (cs.ChannelUsingViEEncoder(video_channel)) { - // We don't allow simulcast channels to share encoder. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Can't share simulcast encoder", - __FUNCTION__); - shared_data_->SetLastError(kViECodecInUse); - return -1; - } - } ViEInputManagerScoped is(*(shared_data_->input_manager())); ViEFrameProviderBase* frame_provider = NULL; diff --git a/src/video_engine/vie_encoder.cc b/src/video_engine/vie_encoder.cc index cf3587b0da..20d8dacaa3 100644 --- a/src/video_engine/vie_encoder.cc +++ b/src/video_engine/vie_encoder.cc @@ -172,6 +172,10 @@ ViEEncoder::~ViEEncoder() { delete qm_callback_; } +int ViEEncoder::Owner() const { + return channel_id_; +} + void ViEEncoder::Pause() { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_, channel_id_), diff --git a/src/video_engine/vie_encoder.h b/src/video_engine/vie_encoder.h index 1b204911f4..f2e7f9e6e6 100644 --- a/src/video_engine/vie_encoder.h +++ b/src/video_engine/vie_encoder.h @@ -45,6 +45,9 @@ class ViEEncoder ProcessThread& module_process_thread); ~ViEEncoder(); + // Returns the id of the owning channel. + int Owner() const; + // Drops incoming packets before they get to the encoder. void Pause(); void Restart(); @@ -144,7 +147,7 @@ class ViEEncoder private: WebRtc_Word32 engine_id_; - WebRtc_Word32 channel_id_; + const int channel_id_; const WebRtc_UWord32 number_of_cores_; VideoCodingModule& vcm_; diff --git a/src/video_engine/vie_remb.cc b/src/video_engine/vie_remb.cc index 7732408fe0..3259a2fda6 100644 --- a/src/video_engine/vie_remb.cc +++ b/src/video_engine/vie_remb.cc @@ -14,6 +14,7 @@ #include #include "modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "modules/utility/interface/process_thread.h" #include "system_wrappers/interface/critical_section_wrapper.h" #include "system_wrappers/interface/tick_util.h" #include "system_wrappers/interface/trace.h" @@ -25,19 +26,21 @@ const int kRembSendIntervallMs = 1000; // % threshold for if we should send a new REMB asap. const int kSendThresholdPercent = 97; -VieRemb::VieRemb(int engine_id) - : engine_id_(engine_id), +VieRemb::VieRemb(ProcessThread* process_thread) + : process_thread_(process_thread), list_crit_(CriticalSectionWrapper::CreateCriticalSection()), last_remb_time_(TickTime::MillisecondTimestamp()), last_send_bitrate_(0) { + process_thread->RegisterModule(this); } VieRemb::~VieRemb() { + process_thread_->DeRegisterModule(this); } void VieRemb::AddReceiveChannel(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "VieRemb::AddReceiveChannel(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -45,7 +48,7 @@ void VieRemb::AddReceiveChannel(RtpRtcp* rtp_rtcp) { receive_modules_.end()) return; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, engine_id_, "AddRembChannel"); + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "AddRembChannel"); // The module probably doesn't have a remote SSRC yet, so don't add it to the // map. receive_modules_.push_back(rtp_rtcp); @@ -53,7 +56,7 @@ void VieRemb::AddReceiveChannel(RtpRtcp* rtp_rtcp) { void VieRemb::RemoveReceiveChannel(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "VieRemb::RemoveReceiveChannel(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -70,7 +73,7 @@ void VieRemb::RemoveReceiveChannel(RtpRtcp* rtp_rtcp) { void VieRemb::AddRembSender(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "VieRemb::AddRembSender(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -84,7 +87,7 @@ void VieRemb::AddRembSender(RtpRtcp* rtp_rtcp) { void VieRemb::RemoveRembSender(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "VieRemb::RemoveRembSender(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -99,7 +102,7 @@ void VieRemb::RemoveRembSender(RtpRtcp* rtp_rtcp) { void VieRemb::AddSendChannel(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "VieRemb::AddSendChannel(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -113,7 +116,7 @@ void VieRemb::AddSendChannel(RtpRtcp* rtp_rtcp) { void VieRemb::RemoveSendChannel(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "VieRemb::RemoveSendChannel(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -126,8 +129,16 @@ void VieRemb::RemoveSendChannel(RtpRtcp* rtp_rtcp) { } } +bool VieRemb::InUse() const { + CriticalSectionScoped cs(list_crit_.get()); + if(receive_modules_.empty() && send_modules_.empty() && rtcp_sender_.empty()) + return false; + else + return true; +} + void VieRemb::OnReceiveBitrateChanged(unsigned int ssrc, unsigned int bitrate) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, "VieRemb::UpdateBitrateEstimate(ssrc: %u, bitrate: %u)", ssrc, bitrate); CriticalSectionScoped cs(list_crit_.get()); @@ -147,7 +158,7 @@ void VieRemb::OnReceiveBitrateChanged(unsigned int ssrc, unsigned int bitrate) { } void VieRemb::OnReceivedRemb(unsigned int bitrate) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, engine_id_, + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, "VieRemb::OnReceivedRemb(bitrate: %u)", bitrate); // TODO(mflodman) Should be extended to allow different split of bitrate. // TODO(mflodman) Do we want to call |SetMaximumBitrateEstimate| from diff --git a/src/video_engine/vie_remb.h b/src/video_engine/vie_remb.h index 32d58a7a4d..d2d548e273 100644 --- a/src/video_engine/vie_remb.h +++ b/src/video_engine/vie_remb.h @@ -28,11 +28,12 @@ namespace webrtc { class CriticalSectionWrapper; +class ProcessThread; class RtpRtcp; class VieRemb : public RtpRemoteBitrateObserver, public Module { public: - explicit VieRemb(int engine_id); + VieRemb(ProcessThread* process_thread); ~VieRemb(); // Called to add a receive channel to include in the REMB packet. @@ -54,6 +55,9 @@ class VieRemb : public RtpRemoteBitrateObserver, public Module { // Removes the specified channel from receiving REMB packet estimates. void RemoveSendChannel(RtpRtcp* rtp_rtcp); + // Returns true if the instance is in use, false otherwise. + bool InUse() const; + // Called every time there is a new bitrate estimate for the received stream // with given SSRC. This call will trigger a new RTCP REMB packet if the // bitrate estimate has decreased or if no RTCP REMB packet has been sent for @@ -74,7 +78,7 @@ class VieRemb : public RtpRemoteBitrateObserver, public Module { typedef std::list RtpModules; typedef std::map SsrcBitrate; - int engine_id_; + ProcessThread* process_thread_; scoped_ptr list_crit_; // The last time a REMB was sent. diff --git a/src/video_engine/vie_remb_unittest.cc b/src/video_engine/vie_remb_unittest.cc index a842103bd3..bcfe06f45d 100644 --- a/src/video_engine/vie_remb_unittest.cc +++ b/src/video_engine/vie_remb_unittest.cc @@ -19,6 +19,7 @@ #include "modules/rtp_rtcp/interface/rtp_rtcp.h" #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" +#include "modules/utility/interface/process_thread.h" #include "system_wrappers/interface/scoped_ptr.h" #include "video_engine/vie_remb.h" @@ -28,11 +29,25 @@ using ::testing::Return; namespace webrtc { +// TODO(mflodman) Make a trigger function for this class to fake a clock and +// remove sleeps in the test. +class TestProcessThread : public ProcessThread { + public: + explicit TestProcessThread() {} + ~TestProcessThread() {} + virtual WebRtc_Word32 Start() { return 0; } + virtual WebRtc_Word32 Stop() { return 0; } + virtual WebRtc_Word32 RegisterModule(const Module* module) { return 0; } + virtual WebRtc_Word32 DeRegisterModule(const Module* module) { return 0; } +}; + class ViERembTest : public ::testing::Test { protected: virtual void SetUp() { - vie_remb_.reset(new VieRemb(1234)); + process_thread_.reset(new TestProcessThread); + vie_remb_.reset(new VieRemb(process_thread_.get())); } + scoped_ptr process_thread_; scoped_ptr vie_remb_; void TestSleep(unsigned int time_ms) {