diff options
author | pwestin@google.com <pwestin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-18 00:06:22 +0000 |
---|---|---|
committer | pwestin@google.com <pwestin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-18 00:06:22 +0000 |
commit | 8dd5e6fbebe5011a1ca78beb645116bbb1e55f36 (patch) | |
tree | d9a5fa81eb5ff2704e5ade036e98641c133707af /media/cast | |
parent | 0e6324b42184712dd66baaeedd8752b69ec5c224 (diff) | |
download | chromium_src-8dd5e6fbebe5011a1ca78beb645116bbb1e55f36.zip chromium_src-8dd5e6fbebe5011a1ca78beb645116bbb1e55f36.tar.gz chromium_src-8dd5e6fbebe5011a1ca78beb645116bbb1e55f36.tar.bz2 |
Cast: Refactor the way timers are first scheduled.
Change the way we schedule the first timers to avoid weak ptrs beeing
accessed by multiple threads.
Inside the CastSender and CastReceiver objects objects where passed
using weak ptrs; the issue was that these objects might have been
created and used by another thread; weak ptr require all usage to be
from the same thread. This cl make sure that all access to the
objects are from the same thread.
BUG=319399
Review URL: https://codereview.chromium.org/64753006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235569 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/cast')
-rw-r--r-- | media/cast/audio_receiver/audio_receiver.cc | 21 | ||||
-rw-r--r-- | media/cast/audio_receiver/audio_receiver.h | 2 | ||||
-rw-r--r-- | media/cast/audio_sender/audio_sender.cc | 18 | ||||
-rw-r--r-- | media/cast/audio_sender/audio_sender.h | 3 | ||||
-rw-r--r-- | media/cast/audio_sender/audio_sender_unittest.cc | 13 | ||||
-rw-r--r-- | media/cast/cast_receiver_impl.cc | 47 | ||||
-rw-r--r-- | media/cast/video_receiver/video_receiver.cc | 93 | ||||
-rw-r--r-- | media/cast/video_receiver/video_receiver.h | 16 | ||||
-rw-r--r-- | media/cast/video_receiver/video_receiver_unittest.cc | 17 | ||||
-rw-r--r-- | media/cast/video_sender/video_encoder.cc | 1 | ||||
-rw-r--r-- | media/cast/video_sender/video_sender.cc | 47 | ||||
-rw-r--r-- | media/cast/video_sender/video_sender.h | 4 | ||||
-rw-r--r-- | media/cast/video_sender/video_sender_unittest.cc | 18 |
13 files changed, 198 insertions, 102 deletions
diff --git a/media/cast/audio_receiver/audio_receiver.cc b/media/cast/audio_receiver/audio_receiver.cc index c3dc393..a3d49fd 100644 --- a/media/cast/audio_receiver/audio_receiver.cc +++ b/media/cast/audio_receiver/audio_receiver.cc @@ -19,7 +19,6 @@ static const int64 kMinSchedulingDelayMs = 1; namespace media { namespace cast { - // Local implementation of RtpData (defined in rtp_rtcp_defines.h). // Used to pass payload data into the audio receiver. class LocalRtpAudioData : public RtpData { @@ -118,17 +117,23 @@ AudioReceiver::AudioReceiver(scoped_refptr<CastEnvironment> cast_environment, audio_config.feedback_ssrc, audio_config.rtcp_c_name)); rtcp_->SetRemoteSSRC(audio_config.incoming_ssrc); - ScheduleNextRtcpReport(); - ScheduleNextCastMessage(); } AudioReceiver::~AudioReceiver() {} +void AudioReceiver::InitializeTimers() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + ScheduleNextRtcpReport(); + ScheduleNextCastMessage(); +} + void AudioReceiver::IncomingParsedRtpPacket(const uint8* payload_data, size_t payload_size, const RtpCastHeader& rtp_header) { // TODO(pwestin): update this as video to refresh over time. + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (time_first_incoming_packet_.is_null()) { + InitializeTimers(); first_incoming_rtp_timestamp_ = rtp_header.webrtc.header.timestamp; time_first_incoming_packet_ = cast_environment_->Clock()->NowTicks(); } @@ -155,6 +160,7 @@ void AudioReceiver::IncomingParsedRtpPacket(const uint8* payload_data, void AudioReceiver::GetRawAudioFrame(int number_of_10ms_blocks, int desired_frequency, const AudioFrameDecodedCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_decoder_) << "Invalid function call in this configuration"; cast_environment_->PostTask(CastEnvironment::AUDIO_DECODER, FROM_HERE, @@ -191,6 +197,7 @@ void AudioReceiver::DecodeAudioFrameThread( } void AudioReceiver::PlayoutTimeout() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_buffer_) << "Invalid function call in this configuration"; if (queued_encoded_callbacks_.empty()) { // Already released by incoming packet. @@ -218,6 +225,7 @@ void AudioReceiver::PlayoutTimeout() { void AudioReceiver::GetEncodedAudioFrame( const AudioFrameEncodedCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_buffer_) << "Invalid function call in this configuration"; uint32 rtp_timestamp = 0; @@ -244,6 +252,7 @@ bool AudioReceiver::PostEncodedAudioFrame( uint32 rtp_timestamp, bool next_frame, scoped_ptr<EncodedAudioFrame>* encoded_frame) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_buffer_) << "Invalid function call in this configuration"; base::TimeTicks now = cast_environment_->Clock()->NowTicks(); base::TimeTicks playout_time = GetPlayoutTime(now, rtp_timestamp); @@ -270,6 +279,7 @@ bool AudioReceiver::PostEncodedAudioFrame( void AudioReceiver::IncomingPacket(const uint8* packet, size_t length, const base::Closure callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); bool rtcp_packet = Rtcp::IsRtcpPacket(packet, length); if (!rtcp_packet) { rtp_receiver_->ReceivedPacket(packet, length); @@ -285,6 +295,7 @@ void AudioReceiver::CastFeedback(const RtcpCastMessage& cast_message) { base::TimeTicks AudioReceiver::GetPlayoutTime(base::TimeTicks now, uint32 rtp_timestamp) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); // Senders time in ms when this frame was recorded. // Note: the senders clock and our local clock might not be synced. base::TimeTicks rtp_timestamp_in_ticks; @@ -315,6 +326,7 @@ base::TimeTicks AudioReceiver::GetPlayoutTime(base::TimeTicks now, } void AudioReceiver::ScheduleNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta time_to_send = rtcp_->TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks(); @@ -327,6 +339,7 @@ void AudioReceiver::ScheduleNextRtcpReport() { } void AudioReceiver::SendNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_->SendRtcpReport(incoming_ssrc_); ScheduleNextRtcpReport(); } @@ -334,6 +347,7 @@ void AudioReceiver::SendNextRtcpReport() { // Cast messages should be sent within a maximum interval. Schedule a call // if not triggered elsewhere, e.g. by the cast message_builder. void AudioReceiver::ScheduleNextCastMessage() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (audio_buffer_) { base::TimeTicks send_time; audio_buffer_->TimeToSendNextCastMessage(&send_time); @@ -349,6 +363,7 @@ void AudioReceiver::ScheduleNextCastMessage() { } void AudioReceiver::SendNextCastMessage() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_buffer_) << "Invalid function call in this configuration"; audio_buffer_->SendCastMessage(); // Will only send a message if it is time. ScheduleNextCastMessage(); diff --git a/media/cast/audio_receiver/audio_receiver.h b/media/cast/audio_receiver/audio_receiver.h index 2044e16..38cc4e4 100644 --- a/media/cast/audio_receiver/audio_receiver.h +++ b/media/cast/audio_receiver/audio_receiver.h @@ -81,6 +81,8 @@ class AudioReceiver : public base::NonThreadSafe, // Return the playout time based on the current time and rtp timestamp. base::TimeTicks GetPlayoutTime(base::TimeTicks now, uint32 rtp_timestamp); + void InitializeTimers(); + // Schedule the next RTCP report. void ScheduleNextRtcpReport(); diff --git a/media/cast/audio_sender/audio_sender.cc b/media/cast/audio_sender/audio_sender.cc index 671236c..c9d1bff 100644 --- a/media/cast/audio_sender/audio_sender.cc +++ b/media/cast/audio_sender/audio_sender.cc @@ -70,6 +70,7 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment, true, audio_config.sender_ssrc, audio_config.rtcp_c_name), + initialized_(false), weak_factory_(this) { rtcp_.SetRemoteSSRC(audio_config.incoming_feedback_ssrc); @@ -79,14 +80,22 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment, base::Bind(&AudioSender::SendEncodedAudioFrame, weak_factory_.GetWeakPtr())); } - ScheduleNextRtcpReport(); } AudioSender::~AudioSender() {} +void AudioSender::InitializeTimers() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + if (!initialized_) { + initialized_ = true; + ScheduleNextRtcpReport(); + } +} + void AudioSender::InsertAudio(const AudioBus* audio_bus, const base::TimeTicks& recorded_time, const base::Closure& done_callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_encoder_.get()) << "Invalid internal state"; audio_encoder_->InsertAudio(audio_bus, recorded_time, done_callback); } @@ -94,6 +103,7 @@ void AudioSender::InsertAudio(const AudioBus* audio_bus, void AudioSender::InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame, const base::TimeTicks& recorded_time, const base::Closure callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(audio_encoder_.get() == NULL) << "Invalid internal state"; rtp_sender_.IncomingEncodedAudioFrame(audio_frame, recorded_time); callback.Run(); @@ -102,21 +112,26 @@ void AudioSender::InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame, void AudioSender::SendEncodedAudioFrame( scoped_ptr<EncodedAudioFrame> audio_frame, const base::TimeTicks& recorded_time) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + InitializeTimers(); rtp_sender_.IncomingEncodedAudioFrame(audio_frame.get(), recorded_time); } void AudioSender::ResendPackets( const MissingFramesAndPacketsMap& missing_frames_and_packets) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtp_sender_.ResendPackets(missing_frames_and_packets); } void AudioSender::IncomingRtcpPacket(const uint8* packet, size_t length, const base::Closure callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_.IncomingRtcpPacket(packet, length); cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback); } void AudioSender::ScheduleNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta time_to_next = rtcp_.TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks(); @@ -129,6 +144,7 @@ void AudioSender::ScheduleNextRtcpReport() { } void AudioSender::SendRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_.SendRtcpReport(incoming_feedback_ssrc_); ScheduleNextRtcpReport(); } diff --git a/media/cast/audio_sender/audio_sender.h b/media/cast/audio_sender/audio_sender.h index 8024a98..1091b90 100644 --- a/media/cast/audio_sender/audio_sender.h +++ b/media/cast/audio_sender/audio_sender.h @@ -73,6 +73,8 @@ class AudioSender : public base::NonThreadSafe, void ScheduleNextRtcpReport(); void SendRtcpReport(); + void InitializeTimers(); + base::WeakPtrFactory<AudioSender> weak_factory_; const uint32 incoming_feedback_ssrc_; @@ -82,6 +84,7 @@ class AudioSender : public base::NonThreadSafe, scoped_ptr<LocalRtpSenderStatistics> rtp_audio_sender_statistics_; scoped_ptr<LocalRtcpAudioSenderFeedback> rtcp_feedback_; Rtcp rtcp_; + bool initialized_; DISALLOW_COPY_AND_ASSIGN(AudioSender); }; diff --git a/media/cast/audio_sender/audio_sender_unittest.cc b/media/cast/audio_sender/audio_sender_unittest.cc index 4461b9b..92f51a3 100644 --- a/media/cast/audio_sender/audio_sender_unittest.cc +++ b/media/cast/audio_sender/audio_sender_unittest.cc @@ -75,8 +75,21 @@ TEST_F(AudioSenderTest, Encode20ms) { } TEST_F(AudioSenderTest, RtcpTimer) { + EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1)); EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).Times(1); + const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(20); + scoped_ptr<AudioBus> bus(TestAudioBusFactory( + audio_config_.channels, audio_config_.frequency, + TestAudioBusFactory::kMiddleANoteFreq, 0.5f).NextAudioBus(kDuration)); + + base::TimeTicks recorded_time = base::TimeTicks::Now(); + audio_sender_->InsertAudio( + bus.get(), recorded_time, + base::Bind(base::IgnoreResult(&scoped_ptr<AudioBus>::release), + base::Unretained(&bus))); + task_runner_->RunTasks(); + // Make sure that we send at least one RTCP packet. base::TimeDelta max_rtcp_timeout = base::TimeDelta::FromMilliseconds(1 + kDefaultRtcpIntervalMs * 3 / 2); diff --git a/media/cast/cast_receiver_impl.cc b/media/cast/cast_receiver_impl.cc index 2a88cd2..e2c004f 100644 --- a/media/cast/cast_receiver_impl.cc +++ b/media/cast/cast_receiver_impl.cc @@ -18,8 +18,8 @@ namespace cast { class LocalFrameReceiver : public FrameReceiver { public: LocalFrameReceiver(scoped_refptr<CastEnvironment> cast_environment, - base::WeakPtr<AudioReceiver> audio_receiver, - base::WeakPtr<VideoReceiver> video_receiver) + AudioReceiver* audio_receiver, + VideoReceiver* video_receiver) : cast_environment_(cast_environment), audio_receiver_(audio_receiver), video_receiver_(video_receiver) {} @@ -27,15 +27,15 @@ class LocalFrameReceiver : public FrameReceiver { virtual void GetRawVideoFrame( const VideoFrameDecodedCallback& callback) OVERRIDE { cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, - base::Bind(&VideoReceiver::GetRawVideoFrame, video_receiver_, - callback)); + base::Bind(&VideoReceiver::GetRawVideoFrame, + video_receiver_->AsWeakPtr(), callback)); } virtual void GetEncodedVideoFrame( const VideoFrameEncodedCallback& callback) OVERRIDE { cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, - base::Bind(&VideoReceiver::GetEncodedVideoFrame, video_receiver_, - callback)); + base::Bind(&VideoReceiver::GetEncodedVideoFrame, + video_receiver_->AsWeakPtr(), callback)); } virtual void GetRawAudioFrame( @@ -43,14 +43,15 @@ class LocalFrameReceiver : public FrameReceiver { int desired_frequency, const AudioFrameDecodedCallback& callback) OVERRIDE { cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, base::Bind( - &AudioReceiver::GetRawAudioFrame, audio_receiver_, + &AudioReceiver::GetRawAudioFrame, audio_receiver_->AsWeakPtr(), number_of_10ms_blocks, desired_frequency, callback)); } virtual void GetCodedAudioFrame( const AudioFrameEncodedCallback& callback) OVERRIDE { - cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, base::Bind( - &AudioReceiver::GetEncodedAudioFrame, audio_receiver_, callback)); + cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, + base::Bind(&AudioReceiver::GetEncodedAudioFrame, + audio_receiver_->AsWeakPtr(), callback)); } protected: @@ -60,16 +61,16 @@ class LocalFrameReceiver : public FrameReceiver { friend class base::RefCountedThreadSafe<LocalFrameReceiver>; scoped_refptr<CastEnvironment> cast_environment_; - base::WeakPtr<AudioReceiver> audio_receiver_; - base::WeakPtr<VideoReceiver> video_receiver_; + AudioReceiver* audio_receiver_; + VideoReceiver* video_receiver_; }; // The video and audio receivers should only be called from the main thread. class LocalPacketReceiver : public PacketReceiver { public: LocalPacketReceiver(scoped_refptr<CastEnvironment> cast_environment, - base::WeakPtr<AudioReceiver> audio_receiver, - base::WeakPtr<VideoReceiver> video_receiver, + AudioReceiver* audio_receiver, + VideoReceiver* video_receiver, uint32 ssrc_of_audio_sender, uint32 ssrc_of_video_sender) : cast_environment_(cast_environment), @@ -103,12 +104,12 @@ class LocalPacketReceiver : public PacketReceiver { } if (ssrc_of_sender == ssrc_of_audio_sender_) { cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, - base::Bind(&AudioReceiver::IncomingPacket, audio_receiver_, - packet, length, callback)); + base::Bind(&AudioReceiver::IncomingPacket, + audio_receiver_->AsWeakPtr(), packet, length, callback)); } else if (ssrc_of_sender == ssrc_of_video_sender_) { cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, - base::Bind(&VideoReceiver::IncomingPacket, video_receiver_, - packet, length, callback)); + base::Bind(&VideoReceiver::IncomingPacket, + video_receiver_->AsWeakPtr(), packet, length, callback)); } else { // No action; just log and call the callback informing that we are done // with the packet. @@ -126,8 +127,8 @@ class LocalPacketReceiver : public PacketReceiver { friend class base::RefCountedThreadSafe<LocalPacketReceiver>; scoped_refptr<CastEnvironment> cast_environment_; - base::WeakPtr<AudioReceiver> audio_receiver_; - base::WeakPtr<VideoReceiver> video_receiver_; + AudioReceiver* audio_receiver_; + VideoReceiver* video_receiver_; const uint32 ssrc_of_audio_sender_; const uint32 ssrc_of_video_sender_; }; @@ -152,11 +153,11 @@ CastReceiverImpl::CastReceiverImpl( audio_receiver_(cast_environment, audio_config, &pacer_), video_receiver_(cast_environment, video_config, &pacer_), frame_receiver_(new LocalFrameReceiver(cast_environment, - audio_receiver_.AsWeakPtr(), - video_receiver_.AsWeakPtr())), + &audio_receiver_, + &video_receiver_)), packet_receiver_(new LocalPacketReceiver(cast_environment, - audio_receiver_.AsWeakPtr(), - video_receiver_.AsWeakPtr(), + &audio_receiver_, + &video_receiver_, audio_config.incoming_ssrc, video_config.incoming_ssrc)) {} diff --git a/media/cast/video_receiver/video_receiver.cc b/media/cast/video_receiver/video_receiver.cc index 1c9aa82..40bf2b4 100644 --- a/media/cast/video_receiver/video_receiver.cc +++ b/media/cast/video_receiver/video_receiver.cc @@ -25,43 +25,20 @@ static const int64_t kMinProcessIntervalMs = 5; // Used to pass payload data into the video receiver. class LocalRtpVideoData : public RtpData { public: - explicit LocalRtpVideoData(base::TickClock* clock, - VideoReceiver* video_receiver) - : clock_(clock), - video_receiver_(video_receiver), - time_updated_(false), - incoming_rtp_timestamp_(0) { - } + explicit LocalRtpVideoData(VideoReceiver* video_receiver) + : video_receiver_(video_receiver) {} + virtual ~LocalRtpVideoData() {} virtual void OnReceivedPayloadData(const uint8* payload_data, size_t payload_size, const RtpCastHeader* rtp_header) OVERRIDE { - base::TimeTicks now = clock_->NowTicks(); - if (time_incoming_packet_.is_null() || now - time_incoming_packet_ > - base::TimeDelta::FromMilliseconds(kMinTimeBetweenOffsetUpdatesMs)) { - incoming_rtp_timestamp_ = rtp_header->webrtc.header.timestamp; - time_incoming_packet_ = now; - time_updated_ = true; - } - video_receiver_->IncomingRtpPacket(payload_data, payload_size, *rtp_header); - } - - bool GetPacketTimeInformation(base::TimeTicks* time_incoming_packet, - uint32* incoming_rtp_timestamp) { - *time_incoming_packet = time_incoming_packet_; - *incoming_rtp_timestamp = incoming_rtp_timestamp_; - bool time_updated = time_updated_; - time_updated_ = false; - return time_updated; + video_receiver_->IncomingParsedRtpPacket(payload_data, payload_size, + *rtp_header); } private: - base::TickClock* clock_; // Not owned by this class. VideoReceiver* video_receiver_; - bool time_updated_; - base::TimeTicks time_incoming_packet_; - uint32 incoming_rtp_timestamp_; }; // Local implementation of RtpPayloadFeedback (defined in rtp_defines.h) @@ -113,13 +90,14 @@ VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment, base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)), frame_delay_(base::TimeDelta::FromMilliseconds( 1000 / video_config.max_frame_rate)), - incoming_payload_callback_( - new LocalRtpVideoData(cast_environment_->Clock(), this)), + incoming_payload_callback_(new LocalRtpVideoData(this)), incoming_payload_feedback_(new LocalRtpVideoFeedback(this)), rtp_receiver_(cast_environment_->Clock(), NULL, &video_config, incoming_payload_callback_.get()), rtp_video_receiver_statistics_( new LocalRtpReceiverStatistics(&rtp_receiver_)), + time_incoming_packet_updated_(false), + incoming_rtp_timestamp_(0), weak_factory_(this) { int max_unacked_frames = video_config.rtp_max_delay_ms * video_config.max_frame_rate / 1000; @@ -147,14 +125,19 @@ VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment, video_config.rtcp_c_name)); rtcp_->SetRemoteSSRC(video_config.incoming_ssrc); - ScheduleNextRtcpReport(); - ScheduleNextCastMessage(); } VideoReceiver::~VideoReceiver() {} +void VideoReceiver::InitializeTimers() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + ScheduleNextRtcpReport(); + ScheduleNextCastMessage(); +} + void VideoReceiver::GetRawVideoFrame( const VideoFrameDecodedCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); GetEncodedVideoFrame(base::Bind(&VideoReceiver::DecodeVideoFrame, weak_factory_.GetWeakPtr(), callback)); @@ -165,6 +148,7 @@ void VideoReceiver::DecodeVideoFrame( const VideoFrameDecodedCallback& callback, scoped_ptr<EncodedVideoFrame> encoded_frame, const base::TimeTicks& render_time) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); // Hand the ownership of the encoded frame to the decode thread. cast_environment_->PostTask(CastEnvironment::VIDEO_DECODER, FROM_HERE, base::Bind(&VideoReceiver::DecodeVideoFrameThread, @@ -192,6 +176,7 @@ void VideoReceiver::DecodeVideoFrameThread( // Called from the main cast thread. void VideoReceiver::GetEncodedVideoFrame( const VideoFrameEncodedCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame()); uint32 rtp_timestamp = 0; bool next_frame = false; @@ -222,6 +207,7 @@ void VideoReceiver::GetEncodedVideoFrame( bool VideoReceiver::PullEncodedVideoFrame(uint32 rtp_timestamp, bool next_frame, scoped_ptr<EncodedVideoFrame>* encoded_frame, base::TimeTicks* render_time) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeTicks now = cast_environment_->Clock()->NowTicks(); *render_time = GetRenderTime(now, rtp_timestamp); @@ -264,6 +250,7 @@ bool VideoReceiver::PullEncodedVideoFrame(uint32 rtp_timestamp, } void VideoReceiver::PlayoutTimeout() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (queued_encoded_callbacks_.empty()) return; uint32 rtp_timestamp = 0; @@ -299,36 +286,33 @@ void VideoReceiver::PlayoutTimeout() { base::TimeTicks VideoReceiver::GetRenderTime(base::TimeTicks now, uint32 rtp_timestamp) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); // Senders time in ms when this frame was captured. // Note: the senders clock and our local clock might not be synced. base::TimeTicks rtp_timestamp_in_ticks; - base::TimeTicks time_incoming_packet; - uint32 incoming_rtp_timestamp; if (time_offset_.InMilliseconds() == 0) { - incoming_payload_callback_->GetPacketTimeInformation( - &time_incoming_packet, &incoming_rtp_timestamp); - if (!rtcp_->RtpTimestampInSenderTime(kVideoFrequency, - incoming_rtp_timestamp, + incoming_rtp_timestamp_, &rtp_timestamp_in_ticks)) { // We have not received any RTCP to sync the stream play it out as soon as // possible. return now; } - time_offset_ = time_incoming_packet - rtp_timestamp_in_ticks; - } else if (incoming_payload_callback_->GetPacketTimeInformation( - &time_incoming_packet, &incoming_rtp_timestamp)) { + time_offset_ = time_incoming_packet_ - rtp_timestamp_in_ticks; + } else if (time_incoming_packet_updated_) { if (rtcp_->RtpTimestampInSenderTime(kVideoFrequency, - incoming_rtp_timestamp, + incoming_rtp_timestamp_, &rtp_timestamp_in_ticks)) { // Time to update the time_offset. base::TimeDelta time_offset = - time_incoming_packet - rtp_timestamp_in_ticks; + time_incoming_packet_ - rtp_timestamp_in_ticks; time_offset_ = ((kTimeOffsetFilter - 1) * time_offset_ + time_offset) / kTimeOffsetFilter; } } + // Reset |time_incoming_packet_updated_| to enable a future measurement. + time_incoming_packet_updated_ = false; if (!rtcp_->RtpTimestampInSenderTime(kVideoFrequency, rtp_timestamp, &rtp_timestamp_in_ticks)) { @@ -340,6 +324,7 @@ base::TimeTicks VideoReceiver::GetRenderTime(base::TimeTicks now, void VideoReceiver::IncomingPacket(const uint8* packet, size_t length, const base::Closure callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (Rtcp::IsRtcpPacket(packet, length)) { rtcp_->IncomingRtcpPacket(packet, length); } else { @@ -348,9 +333,20 @@ void VideoReceiver::IncomingPacket(const uint8* packet, size_t length, cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback); } -void VideoReceiver::IncomingRtpPacket(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header) { +void VideoReceiver::IncomingParsedRtpPacket(const uint8* payload_data, + size_t payload_size, + const RtpCastHeader& rtp_header) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + + base::TimeTicks now = cast_environment_->Clock()->NowTicks(); + if (time_incoming_packet_.is_null() || now - time_incoming_packet_ > + base::TimeDelta::FromMilliseconds(kMinTimeBetweenOffsetUpdatesMs)) { + if (time_incoming_packet_.is_null()) InitializeTimers(); + incoming_rtp_timestamp_ = rtp_header.webrtc.header.timestamp; + time_incoming_packet_ = now; + time_incoming_packet_updated_ = true; + } + bool complete = framer_->InsertPacket(payload_data, payload_size, rtp_header); if (!complete) return; // Video frame not complete; wait for more packets. @@ -366,6 +362,7 @@ void VideoReceiver::IncomingRtpPacket(const uint8* payload_data, // Send a cast feedback message. Actual message created in the framer (cast // message builder). void VideoReceiver::CastFeedback(const RtcpCastMessage& cast_message) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_->SendRtcpCast(cast_message); time_last_sent_cast_message_= cast_environment_->Clock()->NowTicks(); } @@ -373,6 +370,7 @@ void VideoReceiver::CastFeedback(const RtcpCastMessage& cast_message) { // Cast messages should be sent within a maximum interval. Schedule a call // if not triggered elsewhere, e.g. by the cast message_builder. void VideoReceiver::ScheduleNextCastMessage() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeTicks send_time; framer_->TimeToSendNextCastMessage(&send_time); @@ -386,12 +384,14 @@ void VideoReceiver::ScheduleNextCastMessage() { } void VideoReceiver::SendNextCastMessage() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); framer_->SendCastMessage(); // Will only send a message if it is time. ScheduleNextCastMessage(); } // Schedule the next RTCP report to be sent back to the sender. void VideoReceiver::ScheduleNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta time_to_next = rtcp_->TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks(); @@ -404,6 +404,7 @@ void VideoReceiver::ScheduleNextRtcpReport() { } void VideoReceiver::SendNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_->SendRtcpReport(incoming_ssrc_); ScheduleNextRtcpReport(); } diff --git a/media/cast/video_receiver/video_receiver.h b/media/cast/video_receiver/video_receiver.h index d691508..fbc77ad 100644 --- a/media/cast/video_receiver/video_receiver.h +++ b/media/cast/video_receiver/video_receiver.h @@ -32,7 +32,6 @@ class Rtcp; class RtpReceiverStatistics; class VideoDecoder; - // Should only be called from the Main cast thread. class VideoReceiver : public base::NonThreadSafe, public base::SupportsWeakPtr<VideoReceiver> { @@ -54,9 +53,9 @@ class VideoReceiver : public base::NonThreadSafe, const base::Closure callback); protected: - void IncomingRtpPacket(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header); + void IncomingParsedRtpPacket(const uint8* payload_data, + size_t payload_size, + const RtpCastHeader& rtp_header); void DecodeVideoFrameThread( scoped_ptr<EncodedVideoFrame> encoded_frame, @@ -83,6 +82,8 @@ class VideoReceiver : public base::NonThreadSafe, // Returns Render time based on current time and the rtp timestamp. base::TimeTicks GetRenderTime(base::TimeTicks now, uint32 rtp_timestamp); + void InitializeTimers(); + // Schedule timing for the next cast message. void ScheduleNextCastMessage(); @@ -108,11 +109,14 @@ class VideoReceiver : public base::NonThreadSafe, scoped_ptr<Rtcp> rtcp_; scoped_ptr<RtpReceiverStatistics> rtp_video_receiver_statistics_; base::TimeTicks time_last_sent_cast_message_; - // Sender-receiver offset estimation. - base::TimeDelta time_offset_; + base::TimeDelta time_offset_; // Sender-receiver offset estimation. std::list<VideoFrameEncodedCallback> queued_encoded_callbacks_; + bool time_incoming_packet_updated_; + base::TimeTicks time_incoming_packet_; + uint32 incoming_rtp_timestamp_; + base::WeakPtrFactory<VideoReceiver> weak_factory_; DISALLOW_COPY_AND_ASSIGN(VideoReceiver); diff --git a/media/cast/video_receiver/video_receiver_unittest.cc b/media/cast/video_receiver/video_receiver_unittest.cc index 0a8df04..29efa5d 100644 --- a/media/cast/video_receiver/video_receiver_unittest.cc +++ b/media/cast/video_receiver/video_receiver_unittest.cc @@ -61,7 +61,7 @@ class PeerVideoReceiver : public VideoReceiver { PacedPacketSender* const packet_sender) : VideoReceiver(cast_environment, video_config, packet_sender) { } - using VideoReceiver::IncomingRtpPacket; + using VideoReceiver::IncomingParsedRtpPacket; }; @@ -110,7 +110,8 @@ class VideoReceiverTest : public ::testing::Test { TEST_F(VideoReceiverTest, GetOnePacketEncodedframe) { EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).WillRepeatedly( testing::Return(true)); - receiver_->IncomingRtpPacket(payload_.data(), payload_.size(), rtp_header_); + receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(), + rtp_header_); VideoFrameEncodedCallback frame_to_decode_callback = base::Bind(&TestVideoReceiverCallback::FrameToDecode, @@ -125,12 +126,15 @@ TEST_F(VideoReceiverTest, MultiplePackets) { EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).WillRepeatedly( testing::Return(true)); rtp_header_.max_packet_id = 2; - receiver_->IncomingRtpPacket(payload_.data(), payload_.size(), rtp_header_); + receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(), + rtp_header_); ++rtp_header_.packet_id; ++rtp_header_.webrtc.header.sequenceNumber; - receiver_->IncomingRtpPacket(payload_.data(), payload_.size(), rtp_header_); + receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(), + rtp_header_); ++rtp_header_.packet_id; - receiver_->IncomingRtpPacket(payload_.data(), payload_.size(), rtp_header_); + receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(), + rtp_header_); VideoFrameEncodedCallback frame_to_decode_callback = base::Bind(&TestVideoReceiverCallback::FrameToDecode, @@ -145,7 +149,8 @@ TEST_F(VideoReceiverTest, MultiplePackets) { TEST_F(VideoReceiverTest, GetOnePacketRawframe) { EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).WillRepeatedly( testing::Return(true)); - receiver_->IncomingRtpPacket(payload_.data(), payload_.size(), rtp_header_); + receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(), + rtp_header_); // Decode error - requires legal input. VideoFrameDecodedCallback frame_decoded_callback = base::Bind(&TestVideoReceiverCallback::DecodeComplete, diff --git a/media/cast/video_sender/video_encoder.cc b/media/cast/video_sender/video_encoder.cc index bc2b971..a4f350a 100644 --- a/media/cast/video_sender/video_encoder.cc +++ b/media/cast/video_sender/video_encoder.cc @@ -35,6 +35,7 @@ bool VideoEncoder::EncodeVideoFrame( const base::TimeTicks& capture_time, const FrameEncodedCallback& frame_encoded_callback, const base::Closure frame_release_callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (video_config_.codec != kVp8) return false; if (skip_next_frame_) { diff --git a/media/cast/video_sender/video_sender.cc b/media/cast/video_sender/video_sender.cc index d706f02..35a227d 100644 --- a/media/cast/video_sender/video_sender.cc +++ b/media/cast/video_sender/video_sender.cc @@ -63,7 +63,6 @@ VideoSender::VideoSender( paced_packet_sender)), last_acked_frame_id_(-1), last_sent_frame_id_(-1), - last_sent_key_frame_id_(-1), duplicate_ack_(0), last_skip_count_(0), congestion_control_(cast_environment->Clock(), @@ -71,6 +70,7 @@ VideoSender::VideoSender( video_config.max_bitrate, video_config.min_bitrate, video_config.start_bitrate), + initialized_(false), weak_factory_(this) { max_unacked_frames_ = static_cast<uint8>(video_config.rtp_max_delay_ms * video_config.max_frame_rate / 1000) + 1; @@ -101,17 +101,25 @@ VideoSender::VideoSender( video_config.rtcp_c_name)); rtcp_->SetRemoteSSRC(video_config.incoming_feedback_ssrc); - ScheduleNextRtcpReport(); - ScheduleNextResendCheck(); - ScheduleNextSkippedFramesCheck(); } VideoSender::~VideoSender() {} +void VideoSender::InitializeTimers() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + if (!initialized_) { + initialized_ = true; + ScheduleNextRtcpReport(); + ScheduleNextResendCheck(); + ScheduleNextSkippedFramesCheck(); + } +} + void VideoSender::InsertRawVideoFrame( const I420VideoFrame* video_frame, const base::TimeTicks& capture_time, const base::Closure callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(video_encoder_.get()) << "Invalid state"; if (!video_encoder_->EncodeVideoFrame(video_frame, capture_time, @@ -126,6 +134,7 @@ void VideoSender::InsertCodedVideoFrame(const EncodedVideoFrame* encoded_frame, const base::Closure callback) { DCHECK(!video_encoder_.get()) << "Invalid state"; DCHECK(encoded_frame) << "Invalid argument"; + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); SendEncodedVideoFrame(encoded_frame, capture_time); cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback); @@ -139,24 +148,27 @@ void VideoSender::SendEncodedVideoFrameMainThread( void VideoSender::SendEncodedVideoFrame(const EncodedVideoFrame* encoded_frame, const base::TimeTicks& capture_time) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); last_send_time_ = cast_environment_->Clock()->NowTicks(); rtp_sender_->IncomingEncodedVideoFrame(encoded_frame, capture_time); if (encoded_frame->key_frame) { VLOG(1) << "Send encoded key frame; frame_id:" << static_cast<int>(encoded_frame->frame_id); - last_sent_key_frame_id_ = encoded_frame->frame_id; } - last_sent_frame_id_ = encoded_frame->frame_id; + last_sent_frame_id_ = static_cast<int>(encoded_frame->frame_id); UpdateFramesInFlight(); + InitializeTimers(); } void VideoSender::IncomingRtcpPacket(const uint8* packet, size_t length, const base::Closure callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_->IncomingRtcpPacket(packet, length); cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback); } void VideoSender::ScheduleNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta time_to_next = rtcp_->TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks(); @@ -169,11 +181,13 @@ void VideoSender::ScheduleNextRtcpReport() { } void VideoSender::SendRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); rtcp_->SendRtcpReport(incoming_feedback_ssrc_); ScheduleNextRtcpReport(); } void VideoSender::ScheduleNextResendCheck() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta time_to_next; if (last_send_time_.is_null()) { time_to_next = rtp_max_delay_; @@ -190,6 +204,7 @@ void VideoSender::ScheduleNextResendCheck() { } void VideoSender::ResendCheck() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (!last_send_time_.is_null() && last_sent_frame_id_ != -1) { base::TimeDelta time_since_last_send = cast_environment_->Clock()->NowTicks() - last_send_time_; @@ -201,7 +216,6 @@ void VideoSender::ResendCheck() { last_sent_frame_id_ = -1; UpdateFramesInFlight(); } else { - DCHECK_GE(255, last_acked_frame_id_); DCHECK_LE(0, last_acked_frame_id_); uint32 frame_id = static_cast<uint32>(last_acked_frame_id_ + 1); @@ -214,6 +228,7 @@ void VideoSender::ResendCheck() { } void VideoSender::ScheduleNextSkippedFramesCheck() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta time_to_next; if (last_checked_skip_count_time_.is_null()) { time_to_next = @@ -232,6 +247,7 @@ void VideoSender::ScheduleNextSkippedFramesCheck() { } void VideoSender::SkippedFramesCheck() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); int skip_count = video_encoder_controller_->NumberOfSkippedFrames(); if (skip_count - last_skip_count_ > kSkippedFramesThreshold * max_frame_rate_) { @@ -243,6 +259,7 @@ void VideoSender::SkippedFramesCheck() { } void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); base::TimeDelta rtt; base::TimeDelta avg_rtt; base::TimeDelta min_rtt; @@ -270,9 +287,9 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { video_encoder_controller_->SetBitRate(new_bitrate); } } - if (last_acked_frame_id_ == cast_feedback.ack_frame_id_ && + if (static_cast<uint32>(last_acked_frame_id_) == cast_feedback.ack_frame_id_ // We only count duplicate ACKs when we have sent newer frames. - IsNewerFrameId(last_sent_frame_id_, last_acked_frame_id_)) { + && IsNewerFrameId(last_sent_frame_id_, last_acked_frame_id_)) { duplicate_ack_++; } else { duplicate_ack_ = 0; @@ -282,7 +299,6 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { resend_frame = static_cast<uint32>(last_acked_frame_id_ + 1); } if (resend_frame != -1) { - DCHECK_GE(255, resend_frame); DCHECK_LE(0, resend_frame); VLOG(1) << "Received duplicate ACK for frame:" << static_cast<int>(resend_frame); @@ -301,23 +317,23 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { } void VideoSender::ReceivedAck(uint32 acked_frame_id) { - VLOG(1) << "ReceivedAck:" << static_cast<int>(acked_frame_id); - last_acked_frame_id_ = acked_frame_id; + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + VLOG(1) << "ReceivedAck:" << acked_frame_id; + last_acked_frame_id_ = static_cast<int>(acked_frame_id); UpdateFramesInFlight(); } void VideoSender::UpdateFramesInFlight() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); if (last_sent_frame_id_ != -1) { - DCHECK_GE(255, last_sent_frame_id_); DCHECK_LE(0, last_sent_frame_id_); uint32 frames_in_flight; if (last_acked_frame_id_ != -1) { - DCHECK_GE(255, last_acked_frame_id_); DCHECK_LE(0, last_acked_frame_id_); frames_in_flight = static_cast<uint32>(last_sent_frame_id_) - static_cast<uint32>(last_acked_frame_id_); } else { - frames_in_flight = last_sent_frame_id_ + 1; + frames_in_flight = static_cast<uint32>(last_sent_frame_id_) + 1; } VLOG(1) << "Frames in flight; last sent: " << last_sent_frame_id_ << " last acked:" << last_acked_frame_id_; @@ -330,6 +346,7 @@ void VideoSender::UpdateFramesInFlight() { } void VideoSender::ResendFrame(uint32 resend_frame_id) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); MissingFramesAndPacketsMap missing_frames_and_packets; PacketIdSet missing; missing_frames_and_packets.insert(std::make_pair(resend_frame_id, missing)); diff --git a/media/cast/video_sender/video_sender.h b/media/cast/video_sender/video_sender.h index ea89b3c..24fba64 100644 --- a/media/cast/video_sender/video_sender.h +++ b/media/cast/video_sender/video_sender.h @@ -100,6 +100,8 @@ class VideoSender : public base::NonThreadSafe, scoped_ptr<EncodedVideoFrame> video_frame, const base::TimeTicks& capture_time); + void InitializeTimers(); + const uint32 incoming_feedback_ssrc_; const base::TimeDelta rtp_max_delay_; const int max_frame_rate_; @@ -114,13 +116,13 @@ class VideoSender : public base::NonThreadSafe, uint8 max_unacked_frames_; int last_acked_frame_id_; int last_sent_frame_id_; - int last_sent_key_frame_id_; int duplicate_ack_; base::TimeTicks last_send_time_; base::TimeTicks last_checked_skip_count_time_; int last_skip_count_; CongestionControl congestion_control_; + bool initialized_; base::WeakPtrFactory<VideoSender> weak_factory_; DISALLOW_COPY_AND_ASSIGN(VideoSender); diff --git a/media/cast/video_sender/video_sender_unittest.cc b/media/cast/video_sender/video_sender_unittest.cc index dd232a2..3269c85 100644 --- a/media/cast/video_sender/video_sender_unittest.cc +++ b/media/cast/video_sender/video_sender_unittest.cc @@ -23,6 +23,7 @@ static const int64 kStartMillisecond = GG_INT64_C(12345678900000); static const uint8 kPixelValue = 123; using testing::_; +using testing::AtLeast; namespace { class PeerVideoSender : public VideoSender { @@ -156,8 +157,23 @@ TEST_F(VideoSenderTest, ExternalEncoder) { } TEST_F(VideoSenderTest, RtcpTimer) { + EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1)); EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).Times(1); - InitEncoder(false); + EXPECT_CALL(mock_video_encoder_controller_, + SkipNextFrame(false)).Times(AtLeast(1)); + InitEncoder(true); + + EncodedVideoFrame video_frame; + base::TimeTicks capture_time; + + video_frame.codec = kVp8; + video_frame.key_frame = true; + video_frame.frame_id = 0; + video_frame.last_referenced_frame_id = 0; + video_frame.data.insert(video_frame.data.begin(), 1000, kPixelValue); + + video_sender_->InsertCodedVideoFrame(&video_frame, capture_time, + base::Bind(&ReleaseEncodedFrame, &video_frame)); // Make sure that we send at least one RTCP packet. base::TimeDelta max_rtcp_timeout = |