diff --git a/src/modules/utility/interface/file_recorder.h b/src/modules/utility/interface/file_recorder.h index eb460aebb9..01299465a3 100644 --- a/src/modules/utility/interface/file_recorder.h +++ b/src/modules/utility/interface/file_recorder.h @@ -11,12 +11,12 @@ #ifndef WEBRTC_MODULES_UTILITY_INTERFACE_FILE_RECORDER_H_ #define WEBRTC_MODULES_UTILITY_INTERFACE_FILE_RECORDER_H_ -#include "audio_coding_module_typedefs.h" #include "common_types.h" #include "engine_configurations.h" -#include "media_file_defines.h" -#include "module_common_types.h" -#include "tick_util.h" +#include "modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "modules/interface/module_common_types.h" +#include "modules/media_file/interface/media_file_defines.h" +#include "system_wrappers/interface/tick_util.h" #include "typedefs.h" namespace webrtc { diff --git a/src/video_engine/encoder_state_feedback.cc b/src/video_engine/encoder_state_feedback.cc new file mode 100644 index 0000000000..cd155a627d --- /dev/null +++ b/src/video_engine/encoder_state_feedback.cc @@ -0,0 +1,101 @@ +/* + * 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/encoder_state_feedback.h" + +#include + +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "system_wrappers/interface/critical_section_wrapper.h" +#include "video_engine/vie_encoder.h" + +namespace webrtc { + +// Helper class registered at the RTP module relaying callbacks to +// EncoderStatFeedback. +class EncoderStateFeedbackObserver : public RtcpIntraFrameObserver { + public: + explicit EncoderStateFeedbackObserver(EncoderStateFeedback* owner) + : owner_(owner) {} + ~EncoderStateFeedbackObserver() {} + + // Implements RtcpIntraFrameObserver. + virtual void OnReceivedIntraFrameRequest(uint32_t ssrc) { + owner_->OnReceivedIntraFrameRequest(ssrc); + } + virtual void OnReceivedSLI(uint32_t ssrc, uint8_t picture_id) { + owner_->OnReceivedSLI(ssrc, picture_id); + } + virtual void OnReceivedRPSI(uint32_t ssrc, uint64_t picture_id) { + owner_->OnReceivedRPSI(ssrc, picture_id); + } + + private: + EncoderStateFeedback* owner_; +}; + +EncoderStateFeedback::EncoderStateFeedback() + : crit_(CriticalSectionWrapper::CreateCriticalSection()), + observer_(new EncoderStateFeedbackObserver(this)) {} + +EncoderStateFeedback::~EncoderStateFeedback() { + assert(encoders_.empty()); +} + +bool EncoderStateFeedback::AddEncoder(uint32_t ssrc, ViEEncoder* encoder) { + CriticalSectionScoped lock(crit_.get()); + if (encoders_.find(ssrc) != encoders_.end()) + return false; + + encoders_[ssrc] = encoder; + return true; +} + +void EncoderStateFeedback::RemoveEncoder(uint32_t ssrc) { + CriticalSectionScoped lock(crit_.get()); + SsrcEncoderMap::iterator it = encoders_.find(ssrc); + if (it == encoders_.end()) + return; + + encoders_.erase(it); +} + +RtcpIntraFrameObserver* EncoderStateFeedback::GetRtcpIntraFrameObserver() { + return observer_.get(); +} + +void EncoderStateFeedback::OnReceivedIntraFrameRequest(uint32_t ssrc) { + CriticalSectionScoped lock(crit_.get()); + SsrcEncoderMap::iterator it = encoders_.find(ssrc); + if (it == encoders_.end()) + return; + + it->second->OnReceivedIntraFrameRequest(ssrc); +} + +void EncoderStateFeedback::OnReceivedSLI(uint32_t ssrc, uint8_t picture_id) { + CriticalSectionScoped lock(crit_.get()); + SsrcEncoderMap::iterator it = encoders_.find(ssrc); + if (it == encoders_.end()) + return; + + it->second->OnReceivedSLI(ssrc, picture_id); +} + +void EncoderStateFeedback::OnReceivedRPSI(uint32_t ssrc, uint64_t picture_id) { + CriticalSectionScoped lock(crit_.get()); + SsrcEncoderMap::iterator it = encoders_.find(ssrc); + if (it == encoders_.end()) + return; + + it->second->OnReceivedRPSI(ssrc, picture_id); +} + +} // namespace webrtc diff --git a/src/video_engine/encoder_state_feedback.h b/src/video_engine/encoder_state_feedback.h new file mode 100644 index 0000000000..7d063d43ed --- /dev/null +++ b/src/video_engine/encoder_state_feedback.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +// TODO(mflodman) ViEEncoder has a time check to not send key frames too often, +// move the logic to this class. + +#ifndef WEBRTC_VIDEO_ENGINE_ENCODER_STATE_FEEDBACK_H_ +#define WEBRTC_VIDEO_ENGINE_ENCODER_STATE_FEEDBACK_H_ + +#include + +#include "system_wrappers/interface/constructor_magic.h" +#include "system_wrappers/interface/scoped_ptr.h" +#include "typedefs.h" // NOLINT + +namespace webrtc { + +class CriticalSectionWrapper; +class EncoderStateFeedbackObserver; +class RtcpIntraFrameObserver; +class ViEEncoder; + +class EncoderStateFeedback { + public: + friend class EncoderStateFeedbackObserver; + + EncoderStateFeedback(); + ~EncoderStateFeedback(); + + // Adds an encoder to receive feedback for a unique ssrc. + bool AddEncoder(uint32_t ssrc, ViEEncoder* encoder); + + // Removes a registered ViEEncoder. + void RemoveEncoder(uint32_t ssrc); + + // Returns an observer to register at the requesting class. The observer has + // the same lifetime as the EncoderStateFeedback instance. + RtcpIntraFrameObserver* GetRtcpIntraFrameObserver(); + + protected: + // Called by EncoderStateFeedbackObserver when a new key frame is requested. + void OnReceivedIntraFrameRequest(uint32_t ssrc); + void OnReceivedSLI(uint32_t ssrc, uint8_t picture_id); + void OnReceivedRPSI(uint32_t ssrc, uint64_t picture_id); + + private: + typedef std::map SsrcEncoderMap; + + scoped_ptr crit_; + + // Instance registered at the class requesting new key frames. + scoped_ptr observer_; + + // Maps a unique ssrc to the given encoder. + SsrcEncoderMap encoders_; + + DISALLOW_COPY_AND_ASSIGN(EncoderStateFeedback); +}; + +} // namespace webrtc + +#endif // WEBRTC_VIDEO_ENGINE_ENCODER_STATE_FEEDBACK_H_ diff --git a/src/video_engine/encoder_state_feedback_unittest.cc b/src/video_engine/encoder_state_feedback_unittest.cc new file mode 100644 index 0000000000..a7473d35da --- /dev/null +++ b/src/video_engine/encoder_state_feedback_unittest.cc @@ -0,0 +1,142 @@ +/* + * 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. + */ + + +// This file includes unit tests for EncoderStateFeedback. +#include "video_engine/encoder_state_feedback.h" + +#include +#include + +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "modules/utility/interface/process_thread.h" +#include "system_wrappers/interface/scoped_ptr.h" +#include "video_engine/vie_encoder.h" + +namespace webrtc { + +// TODO(mflodman) Create a common mock in module utility. +class TestProcessThread : public ProcessThread { + public: + 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 MockVieEncoder : public ViEEncoder { + public: + explicit MockVieEncoder(TestProcessThread* process_thread) + : ViEEncoder(1, 1, 1, *process_thread, NULL) {} + ~MockVieEncoder() {} + + MOCK_METHOD1(OnReceivedIntraFrameRequest, + void(uint32_t)); + MOCK_METHOD2(OnReceivedSLI, + void(uint32_t ssrc, uint8_t picture_id)); + MOCK_METHOD2(OnReceivedRPSI, + void(uint32_t ssrc, uint64_t picture_id)); +}; + +class VieKeyRequestTest : public ::testing::Test { + protected: + virtual void SetUp() { + process_thread_.reset(new TestProcessThread()); + encoder_state_feedback_.reset(new EncoderStateFeedback()); + } + scoped_ptr process_thread_; + scoped_ptr encoder_state_feedback_; +}; + +TEST_F(VieKeyRequestTest, CreateAndTriggerRequests) { + const int ssrc = 1234; + MockVieEncoder encoder(process_thread_.get()); + EXPECT_EQ(true, encoder_state_feedback_->AddEncoder(ssrc, &encoder)); + + EXPECT_CALL(encoder, OnReceivedIntraFrameRequest(ssrc)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()-> + OnReceivedIntraFrameRequest(ssrc); + + const uint8_t sli_picture_id = 3; + EXPECT_CALL(encoder, OnReceivedSLI(ssrc, sli_picture_id)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()->OnReceivedSLI( + ssrc, sli_picture_id); + + const uint64_t rpsi_picture_id = 9; + EXPECT_CALL(encoder, OnReceivedRPSI(ssrc, rpsi_picture_id)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()->OnReceivedRPSI( + ssrc, rpsi_picture_id); + + encoder_state_feedback_->RemoveEncoder(ssrc); +} + +// Register multiple encoders and make sure the request is relayed to correct +// ViEEncoder. +TEST_F(VieKeyRequestTest, MultipleEncoders) { + const int ssrc_1 = 1234; + const int ssrc_2 = 5678; + MockVieEncoder encoder_1(process_thread_.get()); + MockVieEncoder encoder_2(process_thread_.get()); + EXPECT_EQ(true, encoder_state_feedback_->AddEncoder(ssrc_1, &encoder_1)); + EXPECT_EQ(true, encoder_state_feedback_->AddEncoder(ssrc_2, &encoder_2)); + + EXPECT_CALL(encoder_1, OnReceivedIntraFrameRequest(ssrc_1)) + .Times(1); + EXPECT_CALL(encoder_2, OnReceivedIntraFrameRequest(ssrc_2)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()-> + OnReceivedIntraFrameRequest(ssrc_1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()-> + OnReceivedIntraFrameRequest(ssrc_2); + + const uint8_t sli_pid_1 = 3; + const uint8_t sli_pid_2 = 4; + EXPECT_CALL(encoder_1, OnReceivedSLI(ssrc_1, sli_pid_1)) + .Times(1); + EXPECT_CALL(encoder_2, OnReceivedSLI(ssrc_2, sli_pid_2)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()->OnReceivedSLI( + ssrc_1, sli_pid_1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()->OnReceivedSLI( + ssrc_2, sli_pid_2); + + const uint64_t rpsi_pid_1 = 9; + const uint64_t rpsi_pid_2 = 10; + EXPECT_CALL(encoder_1, OnReceivedRPSI(ssrc_1, rpsi_pid_1)) + .Times(1); + EXPECT_CALL(encoder_2, OnReceivedRPSI(ssrc_2, rpsi_pid_2)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()->OnReceivedRPSI( + ssrc_1, rpsi_pid_1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()->OnReceivedRPSI( + ssrc_2, rpsi_pid_2); + + encoder_state_feedback_->RemoveEncoder(ssrc_1); + EXPECT_CALL(encoder_2, OnReceivedIntraFrameRequest(ssrc_2)) + .Times(1); + encoder_state_feedback_->GetRtcpIntraFrameObserver()-> + OnReceivedIntraFrameRequest(ssrc_2); + encoder_state_feedback_->RemoveEncoder(ssrc_2); +} + +TEST_F(VieKeyRequestTest, AddTwiceError) { + const int ssrc = 1234; + MockVieEncoder encoder(process_thread_.get()); + EXPECT_EQ(true, encoder_state_feedback_->AddEncoder(ssrc, &encoder)); + EXPECT_EQ(false, encoder_state_feedback_->AddEncoder(ssrc, &encoder)); + encoder_state_feedback_->RemoveEncoder(ssrc); +} + +} // namespace webrtc diff --git a/src/video_engine/video_engine_core.gypi b/src/video_engine/video_engine_core.gypi index e24d339d87..43143f371d 100644 --- a/src/video_engine/video_engine_core.gypi +++ b/src/video_engine/video_engine_core.gypi @@ -69,6 +69,7 @@ 'include/vie_rtp_rtcp.h', # headers + 'encoder_state_feedback.h', 'stream_synchronization.h', 'vie_base_impl.h', 'vie_capture_impl.h', @@ -104,6 +105,7 @@ 'vie_sync_module.h', # ViE + 'encoder_state_feedback.cc', 'stream_synchronization.cc', 'vie_base_impl.cc', 'vie_capture_impl.cc', @@ -157,6 +159,7 @@ '../modules/rtp_rtcp/interface', ], 'sources': [ + 'encoder_state_feedback_unittest.cc', 'stream_synchronization_unittest.cc', 'vie_remb_unittest.cc', ],