diff options
author | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-03 10:45:13 +0000 |
---|---|---|
committer | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-03 10:45:13 +0000 |
commit | a225993ef8292433068e9a3ec7d827fe3d69da8e (patch) | |
tree | fee085e128838947d2d71c8203a47d5a081c0f14 | |
parent | b5ced6e30cdee5631c76c0ba60ba5b0674ba0e5d (diff) | |
download | chromium_src-a225993ef8292433068e9a3ec7d827fe3d69da8e.zip chromium_src-a225993ef8292433068e9a3ec7d827fe3d69da8e.tar.gz chromium_src-a225993ef8292433068e9a3ec7d827fe3d69da8e.tar.bz2 |
[Cast] Clean-up: Merge RtpReceiver+AudioReceiver+VideoReceiver-->FrameReceiver.
Creates a new media/cast/receiver directory, and consolidates all code
in media/cast/audio_receiver and media/cast/video_receiver, and some
modules from media/cast/rtp_receiver into one place.
Removed FrameReceiver proxy interface in cast_receiver.h, since there
are no special threading/lifecycle concerns. Clients will directly call
methods in the CastReceiver interface to request frames on the MAIN
thread.
BUG=378568
Review URL: https://codereview.chromium.org/308043006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274464 0039d316-1c4b-4281-b951-d872f2087c98
31 files changed, 1051 insertions, 1780 deletions
diff --git a/media/cast/audio_receiver/audio_receiver.h b/media/cast/audio_receiver/audio_receiver.h deleted file mode 100644 index 87c5147..0000000 --- a/media/cast/audio_receiver/audio_receiver.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_CAST_AUDIO_RECEIVER_AUDIO_RECEIVER_H_ -#define MEDIA_CAST_AUDIO_RECEIVER_AUDIO_RECEIVER_H_ - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/threading/non_thread_safe.h" -#include "base/time/tick_clock.h" -#include "base/time/time.h" -#include "media/cast/base/clock_drift_smoother.h" -#include "media/cast/cast_config.h" -#include "media/cast/cast_environment.h" -#include "media/cast/cast_receiver.h" -#include "media/cast/framer/framer.h" -#include "media/cast/rtcp/receiver_rtcp_event_subscriber.h" -#include "media/cast/rtcp/rtcp.h" -#include "media/cast/rtp_receiver/rtp_receiver.h" -#include "media/cast/rtp_receiver/rtp_receiver_defines.h" -#include "media/cast/transport/utility/transport_encryption_handler.h" - -namespace media { -namespace cast { - -class AudioDecoder; - -// AudioReceiver receives packets out-of-order while clients make requests for -// complete frames in-order. (A frame consists of one or more packets.) -// -// AudioReceiver also includes logic for computing the playout time for each -// frame, accounting for a constant targeted playout delay. The purpose of the -// playout delay is to provide a fixed window of time between the capture event -// on the sender and the playout on the receiver. This is important because -// each step of the pipeline (i.e., encode frame, then transmit/retransmit from -// the sender, then receive and re-order packets on the receiver, then decode -// frame) can vary in duration and is typically very hard to predict. -// -// Two types of frames can be requested: 1) A frame of decoded audio data; or 2) -// a frame of still-encoded audio data, to be passed into an external audio -// decoder. Each request for a frame includes a callback which AudioReceiver -// guarantees will be called at some point in the future unless the -// AudioReceiver is destroyed. Clients should generally limit the number of -// outstanding requests (perhaps to just one or two). -// -// This class is not thread safe. Should only be called from the Main cast -// thread. -class AudioReceiver : public RtpReceiver, - public RtpPayloadFeedback, - public base::NonThreadSafe, - public base::SupportsWeakPtr<AudioReceiver> { - public: - AudioReceiver(scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& audio_config, - transport::PacedPacketSender* const packet_sender); - - virtual ~AudioReceiver(); - - // Request a decoded audio frame. The audio signal data returned in the - // callback will have the sampling rate and number of channels as requested in - // the configuration that was passed to the ctor. - // - // The given |callback| is guaranteed to be run at some point in the future, - // even if to respond with NULL at shutdown time. - void GetRawAudioFrame(const AudioFrameDecodedCallback& callback); - - // Request an encoded audio frame. - // - // The given |callback| is guaranteed to be run at some point in the future, - // even if to respond with NULL at shutdown time. - void GetEncodedAudioFrame(const FrameEncodedCallback& callback); - - // Deliver another packet, possibly a duplicate, and possibly out-of-order. - void IncomingPacket(scoped_ptr<Packet> packet); - - protected: - friend class AudioReceiverTest; // Invokes OnReceivedPayloadData(). - - virtual void OnReceivedPayloadData(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header) OVERRIDE; - - // RtpPayloadFeedback implementation. - virtual void CastFeedback(const RtcpCastMessage& cast_message) OVERRIDE; - - private: - // Processes ready-to-consume packets from |framer_|, decrypting each packet's - // payload data, and then running the enqueued callbacks in order (one for - // each packet). This method may post a delayed task to re-invoke itself in - // the future to wait for missing/incomplete frames. - void EmitAvailableEncodedFrames(); - - // Clears the |is_waiting_for_consecutive_frame_| flag and invokes - // EmitAvailableEncodedFrames(). - void EmitAvailableEncodedFramesAfterWaiting(); - - // Feeds an EncodedFrame into |audio_decoder_|. GetRawAudioFrame() uses this - // as a callback for GetEncodedAudioFrame(). - void DecodeEncodedAudioFrame( - const AudioFrameDecodedCallback& callback, - scoped_ptr<transport::EncodedFrame> encoded_frame); - - // Computes the playout time for a frame with the given |rtp_timestamp|. - // Because lip-sync info is refreshed regularly, calling this method with the - // same argument may return different results. - base::TimeTicks GetPlayoutTime(uint32 rtp_timestamp) const; - - // Schedule the next RTCP report. - void ScheduleNextRtcpReport(); - - // Actually send the next RTCP report. - void SendNextRtcpReport(); - - // Schedule timing for the next cast message. - void ScheduleNextCastMessage(); - - // Actually send the next cast message. - void SendNextCastMessage(); - - // Receives an AudioBus from |audio_decoder_|, logs the event, and passes the - // data on by running the given |callback|. This method is static to ensure - // it can be called after an AudioReceiver instance is destroyed. - // DecodeEncodedAudioFrame() uses this as a callback for - // AudioDecoder::DecodeFrame(). - static void EmitRawAudioFrame( - const scoped_refptr<CastEnvironment>& cast_environment, - const AudioFrameDecodedCallback& callback, - uint32 frame_id, - uint32 rtp_timestamp, - const base::TimeTicks& playout_time, - scoped_ptr<AudioBus> audio_bus, - bool is_continuous); - - const scoped_refptr<CastEnvironment> cast_environment_; - - // Subscribes to raw events. - // Processes raw audio events to be sent over to the cast sender via RTCP. - ReceiverRtcpEventSubscriber event_subscriber_; - - // Configured audio codec. - const transport::AudioCodec codec_; - - // RTP timebase: The number of RTP units advanced per one second. For audio, - // this is the sampling rate. - const int frequency_; - - // The total amount of time between a frame's capture/recording on the sender - // and its playback on the receiver (i.e., shown to a user). This is fixed as - // a value large enough to give the system sufficient time to encode, - // transmit/retransmit, receive, decode, and render; given its run-time - // environment (sender/receiver hardware performance, network conditions, - // etc.). - const base::TimeDelta target_playout_delay_; - - // Hack: This is used in logic that determines whether to skip frames. - const base::TimeDelta expected_frame_duration_; - - // Set to false initially, then set to true after scheduling the periodic - // sending of reports back to the sender. Reports are first scheduled just - // after receiving a first packet (since the first packet identifies the - // sender for the remainder of the session). - bool reports_are_scheduled_; - - // Assembles packets into frames, providing this receiver with complete, - // decodable EncodedFrames. - Framer framer_; - - // Decodes frames into raw audio for playback. - scoped_ptr<AudioDecoder> audio_decoder_; - - // Manages sending/receiving of RTCP packets, including sender/receiver - // reports. - Rtcp rtcp_; - - // Decrypts encrypted frames. - transport::TransportEncryptionHandler decryptor_; - - // Outstanding callbacks to run to deliver on client requests for frames. - std::list<FrameEncodedCallback> frame_request_queue_; - - // True while there's an outstanding task to re-invoke - // EmitAvailableEncodedFrames(). - bool is_waiting_for_consecutive_frame_; - - // This mapping allows us to log AUDIO_ACK_SENT as a frame event. In addition - // it allows the event to be transmitted via RTCP. - RtpTimestamp frame_id_to_rtp_timestamp_[256]; - - // Lip-sync values used to compute the playout time of each frame from its RTP - // timestamp. These are updated each time the first packet of a frame is - // received. - RtpTimestamp lip_sync_rtp_timestamp_; - base::TimeTicks lip_sync_reference_time_; - ClockDriftSmoother lip_sync_drift_; - - // NOTE: Weak pointers must be invalidated before all other member variables. - base::WeakPtrFactory<AudioReceiver> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(AudioReceiver); -}; - -} // namespace cast -} // namespace media - -#endif // MEDIA_CAST_AUDIO_RECEIVER_AUDIO_RECEIVER_H_ diff --git a/media/cast/audio_receiver/audio_receiver_unittest.cc b/media/cast/audio_receiver/audio_receiver_unittest.cc deleted file mode 100644 index e53c1b9..0000000 --- a/media/cast/audio_receiver/audio_receiver_unittest.cc +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <deque> -#include <utility> - -#include "base/bind.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/test/simple_test_tick_clock.h" -#include "media/cast/audio_receiver/audio_receiver.h" -#include "media/cast/cast_defines.h" -#include "media/cast/cast_environment.h" -#include "media/cast/logging/simple_event_subscriber.h" -#include "media/cast/rtcp/test_rtcp_packet_builder.h" -#include "media/cast/test/fake_single_thread_task_runner.h" -#include "media/cast/test/utility/default_config.h" -#include "media/cast/transport/pacing/mock_paced_packet_sender.h" -#include "testing/gmock/include/gmock/gmock.h" - -using ::testing::_; - -namespace media { -namespace cast { - -namespace { - -const uint32 kFirstFrameId = 1234; -const int kPlayoutDelayMillis = 300; - -class FakeAudioClient { - public: - FakeAudioClient() : num_called_(0) {} - virtual ~FakeAudioClient() {} - - void AddExpectedResult(uint32 expected_frame_id, - const base::TimeTicks& expected_playout_time) { - expected_results_.push_back( - std::make_pair(expected_frame_id, expected_playout_time)); - } - - void DeliverEncodedAudioFrame( - scoped_ptr<transport::EncodedFrame> audio_frame) { - SCOPED_TRACE(::testing::Message() << "num_called_ is " << num_called_); - ASSERT_FALSE(!audio_frame) - << "If at shutdown: There were unsatisfied requests enqueued."; - ASSERT_FALSE(expected_results_.empty()); - EXPECT_EQ(expected_results_.front().first, audio_frame->frame_id); - EXPECT_EQ(expected_results_.front().second, audio_frame->reference_time); - expected_results_.pop_front(); - num_called_++; - } - - int number_times_called() const { return num_called_; } - - private: - std::deque<std::pair<uint32, base::TimeTicks> > expected_results_; - int num_called_; - - DISALLOW_COPY_AND_ASSIGN(FakeAudioClient); -}; - -} // namespace - -class AudioReceiverTest : public ::testing::Test { - protected: - AudioReceiverTest() { - // Configure the audio receiver to use PCM16. - audio_config_ = GetDefaultAudioReceiverConfig(); - audio_config_.rtp_max_delay_ms = kPlayoutDelayMillis; - audio_config_.frequency = 16000; - audio_config_.channels = 1; - audio_config_.codec.audio = transport::kPcm16; - testing_clock_ = new base::SimpleTestTickClock(); - testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks()); - start_time_ = testing_clock_->NowTicks(); - task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_); - - cast_environment_ = new CastEnvironment( - scoped_ptr<base::TickClock>(testing_clock_).Pass(), - task_runner_, - task_runner_, - task_runner_); - - receiver_.reset(new AudioReceiver(cast_environment_, audio_config_, - &mock_transport_)); - } - - virtual ~AudioReceiverTest() {} - - virtual void SetUp() { - payload_.assign(kMaxIpPacketSize, 0); - rtp_header_.is_key_frame = true; - rtp_header_.frame_id = kFirstFrameId; - rtp_header_.packet_id = 0; - rtp_header_.max_packet_id = 0; - rtp_header_.reference_frame_id = rtp_header_.frame_id; - rtp_header_.rtp_timestamp = 0; - } - - void FeedOneFrameIntoReceiver() { - receiver_->OnReceivedPayloadData( - payload_.data(), payload_.size(), rtp_header_); - } - - void FeedLipSyncInfoIntoReceiver() { - const base::TimeTicks now = testing_clock_->NowTicks(); - const int64 rtp_timestamp = (now - start_time_) * - audio_config_.frequency / base::TimeDelta::FromSeconds(1); - CHECK_LE(0, rtp_timestamp); - uint32 ntp_seconds; - uint32 ntp_fraction; - ConvertTimeTicksToNtp(now, &ntp_seconds, &ntp_fraction); - TestRtcpPacketBuilder rtcp_packet; - rtcp_packet.AddSrWithNtp(audio_config_.incoming_ssrc, - ntp_seconds, ntp_fraction, - static_cast<uint32>(rtp_timestamp)); - receiver_->IncomingPacket(rtcp_packet.GetPacket().Pass()); - } - - FrameReceiverConfig audio_config_; - std::vector<uint8> payload_; - RtpCastHeader rtp_header_; - base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment. - base::TimeTicks start_time_; - transport::MockPacedPacketSender mock_transport_; - scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_; - scoped_refptr<CastEnvironment> cast_environment_; - FakeAudioClient fake_audio_client_; - - // Important for the AudioReceiver to be declared last, since its dependencies - // must remain alive until after its destruction. - scoped_ptr<AudioReceiver> receiver_; -}; - -TEST_F(AudioReceiverTest, ReceivesOneFrame) { - SimpleEventSubscriber event_subscriber; - cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); - - EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) - .WillRepeatedly(testing::Return(true)); - - FeedLipSyncInfoIntoReceiver(); - task_runner_->RunTasks(); - - // Enqueue a request for an audio frame. - receiver_->GetEncodedAudioFrame( - base::Bind(&FakeAudioClient::DeliverEncodedAudioFrame, - base::Unretained(&fake_audio_client_))); - - // The request should not be satisfied since no packets have been received. - task_runner_->RunTasks(); - EXPECT_EQ(0, fake_audio_client_.number_times_called()); - - // Deliver one audio frame to the receiver and expect to get one frame back. - const base::TimeDelta target_playout_delay = - base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); - fake_audio_client_.AddExpectedResult( - kFirstFrameId, testing_clock_->NowTicks() + target_playout_delay); - FeedOneFrameIntoReceiver(); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_audio_client_.number_times_called()); - - std::vector<FrameEvent> frame_events; - event_subscriber.GetFrameEventsAndReset(&frame_events); - - ASSERT_TRUE(!frame_events.empty()); - EXPECT_EQ(FRAME_ACK_SENT, frame_events.begin()->type); - EXPECT_EQ(AUDIO_EVENT, frame_events.begin()->media_type); - EXPECT_EQ(rtp_header_.frame_id, frame_events.begin()->frame_id); - EXPECT_EQ(rtp_header_.rtp_timestamp, frame_events.begin()->rtp_timestamp); - - cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); -} - -TEST_F(AudioReceiverTest, ReceivesFramesSkippingWhenAppropriate) { - EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) - .WillRepeatedly(testing::Return(true)); - - const uint32 rtp_advance_per_frame = - audio_config_.frequency / audio_config_.max_frame_rate; - const base::TimeDelta time_advance_per_frame = - base::TimeDelta::FromSeconds(1) / audio_config_.max_frame_rate; - - FeedLipSyncInfoIntoReceiver(); - task_runner_->RunTasks(); - const base::TimeTicks first_frame_capture_time = testing_clock_->NowTicks(); - - // Enqueue a request for an audio frame. - const FrameEncodedCallback frame_encoded_callback = - base::Bind(&FakeAudioClient::DeliverEncodedAudioFrame, - base::Unretained(&fake_audio_client_)); - receiver_->GetEncodedAudioFrame(frame_encoded_callback); - task_runner_->RunTasks(); - EXPECT_EQ(0, fake_audio_client_.number_times_called()); - - // Receive one audio frame and expect to see the first request satisfied. - const base::TimeDelta target_playout_delay = - base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); - fake_audio_client_.AddExpectedResult( - kFirstFrameId, first_frame_capture_time + target_playout_delay); - rtp_header_.rtp_timestamp = 0; - FeedOneFrameIntoReceiver(); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_audio_client_.number_times_called()); - - // Enqueue a second request for an audio frame, but it should not be - // fulfilled yet. - receiver_->GetEncodedAudioFrame(frame_encoded_callback); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_audio_client_.number_times_called()); - - // Receive one audio frame out-of-order: Make sure that we are not continuous - // and that the RTP timestamp represents a time in the future. - rtp_header_.is_key_frame = false; - rtp_header_.frame_id = kFirstFrameId + 2; - rtp_header_.reference_frame_id = 0; - rtp_header_.rtp_timestamp += 2 * rtp_advance_per_frame; - fake_audio_client_.AddExpectedResult( - kFirstFrameId + 2, - first_frame_capture_time + 2 * time_advance_per_frame + - target_playout_delay); - FeedOneFrameIntoReceiver(); - - // Frame 2 should not come out at this point in time. - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_audio_client_.number_times_called()); - - // Enqueue a third request for an audio frame. - receiver_->GetEncodedAudioFrame(frame_encoded_callback); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_audio_client_.number_times_called()); - - // Now, advance time forward such that the receiver is convinced it should - // skip Frame 2. Frame 3 is emitted (to satisfy the second request) because a - // decision was made to skip over the no-show Frame 2. - testing_clock_->Advance(2 * time_advance_per_frame + target_playout_delay); - task_runner_->RunTasks(); - EXPECT_EQ(2, fake_audio_client_.number_times_called()); - - // Receive Frame 4 and expect it to fulfill the third request immediately. - rtp_header_.frame_id = kFirstFrameId + 3; - rtp_header_.reference_frame_id = rtp_header_.frame_id - 1; - rtp_header_.rtp_timestamp += rtp_advance_per_frame; - fake_audio_client_.AddExpectedResult( - kFirstFrameId + 3, first_frame_capture_time + 3 * time_advance_per_frame + - target_playout_delay); - FeedOneFrameIntoReceiver(); - task_runner_->RunTasks(); - EXPECT_EQ(3, fake_audio_client_.number_times_called()); - - // Move forward to the playout time of an unreceived Frame 5. Expect no - // additional frames were emitted. - testing_clock_->Advance(3 * time_advance_per_frame); - task_runner_->RunTasks(); - EXPECT_EQ(3, fake_audio_client_.number_times_called()); -} - -} // namespace cast -} // namespace media diff --git a/media/cast/audio_sender/audio_sender.cc b/media/cast/audio_sender/audio_sender.cc index 33623e9..a6e8cfe 100644 --- a/media/cast/audio_sender/audio_sender.cc +++ b/media/cast/audio_sender/audio_sender.cc @@ -33,7 +33,7 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment, audio_config.rtp_config.ssrc, audio_config.incoming_feedback_ssrc, audio_config.rtcp_c_name, - true), + AUDIO_EVENT), num_aggressive_rtcp_reports_sent_(0), cast_initialization_cb_(STATUS_AUDIO_UNINITIALIZED), weak_factory_(this) { diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp index 4f0c2dd..45ff016 100644 --- a/media/cast/cast.gyp +++ b/media/cast/cast.gyp @@ -99,13 +99,7 @@ '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry', ], 'sources': [ - 'audio_receiver/audio_decoder.h', - 'audio_receiver/audio_decoder.cc', - 'audio_receiver/audio_receiver.h', - 'audio_receiver/audio_receiver.cc', 'cast_receiver.h', - 'cast_receiver_impl.cc', - 'cast_receiver_impl.h', 'framer/cast_message_builder.cc', 'framer/cast_message_builder.h', 'framer/frame_buffer.cc', @@ -114,18 +108,20 @@ 'framer/frame_id_map.h', 'framer/framer.cc', 'framer/framer.h', + 'receiver/audio_decoder.cc', + 'receiver/audio_decoder.h', + 'receiver/cast_receiver_impl.cc', + 'receiver/cast_receiver_impl.h', + 'receiver/frame_receiver.cc', + 'receiver/frame_receiver.h', + 'receiver/video_decoder.cc', + 'receiver/video_decoder.h', 'rtp_receiver/receiver_stats.cc', 'rtp_receiver/receiver_stats.h', - 'rtp_receiver/rtp_receiver.cc', - 'rtp_receiver/rtp_receiver.h', 'rtp_receiver/rtp_receiver_defines.cc', 'rtp_receiver/rtp_receiver_defines.h', 'rtp_receiver/rtp_parser/rtp_parser.cc', 'rtp_receiver/rtp_parser/rtp_parser.h', - 'video_receiver/video_decoder.h', - 'video_receiver/video_decoder.cc', - 'video_receiver/video_receiver.h', - 'video_receiver/video_receiver.cc', ], # source }, { diff --git a/media/cast/cast_receiver.h b/media/cast/cast_receiver.h index 2d83dd2..a9d3ede 100644 --- a/media/cast/cast_receiver.h +++ b/media/cast/cast_receiver.h @@ -45,27 +45,8 @@ typedef base::Callback<void(const scoped_refptr<media::VideoFrame>& video_frame, // dropped (i.e., frame_id should be incrementing by one each time). Note: A // NULL pointer can be returned on error. typedef base::Callback<void(scoped_ptr<transport::EncodedFrame>)> - FrameEncodedCallback; + ReceiveEncodedFrameCallback; -// This Class is thread safe. -class FrameReceiver : public base::RefCountedThreadSafe<FrameReceiver> { - public: - virtual void GetRawAudioFrame(const AudioFrameDecodedCallback& callback) = 0; - - virtual void GetCodedAudioFrame(const FrameEncodedCallback& callback) = 0; - - virtual void GetRawVideoFrame(const VideoFrameDecodedCallback& callback) = 0; - - virtual void GetEncodedVideoFrame(const FrameEncodedCallback& callback) = 0; - - protected: - virtual ~FrameReceiver() {} - - private: - friend class base::RefCountedThreadSafe<FrameReceiver>; -}; - -// This Class is thread safe. class CastReceiver { public: static scoped_ptr<CastReceiver> Create( @@ -75,13 +56,28 @@ class CastReceiver { transport::PacketSender* const packet_sender); // All received RTP and RTCP packets for the call should be sent to this - // PacketReceiver. Can be called from any function. + // PacketReceiver. Can be called from any thread. // TODO(hubbe): Replace with: // virtual void ReceivePacket(scoped_ptr<Packet> packet) = 0; virtual transport::PacketReceiverCallback packet_receiver() = 0; - // Polling interface to get audio and video frames from the CastReceiver. - virtual scoped_refptr<FrameReceiver> frame_receiver() = 0; + // Polling interface to get audio and video frames from the CastReceiver. The + // the RequestDecodedXXXXXFrame() methods utilize internal software-based + // decoding, while the RequestEncodedXXXXXFrame() methods provides + // still-encoded frames for use with external/hardware decoders. + // + // In all cases, the given |callback| is guaranteed to be run at some point in + // the future, except for those requests still enqueued at destruction time. + // + // These methods should all be called on the CastEnvironment's MAIN thread. + virtual void RequestDecodedAudioFrame( + const AudioFrameDecodedCallback& callback) = 0; + virtual void RequestEncodedAudioFrame( + const ReceiveEncodedFrameCallback& callback) = 0; + virtual void RequestDecodedVideoFrame( + const VideoFrameDecodedCallback& callback) = 0; + virtual void RequestEncodedVideoFrame( + const ReceiveEncodedFrameCallback& callback) = 0; virtual ~CastReceiver() {} }; diff --git a/media/cast/cast_receiver_impl.cc b/media/cast/cast_receiver_impl.cc deleted file mode 100644 index 661cbbe..0000000 --- a/media/cast/cast_receiver_impl.cc +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/cast/cast_receiver_impl.h" - -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" - -namespace media { -namespace cast { - -// The video and audio receivers should only be called from the main thread. -// LocalFrameReciever posts tasks to the main thread, making the cast interface -// thread safe. -class LocalFrameReceiver : public FrameReceiver { - public: - LocalFrameReceiver(scoped_refptr<CastEnvironment> cast_environment, - AudioReceiver* audio_receiver, - VideoReceiver* video_receiver) - : cast_environment_(cast_environment), - audio_receiver_(audio_receiver), - video_receiver_(video_receiver) {} - - virtual void GetRawVideoFrame(const VideoFrameDecodedCallback& callback) - OVERRIDE { - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&VideoReceiver::GetRawVideoFrame, - video_receiver_->AsWeakPtr(), - callback)); - } - - virtual void GetEncodedVideoFrame(const FrameEncodedCallback& callback) - OVERRIDE { - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&VideoReceiver::GetEncodedVideoFrame, - video_receiver_->AsWeakPtr(), - callback)); - } - - virtual void GetRawAudioFrame(const AudioFrameDecodedCallback& callback) - OVERRIDE { - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&AudioReceiver::GetRawAudioFrame, - audio_receiver_->AsWeakPtr(), - callback)); - } - - virtual void GetCodedAudioFrame(const FrameEncodedCallback& callback) - OVERRIDE { - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&AudioReceiver::GetEncodedAudioFrame, - audio_receiver_->AsWeakPtr(), - callback)); - } - - protected: - virtual ~LocalFrameReceiver() {} - - private: - friend class base::RefCountedThreadSafe<LocalFrameReceiver>; - - scoped_refptr<CastEnvironment> cast_environment_; - AudioReceiver* audio_receiver_; - VideoReceiver* video_receiver_; -}; - -scoped_ptr<CastReceiver> CastReceiver::Create( - scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& audio_config, - const FrameReceiverConfig& video_config, - transport::PacketSender* const packet_sender) { - return scoped_ptr<CastReceiver>(new CastReceiverImpl( - cast_environment, audio_config, video_config, packet_sender)); -} - -CastReceiverImpl::CastReceiverImpl( - scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& audio_config, - const FrameReceiverConfig& video_config, - transport::PacketSender* const packet_sender) - : pacer_(cast_environment->Clock(), - cast_environment->Logging(), - packet_sender, - cast_environment->GetTaskRunner(CastEnvironment::MAIN)), - audio_receiver_(cast_environment, audio_config, &pacer_), - video_receiver_(cast_environment, - video_config, - &pacer_), - frame_receiver_(new LocalFrameReceiver(cast_environment, - &audio_receiver_, - &video_receiver_)), - cast_environment_(cast_environment), - ssrc_of_audio_sender_(audio_config.incoming_ssrc), - ssrc_of_video_sender_(video_config.incoming_ssrc) {} - -CastReceiverImpl::~CastReceiverImpl() {} - -// The video and audio receivers should only be called from the main thread. -void CastReceiverImpl::ReceivedPacket(scoped_ptr<Packet> packet) { - const uint8_t* data = &packet->front(); - size_t length = packet->size(); - if (length < kMinLengthOfRtcp) { - VLOG(1) << "Received a packet which is too short " << length; - return; - } - uint32 ssrc_of_sender; - if (!Rtcp::IsRtcpPacket(data, length)) { - if (length < kMinLengthOfRtp) { - VLOG(1) << "Received a RTP packet which is too short " << length; - return; - } - ssrc_of_sender = RtpReceiver::GetSsrcOfSender(data, length); - } else { - ssrc_of_sender = Rtcp::GetSsrcOfSender(data, length); - } - if (ssrc_of_sender == ssrc_of_audio_sender_) { - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&AudioReceiver::IncomingPacket, - audio_receiver_.AsWeakPtr(), - base::Passed(&packet))); - } else if (ssrc_of_sender == ssrc_of_video_sender_) { - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&VideoReceiver::IncomingPacket, - video_receiver_.AsWeakPtr(), - base::Passed(&packet))); - } else { - VLOG(1) << "Received a packet with a non matching sender SSRC " - << ssrc_of_sender; - } -} - -transport::PacketReceiverCallback CastReceiverImpl::packet_receiver() { - return base::Bind(&CastReceiverImpl::ReceivedPacket, base::Unretained(this)); -} - -scoped_refptr<FrameReceiver> CastReceiverImpl::frame_receiver() { - return frame_receiver_; -} - -} // namespace cast -} // namespace media diff --git a/media/cast/cast_receiver_impl.h b/media/cast/cast_receiver_impl.h deleted file mode 100644 index ae7d50e..0000000 --- a/media/cast/cast_receiver_impl.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_CAST_CAST_RECEIVER_IMPL_H_ -#define MEDIA_CAST_CAST_RECEIVER_IMPL_H_ - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "media/cast/audio_receiver/audio_receiver.h" -#include "media/cast/cast_config.h" -#include "media/cast/cast_environment.h" -#include "media/cast/cast_receiver.h" -#include "media/cast/transport/pacing/paced_sender.h" -#include "media/cast/video_receiver/video_receiver.h" - -namespace media { -namespace cast { -// This calls is a pure owner class that group all required receive objects -// together such as pacer, packet receiver, frame receiver, audio and video -// receivers. -class CastReceiverImpl : public CastReceiver { - public: - CastReceiverImpl(scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& audio_config, - const FrameReceiverConfig& video_config, - transport::PacketSender* const packet_sender); - - virtual ~CastReceiverImpl(); - - // All received RTP and RTCP packets for the call should be sent to this - // PacketReceiver; - virtual transport::PacketReceiverCallback packet_receiver() OVERRIDE; - - // Interface to get audio and video frames from the CastReceiver. - virtual scoped_refptr<FrameReceiver> frame_receiver() OVERRIDE; - - private: - void ReceivedPacket(scoped_ptr<Packet> packet); - - transport::PacedSender pacer_; - AudioReceiver audio_receiver_; - VideoReceiver video_receiver_; - scoped_refptr<FrameReceiver> frame_receiver_; - scoped_refptr<CastEnvironment> cast_environment_; - const uint32 ssrc_of_audio_sender_; - const uint32 ssrc_of_video_sender_; - - DISALLOW_COPY_AND_ASSIGN(CastReceiverImpl); -}; - -} // namespace cast -} // namespace media - -#endif // MEDIA_CAST_CAST_RECEIVER_IMPL_ diff --git a/media/cast/cast_testing.gypi b/media/cast/cast_testing.gypi index 666b582..ff6bb57 100644 --- a/media/cast/cast_testing.gypi +++ b/media/cast/cast_testing.gypi @@ -67,8 +67,6 @@ ], 'sources': [ '<(DEPTH)/media/base/run_all_unittests.cc', - 'audio_receiver/audio_decoder_unittest.cc', - 'audio_receiver/audio_receiver_unittest.cc', 'audio_sender/audio_encoder_unittest.cc', 'audio_sender/audio_sender_unittest.cc', 'congestion_control/congestion_control_unittest.cc', @@ -82,6 +80,9 @@ 'logging/receiver_time_offset_estimator_impl_unittest.cc', 'logging/simple_event_subscriber_unittest.cc', 'logging/stats_event_subscriber_unittest.cc', + 'receiver/audio_decoder_unittest.cc', + 'receiver/frame_receiver_unittest.cc', + 'receiver/video_decoder_unittest.cc', 'rtcp/mock_rtcp_receiver_feedback.cc', 'rtcp/mock_rtcp_receiver_feedback.h', 'rtcp/mock_rtcp_sender_feedback.cc', @@ -117,8 +118,6 @@ 'transport/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc', 'transport/rtp_sender/rtp_packetizer/test/rtp_header_parser.h', 'transport/transport/udp_transport_unittest.cc', - 'video_receiver/video_decoder_unittest.cc', - 'video_receiver/video_receiver_unittest.cc', 'video_sender/external_video_encoder_unittest.cc', 'video_sender/video_encoder_impl_unittest.cc', 'video_sender/video_sender_unittest.cc', diff --git a/media/cast/audio_receiver/audio_decoder.cc b/media/cast/receiver/audio_decoder.cc index e4e9a14..a4d1896 100644 --- a/media/cast/audio_receiver/audio_decoder.cc +++ b/media/cast/receiver/audio_decoder.cc @@ -1,8 +1,8 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/cast/audio_receiver/audio_decoder.h" +#include "media/cast/receiver/audio_decoder.h" #include "base/bind.h" #include "base/bind_helpers.h" @@ -200,18 +200,16 @@ class AudioDecoder::Pcm16Impl : public AudioDecoder::ImplBase { AudioDecoder::AudioDecoder( const scoped_refptr<CastEnvironment>& cast_environment, - const FrameReceiverConfig& audio_config) + int channels, + int sampling_rate, + transport::AudioCodec codec) : cast_environment_(cast_environment) { - switch (audio_config.codec.audio) { + switch (codec) { case transport::kOpus: - impl_ = new OpusImpl(cast_environment, - audio_config.channels, - audio_config.frequency); + impl_ = new OpusImpl(cast_environment, channels, sampling_rate); break; case transport::kPcm16: - impl_ = new Pcm16Impl(cast_environment, - audio_config.channels, - audio_config.frequency); + impl_ = new Pcm16Impl(cast_environment, channels, sampling_rate); break; default: NOTREACHED() << "Unknown or unspecified codec."; diff --git a/media/cast/audio_receiver/audio_decoder.h b/media/cast/receiver/audio_decoder.h index a8e264d..c66735e 100644 --- a/media/cast/audio_receiver/audio_decoder.h +++ b/media/cast/receiver/audio_decoder.h @@ -1,9 +1,9 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_CAST_AUDIO_RECEIVER_AUDIO_DECODER_H_ -#define MEDIA_CAST_AUDIO_RECEIVER_AUDIO_DECODER_H_ +#ifndef MEDIA_CAST_RECEIVER_AUDIO_DECODER_H_ +#define MEDIA_CAST_RECEIVER_AUDIO_DECODER_H_ #include "base/callback.h" #include "base/memory/ref_counted.h" @@ -27,7 +27,9 @@ class AudioDecoder { bool is_continuous)> DecodeFrameCallback; AudioDecoder(const scoped_refptr<CastEnvironment>& cast_environment, - const FrameReceiverConfig& audio_config); + int channels, + int sampling_rate, + transport::AudioCodec codec); virtual ~AudioDecoder(); // Returns STATUS_AUDIO_INITIALIZED if the decoder was successfully @@ -59,4 +61,4 @@ class AudioDecoder { } // namespace cast } // namespace media -#endif // MEDIA_CAST_AUDIO_RECEIVER_AUDIO_DECODER_H_ +#endif // MEDIA_CAST_RECEIVER_AUDIO_DECODER_H_ diff --git a/media/cast/audio_receiver/audio_decoder_unittest.cc b/media/cast/receiver/audio_decoder_unittest.cc index 11973ab..6985a69 100644 --- a/media/cast/audio_receiver/audio_decoder_unittest.cc +++ b/media/cast/receiver/audio_decoder_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,10 +8,9 @@ #include "base/synchronization/lock.h" #include "base/sys_byteorder.h" #include "base/time/time.h" -#include "media/cast/audio_receiver/audio_decoder.h" #include "media/cast/cast_config.h" +#include "media/cast/receiver/audio_decoder.h" #include "media/cast/test/utility/audio_utility.h" -#include "media/cast/test/utility/default_config.h" #include "media/cast/test/utility/standalone_cast_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/opus/src/include/opus.h" @@ -38,11 +37,10 @@ class AudioDecoderTest : public ::testing::TestWithParam<TestScenario> { protected: virtual void SetUp() OVERRIDE { - FrameReceiverConfig decoder_config = GetDefaultAudioReceiverConfig(); - decoder_config.frequency = GetParam().sampling_rate; - decoder_config.channels = GetParam().num_channels; - decoder_config.codec.audio = GetParam().codec; - audio_decoder_.reset(new AudioDecoder(cast_environment_, decoder_config)); + audio_decoder_.reset(new AudioDecoder(cast_environment_, + GetParam().num_channels, + GetParam().sampling_rate, + GetParam().codec)); CHECK_EQ(STATUS_AUDIO_INITIALIZED, audio_decoder_->InitializationResult()); audio_bus_factory_.reset( diff --git a/media/cast/receiver/cast_receiver_impl.cc b/media/cast/receiver/cast_receiver_impl.cc new file mode 100644 index 0000000..7cff354 --- /dev/null +++ b/media/cast/receiver/cast_receiver_impl.cc @@ -0,0 +1,232 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/cast/receiver/cast_receiver_impl.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "media/cast/receiver/audio_decoder.h" +#include "media/cast/receiver/video_decoder.h" + +namespace media { +namespace cast { + +scoped_ptr<CastReceiver> CastReceiver::Create( + scoped_refptr<CastEnvironment> cast_environment, + const FrameReceiverConfig& audio_config, + const FrameReceiverConfig& video_config, + transport::PacketSender* const packet_sender) { + return scoped_ptr<CastReceiver>(new CastReceiverImpl( + cast_environment, audio_config, video_config, packet_sender)); +} + +CastReceiverImpl::CastReceiverImpl( + scoped_refptr<CastEnvironment> cast_environment, + const FrameReceiverConfig& audio_config, + const FrameReceiverConfig& video_config, + transport::PacketSender* const packet_sender) + : cast_environment_(cast_environment), + pacer_(cast_environment->Clock(), + cast_environment->Logging(), + packet_sender, + cast_environment->GetTaskRunner(CastEnvironment::MAIN)), + audio_receiver_(cast_environment, audio_config, AUDIO_EVENT, &pacer_), + video_receiver_(cast_environment, video_config, VIDEO_EVENT, &pacer_), + ssrc_of_audio_sender_(audio_config.incoming_ssrc), + ssrc_of_video_sender_(video_config.incoming_ssrc), + num_audio_channels_(audio_config.channels), + audio_sampling_rate_(audio_config.frequency), + audio_codec_(audio_config.codec.audio), + video_codec_(video_config.codec.video) {} + +CastReceiverImpl::~CastReceiverImpl() {} + +void CastReceiverImpl::DispatchReceivedPacket(scoped_ptr<Packet> packet) { + const uint8_t* const data = &packet->front(); + const size_t length = packet->size(); + + uint32 ssrc_of_sender; + if (Rtcp::IsRtcpPacket(data, length)) { + ssrc_of_sender = Rtcp::GetSsrcOfSender(data, length); + } else if (!FrameReceiver::ParseSenderSsrc(data, length, &ssrc_of_sender)) { + VLOG(1) << "Invalid RTP packet."; + return; + } + + base::WeakPtr<FrameReceiver> target; + if (ssrc_of_sender == ssrc_of_video_sender_) { + target = video_receiver_.AsWeakPtr(); + } else if (ssrc_of_sender == ssrc_of_audio_sender_) { + target = audio_receiver_.AsWeakPtr(); + } else { + VLOG(1) << "Dropping packet with a non matching sender SSRC: " + << ssrc_of_sender; + return; + } + cast_environment_->PostTask( + CastEnvironment::MAIN, + FROM_HERE, + base::Bind(base::IgnoreResult(&FrameReceiver::ProcessPacket), + target, + base::Passed(&packet))); +} + +transport::PacketReceiverCallback CastReceiverImpl::packet_receiver() { + return base::Bind(&CastReceiverImpl::DispatchReceivedPacket, + // TODO(miu): This code structure is dangerous, since the + // callback could be stored and then invoked after + // destruction of |this|. + base::Unretained(this)); +} + +void CastReceiverImpl::RequestDecodedAudioFrame( + const AudioFrameDecodedCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + DCHECK(!callback.is_null()); + audio_receiver_.RequestEncodedFrame(base::Bind( + &CastReceiverImpl::DecodeEncodedAudioFrame, + // Note: Use of Unretained is safe since this Closure is guaranteed to be + // invoked or discarded by |audio_receiver_| before destruction of |this|. + base::Unretained(this), + callback)); +} + +void CastReceiverImpl::RequestEncodedAudioFrame( + const ReceiveEncodedFrameCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + audio_receiver_.RequestEncodedFrame(callback); +} + +void CastReceiverImpl::RequestDecodedVideoFrame( + const VideoFrameDecodedCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + DCHECK(!callback.is_null()); + video_receiver_.RequestEncodedFrame(base::Bind( + &CastReceiverImpl::DecodeEncodedVideoFrame, + // Note: Use of Unretained is safe since this Closure is guaranteed to be + // invoked or discarded by |video_receiver_| before destruction of |this|. + base::Unretained(this), + callback)); +} + +void CastReceiverImpl::RequestEncodedVideoFrame( + const ReceiveEncodedFrameCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + video_receiver_.RequestEncodedFrame(callback); +} + +void CastReceiverImpl::DecodeEncodedAudioFrame( + const AudioFrameDecodedCallback& callback, + scoped_ptr<transport::EncodedFrame> encoded_frame) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + if (!encoded_frame) { + callback.Run(make_scoped_ptr<AudioBus>(NULL), base::TimeTicks(), false); + return; + } + + if (!audio_decoder_) { + audio_decoder_.reset(new AudioDecoder(cast_environment_, + num_audio_channels_, + audio_sampling_rate_, + audio_codec_)); + } + const uint32 frame_id = encoded_frame->frame_id; + const uint32 rtp_timestamp = encoded_frame->rtp_timestamp; + const base::TimeTicks playout_time = encoded_frame->reference_time; + audio_decoder_->DecodeFrame( + encoded_frame.Pass(), + base::Bind(&CastReceiverImpl::EmitDecodedAudioFrame, + cast_environment_, + callback, + frame_id, + rtp_timestamp, + playout_time)); +} + +void CastReceiverImpl::DecodeEncodedVideoFrame( + const VideoFrameDecodedCallback& callback, + scoped_ptr<transport::EncodedFrame> encoded_frame) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + if (!encoded_frame) { + callback.Run( + make_scoped_refptr<VideoFrame>(NULL), base::TimeTicks(), false); + return; + } + + // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc + TRACE_EVENT_INSTANT2( + "cast_perf_test", "PullEncodedVideoFrame", + TRACE_EVENT_SCOPE_THREAD, + "rtp_timestamp", encoded_frame->rtp_timestamp, + "render_time", encoded_frame->reference_time.ToInternalValue()); + + if (!video_decoder_) + video_decoder_.reset(new VideoDecoder(cast_environment_, video_codec_)); + const uint32 frame_id = encoded_frame->frame_id; + const uint32 rtp_timestamp = encoded_frame->rtp_timestamp; + const base::TimeTicks playout_time = encoded_frame->reference_time; + video_decoder_->DecodeFrame( + encoded_frame.Pass(), + base::Bind(&CastReceiverImpl::EmitDecodedVideoFrame, + cast_environment_, + callback, + frame_id, + rtp_timestamp, + playout_time)); +} + +// static +void CastReceiverImpl::EmitDecodedAudioFrame( + const scoped_refptr<CastEnvironment>& cast_environment, + const AudioFrameDecodedCallback& callback, + uint32 frame_id, + uint32 rtp_timestamp, + const base::TimeTicks& playout_time, + scoped_ptr<AudioBus> audio_bus, + bool is_continuous) { + DCHECK(cast_environment->CurrentlyOn(CastEnvironment::MAIN)); + if (audio_bus.get()) { + const base::TimeTicks now = cast_environment->Clock()->NowTicks(); + cast_environment->Logging()->InsertFrameEvent( + now, FRAME_DECODED, AUDIO_EVENT, rtp_timestamp, frame_id); + cast_environment->Logging()->InsertFrameEventWithDelay( + now, FRAME_PLAYOUT, AUDIO_EVENT, rtp_timestamp, frame_id, + playout_time - now); + } + callback.Run(audio_bus.Pass(), playout_time, is_continuous); +} + +// static +void CastReceiverImpl::EmitDecodedVideoFrame( + const scoped_refptr<CastEnvironment>& cast_environment, + const VideoFrameDecodedCallback& callback, + uint32 frame_id, + uint32 rtp_timestamp, + const base::TimeTicks& playout_time, + const scoped_refptr<VideoFrame>& video_frame, + bool is_continuous) { + DCHECK(cast_environment->CurrentlyOn(CastEnvironment::MAIN)); + if (video_frame) { + const base::TimeTicks now = cast_environment->Clock()->NowTicks(); + cast_environment->Logging()->InsertFrameEvent( + now, FRAME_DECODED, VIDEO_EVENT, rtp_timestamp, frame_id); + cast_environment->Logging()->InsertFrameEventWithDelay( + now, FRAME_PLAYOUT, VIDEO_EVENT, rtp_timestamp, frame_id, + playout_time - now); + + // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc + TRACE_EVENT_INSTANT1( + "cast_perf_test", "FrameDecoded", + TRACE_EVENT_SCOPE_THREAD, + "rtp_timestamp", rtp_timestamp); + } + callback.Run(video_frame, playout_time, is_continuous); +} + +} // namespace cast +} // namespace media diff --git a/media/cast/receiver/cast_receiver_impl.h b/media/cast/receiver/cast_receiver_impl.h new file mode 100644 index 0000000..c0dd5f3 --- /dev/null +++ b/media/cast/receiver/cast_receiver_impl.h @@ -0,0 +1,122 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_CAST_RECEIVER_CAST_RECEIVER_IMPL_H_ +#define MEDIA_CAST_RECEIVER_CAST_RECEIVER_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/cast/cast_config.h" +#include "media/cast/cast_environment.h" +#include "media/cast/cast_receiver.h" +#include "media/cast/receiver/frame_receiver.h" +#include "media/cast/transport/pacing/paced_sender.h" + +namespace media { +namespace cast { + +class AudioDecoder; +class VideoDecoder; + +// This is a pure owner class that groups all required receiver-related objects +// together, such as the paced packet sender, audio/video RTP frame receivers, +// and software decoders (created on-demand). +class CastReceiverImpl : public CastReceiver { + public: + CastReceiverImpl(scoped_refptr<CastEnvironment> cast_environment, + const FrameReceiverConfig& audio_config, + const FrameReceiverConfig& video_config, + transport::PacketSender* const packet_sender); + + virtual ~CastReceiverImpl(); + + // CastReceiver implementation. + virtual transport::PacketReceiverCallback packet_receiver() OVERRIDE; + virtual void RequestDecodedAudioFrame( + const AudioFrameDecodedCallback& callback) OVERRIDE; + virtual void RequestEncodedAudioFrame( + const ReceiveEncodedFrameCallback& callback) OVERRIDE; + virtual void RequestDecodedVideoFrame( + const VideoFrameDecodedCallback& callback) OVERRIDE; + virtual void RequestEncodedVideoFrame( + const ReceiveEncodedFrameCallback& callback) OVERRIDE; + + private: + // Forwards |packet| to a specific RTP frame receiver, or drops it if SSRC + // does not map to one of the receivers. + void DispatchReceivedPacket(scoped_ptr<Packet> packet); + + // Feeds an EncodedFrame into |audio_decoder_|. RequestDecodedAudioFrame() + // uses this as a callback for RequestEncodedAudioFrame(). + void DecodeEncodedAudioFrame( + const AudioFrameDecodedCallback& callback, + scoped_ptr<transport::EncodedFrame> encoded_frame); + + // Feeds an EncodedFrame into |video_decoder_|. RequestDecodedVideoFrame() + // uses this as a callback for RequestEncodedVideoFrame(). + void DecodeEncodedVideoFrame( + const VideoFrameDecodedCallback& callback, + scoped_ptr<transport::EncodedFrame> encoded_frame); + + // Receives an AudioBus from |audio_decoder_|, logs the event, and passes the + // data on by running the given |callback|. This method is static to ensure + // it can be called after a CastReceiverImpl instance is destroyed. + // DecodeEncodedAudioFrame() uses this as a callback for + // AudioDecoder::DecodeFrame(). + static void EmitDecodedAudioFrame( + const scoped_refptr<CastEnvironment>& cast_environment, + const AudioFrameDecodedCallback& callback, + uint32 frame_id, + uint32 rtp_timestamp, + const base::TimeTicks& playout_time, + scoped_ptr<AudioBus> audio_bus, + bool is_continuous); + + // Receives a VideoFrame from |video_decoder_|, logs the event, and passes the + // data on by running the given |callback|. This method is static to ensure + // it can be called after a CastReceiverImpl instance is destroyed. + // DecodeEncodedVideoFrame() uses this as a callback for + // VideoDecoder::DecodeFrame(). + static void EmitDecodedVideoFrame( + const scoped_refptr<CastEnvironment>& cast_environment, + const VideoFrameDecodedCallback& callback, + uint32 frame_id, + uint32 rtp_timestamp, + const base::TimeTicks& playout_time, + const scoped_refptr<VideoFrame>& video_frame, + bool is_continuous); + + const scoped_refptr<CastEnvironment> cast_environment_; + transport::PacedSender pacer_; + FrameReceiver audio_receiver_; + FrameReceiver video_receiver_; + + // Used by DispatchReceivedPacket() to direct packets to the appropriate frame + // receiver. + const uint32 ssrc_of_audio_sender_; + const uint32 ssrc_of_video_sender_; + + // Parameters for the decoders that are created on-demand. The values here + // might be nonsense if the client of CastReceiverImpl never intends to use + // the internal software-based decoders. + const int num_audio_channels_; + const int audio_sampling_rate_; + const transport::AudioCodec audio_codec_; + const transport::VideoCodec video_codec_; + + // Created on-demand to decode frames from |audio_receiver_| into AudioBuses + // for playback. + scoped_ptr<AudioDecoder> audio_decoder_; + + // Created on-demand to decode frames from |video_receiver_| into VideoFrame + // images for playback. + scoped_ptr<VideoDecoder> video_decoder_; + + DISALLOW_COPY_AND_ASSIGN(CastReceiverImpl); +}; + +} // namespace cast +} // namespace media + +#endif // MEDIA_CAST_RECEIVER_CAST_RECEIVER_IMPL_ diff --git a/media/cast/audio_receiver/audio_receiver.cc b/media/cast/receiver/frame_receiver.cc index 1f47827..e189cc9 100644 --- a/media/cast/audio_receiver/audio_receiver.cc +++ b/media/cast/receiver/frame_receiver.cc @@ -1,16 +1,16 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/cast/audio_receiver/audio_receiver.h" +#include "media/cast/receiver/frame_receiver.h" #include <algorithm> +#include "base/big_endian.h" #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" -#include "media/cast/audio_receiver/audio_decoder.h" -#include "media/cast/transport/cast_transport_defines.h" +#include "media/cast/cast_environment.h" namespace { const int kMinSchedulingDelayMs = 1; @@ -19,56 +19,103 @@ const int kMinSchedulingDelayMs = 1; namespace media { namespace cast { -AudioReceiver::AudioReceiver(scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& audio_config, - transport::PacedPacketSender* const packet_sender) - : RtpReceiver(cast_environment->Clock(), &audio_config, NULL), - cast_environment_(cast_environment), - event_subscriber_(kReceiverRtcpEventHistorySize, AUDIO_EVENT), - codec_(audio_config.codec.audio), - frequency_(audio_config.frequency), +FrameReceiver::FrameReceiver( + const scoped_refptr<CastEnvironment>& cast_environment, + const FrameReceiverConfig& config, + EventMediaType event_media_type, + transport::PacedPacketSender* const packet_sender) + : cast_environment_(cast_environment), + packet_parser_(config.incoming_ssrc, config.rtp_payload_type), + stats_(cast_environment->Clock()), + event_media_type_(event_media_type), + event_subscriber_(kReceiverRtcpEventHistorySize, event_media_type), + rtp_timebase_(config.frequency), target_playout_delay_( - base::TimeDelta::FromMilliseconds(audio_config.rtp_max_delay_ms)), + base::TimeDelta::FromMilliseconds(config.rtp_max_delay_ms)), expected_frame_duration_( - base::TimeDelta::FromSeconds(1) / audio_config.max_frame_rate), + base::TimeDelta::FromSeconds(1) / config.max_frame_rate), reports_are_scheduled_(false), framer_(cast_environment->Clock(), this, - audio_config.incoming_ssrc, + config.incoming_ssrc, true, - audio_config.rtp_max_delay_ms * audio_config.max_frame_rate / - 1000), - rtcp_(cast_environment, + config.rtp_max_delay_ms * config.max_frame_rate / 1000), + rtcp_(cast_environment_, NULL, NULL, packet_sender, - GetStatistics(), - audio_config.rtcp_mode, - base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval), - audio_config.feedback_ssrc, - audio_config.incoming_ssrc, - audio_config.rtcp_c_name, - true), + &stats_, + config.rtcp_mode, + base::TimeDelta::FromMilliseconds(config.rtcp_interval), + config.feedback_ssrc, + config.incoming_ssrc, + config.rtcp_c_name, + event_media_type), is_waiting_for_consecutive_frame_(false), lip_sync_drift_(ClockDriftSmoother::GetDefaultTimeConstant()), weak_factory_(this) { - DCHECK_GT(audio_config.rtp_max_delay_ms, 0); - DCHECK_GT(audio_config.max_frame_rate, 0); - audio_decoder_.reset(new AudioDecoder(cast_environment, audio_config)); - decryptor_.Initialize(audio_config.aes_key, audio_config.aes_iv_mask); + DCHECK_GT(config.rtp_max_delay_ms, 0); + DCHECK_GT(config.max_frame_rate, 0); + decryptor_.Initialize(config.aes_key, config.aes_iv_mask); rtcp_.SetTargetDelay(target_playout_delay_); cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber_); memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_)); } -AudioReceiver::~AudioReceiver() { +FrameReceiver::~FrameReceiver() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber_); } -void AudioReceiver::OnReceivedPayloadData(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header) { +void FrameReceiver::RequestEncodedFrame( + const ReceiveEncodedFrameCallback& callback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + frame_request_queue_.push_back(callback); + EmitAvailableEncodedFrames(); +} + +bool FrameReceiver::ProcessPacket(scoped_ptr<Packet> packet) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + + if (Rtcp::IsRtcpPacket(&packet->front(), packet->size())) { + rtcp_.IncomingRtcpPacket(&packet->front(), packet->size()); + } else { + RtpCastHeader rtp_header; + const uint8* payload_data; + size_t payload_size; + if (!packet_parser_.ParsePacket(&packet->front(), + packet->size(), + &rtp_header, + &payload_data, + &payload_size)) { + return false; + } + + ProcessParsedPacket(rtp_header, payload_data, payload_size); + stats_.UpdateStatistics(rtp_header); + } + + if (!reports_are_scheduled_) { + ScheduleNextRtcpReport(); + ScheduleNextCastMessage(); + reports_are_scheduled_ = true; + } + + return true; +} + +// static +bool FrameReceiver::ParseSenderSsrc(const uint8* packet, + size_t length, + uint32* ssrc) { + base::BigEndianReader big_endian_reader( + reinterpret_cast<const char*>(packet), length); + return big_endian_reader.Skip(8) && big_endian_reader.ReadU32(ssrc); +} + +void FrameReceiver::ProcessParsedPacket(const RtpCastHeader& rtp_header, + const uint8* payload_data, + size_t payload_size) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); @@ -76,7 +123,7 @@ void AudioReceiver::OnReceivedPayloadData(const uint8* payload_data, frame_id_to_rtp_timestamp_[rtp_header.frame_id & 0xff] = rtp_header.rtp_timestamp; cast_environment_->Logging()->InsertPacketEvent( - now, PACKET_RECEIVED, AUDIO_EVENT, rtp_header.rtp_timestamp, + now, PACKET_RECEIVED, event_media_type_, rtp_header.rtp_timestamp, rtp_header.frame_id, rtp_header.packet_id, rtp_header.max_packet_id, payload_size); @@ -111,81 +158,35 @@ void AudioReceiver::OnReceivedPayloadData(const uint8* payload_data, } else { lip_sync_reference_time_ += RtpDeltaToTimeDelta( static_cast<int32>(fresh_sync_rtp - lip_sync_rtp_timestamp_), - frequency_); + rtp_timebase_); } lip_sync_rtp_timestamp_ = fresh_sync_rtp; lip_sync_drift_.Update( now, fresh_sync_reference - lip_sync_reference_time_); } - // Frame not complete; wait for more packets. - if (!complete) - return; - - EmitAvailableEncodedFrames(); -} - -void AudioReceiver::GetRawAudioFrame( - const AudioFrameDecodedCallback& callback) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - DCHECK(!callback.is_null()); - DCHECK(audio_decoder_.get()); - GetEncodedAudioFrame(base::Bind( - &AudioReceiver::DecodeEncodedAudioFrame, - // Note: Use of Unretained is safe since this Closure is guaranteed to be - // invoked before destruction of |this|. - base::Unretained(this), - callback)); + // Another frame is complete from a non-duplicate packet. Attempt to emit + // more frames to satisfy enqueued requests. + if (complete) + EmitAvailableEncodedFrames(); } -void AudioReceiver::DecodeEncodedAudioFrame( - const AudioFrameDecodedCallback& callback, - scoped_ptr<transport::EncodedFrame> encoded_frame) { +void FrameReceiver::CastFeedback(const RtcpCastMessage& cast_message) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - if (!encoded_frame) { - callback.Run(make_scoped_ptr<AudioBus>(NULL), base::TimeTicks(), false); - return; - } - const uint32 frame_id = encoded_frame->frame_id; - const uint32 rtp_timestamp = encoded_frame->rtp_timestamp; - const base::TimeTicks playout_time = encoded_frame->reference_time; - audio_decoder_->DecodeFrame(encoded_frame.Pass(), - base::Bind(&AudioReceiver::EmitRawAudioFrame, - cast_environment_, - callback, - frame_id, - rtp_timestamp, - playout_time)); -} -// static -void AudioReceiver::EmitRawAudioFrame( - const scoped_refptr<CastEnvironment>& cast_environment, - const AudioFrameDecodedCallback& callback, - uint32 frame_id, - uint32 rtp_timestamp, - const base::TimeTicks& playout_time, - scoped_ptr<AudioBus> audio_bus, - bool is_continuous) { - DCHECK(cast_environment->CurrentlyOn(CastEnvironment::MAIN)); - if (audio_bus.get()) { - const base::TimeTicks now = cast_environment->Clock()->NowTicks(); - cast_environment->Logging()->InsertFrameEvent( - now, FRAME_DECODED, AUDIO_EVENT, rtp_timestamp, frame_id); - cast_environment->Logging()->InsertFrameEventWithDelay( - now, FRAME_PLAYOUT, AUDIO_EVENT, rtp_timestamp, frame_id, - playout_time - now); - } - callback.Run(audio_bus.Pass(), playout_time, is_continuous); -} + base::TimeTicks now = cast_environment_->Clock()->NowTicks(); + RtpTimestamp rtp_timestamp = + frame_id_to_rtp_timestamp_[cast_message.ack_frame_id_ & 0xff]; + cast_environment_->Logging()->InsertFrameEvent( + now, FRAME_ACK_SENT, event_media_type_, + rtp_timestamp, cast_message.ack_frame_id_); -void AudioReceiver::GetEncodedAudioFrame(const FrameEncodedCallback& callback) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - frame_request_queue_.push_back(callback); - EmitAvailableEncodedFrames(); + ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events; + event_subscriber_.GetRtcpEventsAndReset(&rtcp_events); + rtcp_.SendRtcpFromRtpReceiver(&cast_message, &rtcp_events); } -void AudioReceiver::EmitAvailableEncodedFrames() { +void FrameReceiver::EmitAvailableEncodedFrames() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); while (!frame_request_queue_.empty()) { @@ -199,8 +200,8 @@ void AudioReceiver::EmitAvailableEncodedFrames() { if (!framer_.GetEncodedFrame(encoded_frame.get(), &is_consecutively_next_frame, &have_multiple_complete_frames)) { - VLOG(1) << "Wait for more audio packets to produce a completed frame."; - return; // OnReceivedPayloadData() will invoke this method in the future. + VLOG(1) << "Wait for more packets to produce a completed frame."; + return; // ProcessParsedPacket() will invoke this method in the future. } const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); @@ -229,7 +230,7 @@ void AudioReceiver::EmitAvailableEncodedFrames() { cast_environment_->PostDelayedTask( CastEnvironment::MAIN, FROM_HERE, - base::Bind(&AudioReceiver::EmitAvailableEncodedFramesAfterWaiting, + base::Bind(&FrameReceiver::EmitAvailableEncodedFramesAfterWaiting, weak_factory_.GetWeakPtr()), playout_time - now); } @@ -239,16 +240,15 @@ void AudioReceiver::EmitAvailableEncodedFrames() { // Decrypt the payload data in the frame, if crypto is being used. if (decryptor_.initialized()) { - std::string decrypted_audio_data; + std::string decrypted_data; if (!decryptor_.Decrypt(encoded_frame->frame_id, encoded_frame->data, - &decrypted_audio_data)) { - // Decryption failed. Give up on this frame, releasing it from the - // jitter buffer. + &decrypted_data)) { + // Decryption failed. Give up on this frame. framer_.ReleaseFrame(encoded_frame->frame_id); continue; } - encoded_frame->data.swap(decrypted_audio_data); + encoded_frame->data.swap(decrypted_data); } // At this point, we have a decrypted EncodedFrame ready to be emitted. @@ -262,95 +262,64 @@ void AudioReceiver::EmitAvailableEncodedFrames() { } } -void AudioReceiver::EmitAvailableEncodedFramesAfterWaiting() { +void FrameReceiver::EmitAvailableEncodedFramesAfterWaiting() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(is_waiting_for_consecutive_frame_); is_waiting_for_consecutive_frame_ = false; EmitAvailableEncodedFrames(); } -base::TimeTicks AudioReceiver::GetPlayoutTime(uint32 rtp_timestamp) const { +base::TimeTicks FrameReceiver::GetPlayoutTime(uint32 rtp_timestamp) const { return lip_sync_reference_time_ + lip_sync_drift_.Current() + RtpDeltaToTimeDelta( static_cast<int32>(rtp_timestamp - lip_sync_rtp_timestamp_), - frequency_) + + rtp_timebase_) + target_playout_delay_; } -void AudioReceiver::IncomingPacket(scoped_ptr<Packet> packet) { +void FrameReceiver::ScheduleNextCastMessage() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - if (Rtcp::IsRtcpPacket(&packet->front(), packet->size())) { - rtcp_.IncomingRtcpPacket(&packet->front(), packet->size()); - } else { - ReceivedPacket(&packet->front(), packet->size()); - } - if (!reports_are_scheduled_) { - ScheduleNextRtcpReport(); - ScheduleNextCastMessage(); - reports_are_scheduled_ = true; - } -} - -void AudioReceiver::CastFeedback(const RtcpCastMessage& cast_message) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - base::TimeTicks now = cast_environment_->Clock()->NowTicks(); - RtpTimestamp rtp_timestamp = - frame_id_to_rtp_timestamp_[cast_message.ack_frame_id_ & 0xff]; - cast_environment_->Logging()->InsertFrameEvent( - now, FRAME_ACK_SENT, AUDIO_EVENT, rtp_timestamp, - cast_message.ack_frame_id_); - - ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events; - event_subscriber_.GetRtcpEventsAndReset(&rtcp_events); - rtcp_.SendRtcpFromRtpReceiver(&cast_message, &rtcp_events); -} - -void AudioReceiver::ScheduleNextRtcpReport() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - base::TimeDelta time_to_send = rtcp_.TimeToSendNextRtcpReport() - - cast_environment_->Clock()->NowTicks(); - + base::TimeTicks send_time; + framer_.TimeToSendNextCastMessage(&send_time); + base::TimeDelta time_to_send = + send_time - cast_environment_->Clock()->NowTicks(); time_to_send = std::max( time_to_send, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs)); - cast_environment_->PostDelayedTask( CastEnvironment::MAIN, FROM_HERE, - base::Bind(&AudioReceiver::SendNextRtcpReport, + base::Bind(&FrameReceiver::SendNextCastMessage, weak_factory_.GetWeakPtr()), time_to_send); } -void AudioReceiver::SendNextRtcpReport() { +void FrameReceiver::SendNextCastMessage() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - // TODO(pwestin): add logging. - rtcp_.SendRtcpFromRtpReceiver(NULL, NULL); - ScheduleNextRtcpReport(); + framer_.SendCastMessage(); // Will only send a message if it is time. + ScheduleNextCastMessage(); } -// 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() { +void FrameReceiver::ScheduleNextRtcpReport() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - base::TimeTicks send_time; - framer_.TimeToSendNextCastMessage(&send_time); - base::TimeDelta time_to_send = - send_time - cast_environment_->Clock()->NowTicks(); - time_to_send = std::max( - time_to_send, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs)); + base::TimeDelta time_to_next = rtcp_.TimeToSendNextRtcpReport() - + cast_environment_->Clock()->NowTicks(); + + time_to_next = std::max( + time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs)); + cast_environment_->PostDelayedTask( CastEnvironment::MAIN, FROM_HERE, - base::Bind(&AudioReceiver::SendNextCastMessage, + base::Bind(&FrameReceiver::SendNextRtcpReport, weak_factory_.GetWeakPtr()), - time_to_send); + time_to_next); } -void AudioReceiver::SendNextCastMessage() { +void FrameReceiver::SendNextRtcpReport() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - framer_.SendCastMessage(); // Will only send a message if it is time. - ScheduleNextCastMessage(); + rtcp_.SendRtcpFromRtpReceiver(NULL, NULL); + ScheduleNextRtcpReport(); } } // namespace cast diff --git a/media/cast/video_receiver/video_receiver.h b/media/cast/receiver/frame_receiver.h index 14a4bfa..ac14ab1 100644 --- a/media/cast/video_receiver/video_receiver.h +++ b/media/cast/receiver/frame_receiver.h @@ -1,41 +1,35 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_CAST_VIDEO_RECEIVER_VIDEO_RECEIVER_H_ -#define MEDIA_CAST_VIDEO_RECEIVER_VIDEO_RECEIVER_H_ +#ifndef MEDIA_CAST_RECEIVER_FRAME_RECEIVER_H_ +#define MEDIA_CAST_RECEIVER_FRAME_RECEIVER_H_ -#include "base/basictypes.h" -#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/threading/non_thread_safe.h" -#include "base/time/tick_clock.h" #include "base/time/time.h" #include "media/cast/base/clock_drift_smoother.h" #include "media/cast/cast_config.h" -#include "media/cast/cast_environment.h" #include "media/cast/cast_receiver.h" #include "media/cast/framer/framer.h" +#include "media/cast/logging/logging_defines.h" #include "media/cast/rtcp/receiver_rtcp_event_subscriber.h" #include "media/cast/rtcp/rtcp.h" -#include "media/cast/rtp_receiver/rtp_receiver.h" +#include "media/cast/rtp_receiver/receiver_stats.h" +#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h" #include "media/cast/rtp_receiver/rtp_receiver_defines.h" #include "media/cast/transport/utility/transport_encryption_handler.h" namespace media { - -class VideoFrame; - namespace cast { -class VideoDecoder; +class CastEnvironment; -// VideoReceiver receives packets out-of-order while clients make requests for +// FrameReceiver receives packets out-of-order while clients make requests for // complete frames in-order. (A frame consists of one or more packets.) // -// VideoReceiver also includes logic for computing the playout time for each +// FrameReceiver also includes logic for computing the playout time for each // frame, accounting for a constant targeted playout delay. The purpose of the // playout delay is to provide a fixed window of time between the capture event // on the sender and the playout on the receiver. This is important because @@ -43,47 +37,43 @@ class VideoDecoder; // the sender, then receive and re-order packets on the receiver, then decode // frame) can vary in duration and is typically very hard to predict. // -// Two types of frames can be requested: 1) A frame of decoded video data; or 2) -// a frame of still-encoded video data, to be passed into an external video -// decoder. Each request for a frame includes a callback which VideoReceiver -// guarantees will be called at some point in the future unless the -// VideoReceiver is destroyed. Clients should generally limit the number of -// outstanding requests (perhaps to just one or two). +// Each request for a frame includes a callback which FrameReceiver guarantees +// will be called at some point in the future unless the FrameReceiver is +// destroyed. Clients should generally limit the number of outstanding requests +// (perhaps to just one or two). // // This class is not thread safe. Should only be called from the Main cast // thread. -class VideoReceiver : public RtpReceiver, - public RtpPayloadFeedback, - public base::NonThreadSafe, - public base::SupportsWeakPtr<VideoReceiver> { +class FrameReceiver : public RtpPayloadFeedback, + public base::SupportsWeakPtr<FrameReceiver> { public: - VideoReceiver(scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& video_config, + FrameReceiver(const scoped_refptr<CastEnvironment>& cast_environment, + const FrameReceiverConfig& config, + EventMediaType event_media_type, transport::PacedPacketSender* const packet_sender); - virtual ~VideoReceiver(); + virtual ~FrameReceiver(); - // Request a decoded video frame. + // Request an encoded frame. // // The given |callback| is guaranteed to be run at some point in the future, - // even if to respond with NULL at shutdown time. - void GetRawVideoFrame(const VideoFrameDecodedCallback& callback); + // except for those requests still enqueued at destruction time. + void RequestEncodedFrame(const ReceiveEncodedFrameCallback& callback); - // Request an encoded video frame. - // - // The given |callback| is guaranteed to be run at some point in the future, - // even if to respond with NULL at shutdown time. - void GetEncodedVideoFrame(const FrameEncodedCallback& callback); + // Called to deliver another packet, possibly a duplicate, and possibly + // out-of-order. Returns true if the parsing of the packet succeeded. + bool ProcessPacket(scoped_ptr<Packet> packet); - // Deliver another packet, possibly a duplicate, and possibly out-of-order. - void IncomingPacket(scoped_ptr<Packet> packet); + // TODO(miu): This is the wrong place for this, but the (de)serialization + // implementation needs to be consolidated first. + static bool ParseSenderSsrc(const uint8* packet, size_t length, uint32* ssrc); protected: - friend class VideoReceiverTest; // Invokes OnReceivedPayloadData(). + friend class FrameReceiverTest; // Invokes ProcessParsedPacket(). - virtual void OnReceivedPayloadData(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header) OVERRIDE; + void ProcessParsedPacket(const RtpCastHeader& rtp_header, + const uint8* payload_data, + size_t payload_size); // RtpPayloadFeedback implementation. virtual void CastFeedback(const RtcpCastMessage& cast_message) OVERRIDE; @@ -99,12 +89,6 @@ class VideoReceiver : public RtpReceiver, // EmitAvailableEncodedFrames(). void EmitAvailableEncodedFramesAfterWaiting(); - // Feeds an EncodedFrame into |video_decoder_|. GetRawVideoFrame() uses this - // as a callback for GetEncodedVideoFrame(). - void DecodeEncodedVideoFrame( - const VideoFrameDecodedCallback& callback, - scoped_ptr<transport::EncodedFrame> encoded_frame); - // Computes the playout time for a frame with the given |rtp_timestamp|. // Because lip-sync info is refreshed regularly, calling this method with the // same argument may return different results. @@ -122,28 +106,23 @@ class VideoReceiver : public RtpReceiver, // Actually send the next RTCP report. void SendNextRtcpReport(); - // Receives a VideoFrame from |video_decoder_|, logs the event, and passes the - // data on by running the given |callback|. This method is static to ensure - // it can be called after a VideoReceiver instance is destroyed. - // DecodeEncodedVideoFrame() uses this as a callback for - // VideoDecoder::DecodeFrame(). - static void EmitRawVideoFrame( - const scoped_refptr<CastEnvironment>& cast_environment, - const VideoFrameDecodedCallback& callback, - uint32 frame_id, - uint32 rtp_timestamp, - const base::TimeTicks& playout_time, - const scoped_refptr<VideoFrame>& video_frame, - bool is_continuous); - const scoped_refptr<CastEnvironment> cast_environment_; + // Deserializes a packet into a RtpHeader + payload bytes. + RtpParser packet_parser_; + + // Accumulates packet statistics, including packet loss, counts, and jitter. + ReceiverStats stats_; + + // Partitions logged events by the type of media passing through. + EventMediaType event_media_type_; + // Subscribes to raw events. - // Processes raw audio events to be sent over to the cast sender via RTCP. + // Processes raw events to be sent over to the cast sender via RTCP. ReceiverRtcpEventSubscriber event_subscriber_; - // Configured video codec. - const transport::VideoCodec codec_; + // RTP timebase: The number of RTP units advanced per one second. + const int rtp_timebase_; // The total amount of time between a frame's capture/recording on the sender // and its playback on the receiver (i.e., shown to a user). This is fixed as @@ -154,6 +133,8 @@ class VideoReceiver : public RtpReceiver, const base::TimeDelta target_playout_delay_; // Hack: This is used in logic that determines whether to skip frames. + // TODO(miu): Revisit this. Logic needs to also account for expected decode + // time. const base::TimeDelta expected_frame_duration_; // Set to false initially, then set to true after scheduling the periodic @@ -166,9 +147,6 @@ class VideoReceiver : public RtpReceiver, // decodable EncodedFrames. Framer framer_; - // Decodes frames into media::VideoFrame images for playback. - scoped_ptr<VideoDecoder> video_decoder_; - // Manages sending/receiving of RTCP packets, including sender/receiver // reports. Rtcp rtcp_; @@ -177,7 +155,7 @@ class VideoReceiver : public RtpReceiver, transport::TransportEncryptionHandler decryptor_; // Outstanding callbacks to run to deliver on client requests for frames. - std::list<FrameEncodedCallback> frame_request_queue_; + std::list<ReceiveEncodedFrameCallback> frame_request_queue_; // True while there's an outstanding task to re-invoke // EmitAvailableEncodedFrames(). @@ -195,12 +173,12 @@ class VideoReceiver : public RtpReceiver, ClockDriftSmoother lip_sync_drift_; // NOTE: Weak pointers must be invalidated before all other member variables. - base::WeakPtrFactory<VideoReceiver> weak_factory_; + base::WeakPtrFactory<FrameReceiver> weak_factory_; - DISALLOW_COPY_AND_ASSIGN(VideoReceiver); + DISALLOW_COPY_AND_ASSIGN(FrameReceiver); }; } // namespace cast } // namespace media -#endif // MEDIA_CAST_VIDEO_RECEIVER_VIDEO_RECEIVER_H_ +#endif // MEDIA_CAST_RECEIVER_FRAME_RECEIVER_H_ diff --git a/media/cast/receiver/frame_receiver_unittest.cc b/media/cast/receiver/frame_receiver_unittest.cc new file mode 100644 index 0000000..4d8273e1 --- /dev/null +++ b/media/cast/receiver/frame_receiver_unittest.cc @@ -0,0 +1,419 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <deque> +#include <utility> + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/simple_test_tick_clock.h" +#include "media/cast/cast_defines.h" +#include "media/cast/cast_environment.h" +#include "media/cast/logging/simple_event_subscriber.h" +#include "media/cast/receiver/frame_receiver.h" +#include "media/cast/rtcp/test_rtcp_packet_builder.h" +#include "media/cast/test/fake_single_thread_task_runner.h" +#include "media/cast/test/utility/default_config.h" +#include "media/cast/transport/pacing/mock_paced_packet_sender.h" +#include "testing/gmock/include/gmock/gmock.h" + +using ::testing::_; + +namespace media { +namespace cast { + +namespace { + +const int kPacketSize = 1500; +const uint32 kFirstFrameId = 1234; +const int kPlayoutDelayMillis = 100; + +class FakeFrameClient { + public: + FakeFrameClient() : num_called_(0) {} + virtual ~FakeFrameClient() {} + + void AddExpectedResult(uint32 expected_frame_id, + const base::TimeTicks& expected_playout_time) { + expected_results_.push_back( + std::make_pair(expected_frame_id, expected_playout_time)); + } + + void DeliverEncodedFrame(scoped_ptr<transport::EncodedFrame> frame) { + SCOPED_TRACE(::testing::Message() << "num_called_ is " << num_called_); + ASSERT_FALSE(!frame) + << "If at shutdown: There were unsatisfied requests enqueued."; + ASSERT_FALSE(expected_results_.empty()); + EXPECT_EQ(expected_results_.front().first, frame->frame_id); + EXPECT_EQ(expected_results_.front().second, frame->reference_time); + expected_results_.pop_front(); + ++num_called_; + } + + int number_times_called() const { return num_called_; } + + private: + std::deque<std::pair<uint32, base::TimeTicks> > expected_results_; + int num_called_; + + DISALLOW_COPY_AND_ASSIGN(FakeFrameClient); +}; +} // namespace + +class FrameReceiverTest : public ::testing::Test { + protected: + FrameReceiverTest() { + testing_clock_ = new base::SimpleTestTickClock(); + testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks()); + start_time_ = testing_clock_->NowTicks(); + task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_); + + cast_environment_ = + new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(), + task_runner_, + task_runner_, + task_runner_); + } + + virtual ~FrameReceiverTest() {} + + virtual void SetUp() { + payload_.assign(kPacketSize, 0); + + // Always start with a key frame. + rtp_header_.is_key_frame = true; + rtp_header_.frame_id = kFirstFrameId; + rtp_header_.packet_id = 0; + rtp_header_.max_packet_id = 0; + rtp_header_.reference_frame_id = rtp_header_.frame_id; + rtp_header_.rtp_timestamp = 0; + } + + void CreateFrameReceiverOfAudio() { + config_ = GetDefaultAudioReceiverConfig(); + config_.rtp_max_delay_ms = kPlayoutDelayMillis; + + receiver_.reset(new FrameReceiver( + cast_environment_, config_, AUDIO_EVENT, &mock_transport_)); + } + + void CreateFrameReceiverOfVideo() { + config_ = GetDefaultVideoReceiverConfig(); + config_.rtp_max_delay_ms = kPlayoutDelayMillis; + // Note: Frame rate must divide 1000 without remainder so the test code + // doesn't have to account for rounding errors. + config_.max_frame_rate = 25; + + receiver_.reset(new FrameReceiver( + cast_environment_, config_, VIDEO_EVENT, &mock_transport_)); + } + + void FeedOneFrameIntoReceiver() { + // Note: For testing purposes, a frame consists of only a single packet. + receiver_->ProcessParsedPacket( + rtp_header_, payload_.data(), payload_.size()); + } + + void FeedLipSyncInfoIntoReceiver() { + const base::TimeTicks now = testing_clock_->NowTicks(); + const int64 rtp_timestamp = (now - start_time_) * + config_.frequency / base::TimeDelta::FromSeconds(1); + CHECK_LE(0, rtp_timestamp); + uint32 ntp_seconds; + uint32 ntp_fraction; + ConvertTimeTicksToNtp(now, &ntp_seconds, &ntp_fraction); + TestRtcpPacketBuilder rtcp_packet; + rtcp_packet.AddSrWithNtp(config_.incoming_ssrc, + ntp_seconds, ntp_fraction, + static_cast<uint32>(rtp_timestamp)); + ASSERT_TRUE(receiver_->ProcessPacket(rtcp_packet.GetPacket().Pass())); + } + + FrameReceiverConfig config_; + std::vector<uint8> payload_; + RtpCastHeader rtp_header_; + base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment. + base::TimeTicks start_time_; + transport::MockPacedPacketSender mock_transport_; + scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_; + scoped_refptr<CastEnvironment> cast_environment_; + FakeFrameClient frame_client_; + + // Important for the FrameReceiver to be declared last, since its dependencies + // must remain alive until after its destruction. + scoped_ptr<FrameReceiver> receiver_; + + DISALLOW_COPY_AND_ASSIGN(FrameReceiverTest); +}; + +TEST_F(FrameReceiverTest, RejectsUnparsablePackets) { + CreateFrameReceiverOfVideo(); + + SimpleEventSubscriber event_subscriber; + cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); + + const bool success = receiver_->ProcessPacket( + scoped_ptr<Packet>(new Packet(kPacketSize, 0xff)).Pass()); + EXPECT_FALSE(success); + + // Confirm no log events. + std::vector<FrameEvent> frame_events; + event_subscriber.GetFrameEventsAndReset(&frame_events); + EXPECT_TRUE(frame_events.empty()); + cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); +} + +TEST_F(FrameReceiverTest, ReceivesOneFrame) { + CreateFrameReceiverOfAudio(); + + SimpleEventSubscriber event_subscriber; + cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); + + EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) + .WillRepeatedly(testing::Return(true)); + + FeedLipSyncInfoIntoReceiver(); + task_runner_->RunTasks(); + + // Enqueue a request for a frame. + receiver_->RequestEncodedFrame( + base::Bind(&FakeFrameClient::DeliverEncodedFrame, + base::Unretained(&frame_client_))); + + // The request should not be satisfied since no packets have been received. + task_runner_->RunTasks(); + EXPECT_EQ(0, frame_client_.number_times_called()); + + // Deliver one frame to the receiver and expect to get one frame back. + const base::TimeDelta target_playout_delay = + base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); + frame_client_.AddExpectedResult( + kFirstFrameId, testing_clock_->NowTicks() + target_playout_delay); + FeedOneFrameIntoReceiver(); + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Was the frame logged? + std::vector<FrameEvent> frame_events; + event_subscriber.GetFrameEventsAndReset(&frame_events); + ASSERT_TRUE(!frame_events.empty()); + EXPECT_EQ(FRAME_ACK_SENT, frame_events.begin()->type); + EXPECT_EQ(AUDIO_EVENT, frame_events.begin()->media_type); + EXPECT_EQ(rtp_header_.frame_id, frame_events.begin()->frame_id); + EXPECT_EQ(rtp_header_.rtp_timestamp, frame_events.begin()->rtp_timestamp); + cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); +} + +TEST_F(FrameReceiverTest, ReceivesFramesSkippingWhenAppropriate) { + CreateFrameReceiverOfAudio(); + + SimpleEventSubscriber event_subscriber; + cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); + + EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) + .WillRepeatedly(testing::Return(true)); + + const uint32 rtp_advance_per_frame = + config_.frequency / config_.max_frame_rate; + const base::TimeDelta time_advance_per_frame = + base::TimeDelta::FromSeconds(1) / config_.max_frame_rate; + + // Feed and process lip sync in receiver. + FeedLipSyncInfoIntoReceiver(); + task_runner_->RunTasks(); + const base::TimeTicks first_frame_capture_time = testing_clock_->NowTicks(); + + // Enqueue a request for a frame. + const ReceiveEncodedFrameCallback frame_encoded_callback = + base::Bind(&FakeFrameClient::DeliverEncodedFrame, + base::Unretained(&frame_client_)); + receiver_->RequestEncodedFrame(frame_encoded_callback); + task_runner_->RunTasks(); + EXPECT_EQ(0, frame_client_.number_times_called()); + + // Receive one frame and expect to see the first request satisfied. + const base::TimeDelta target_playout_delay = + base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); + frame_client_.AddExpectedResult( + kFirstFrameId, first_frame_capture_time + target_playout_delay); + rtp_header_.rtp_timestamp = 0; + FeedOneFrameIntoReceiver(); // Frame 1 + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Enqueue a second request for a frame, but it should not be fulfilled yet. + receiver_->RequestEncodedFrame(frame_encoded_callback); + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Receive one frame out-of-order: Make sure that we are not continuous and + // that the RTP timestamp represents a time in the future. + rtp_header_.frame_id = kFirstFrameId + 2; // "Frame 3" + rtp_header_.reference_frame_id = rtp_header_.frame_id; + rtp_header_.rtp_timestamp += 2 * rtp_advance_per_frame; + frame_client_.AddExpectedResult( + kFirstFrameId + 2, + first_frame_capture_time + 2 * time_advance_per_frame + + target_playout_delay); + FeedOneFrameIntoReceiver(); // Frame 3 + + // Frame 2 should not come out at this point in time. + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Enqueue a third request for a frame. + receiver_->RequestEncodedFrame(frame_encoded_callback); + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Now, advance time forward such that the receiver is convinced it should + // skip Frame 2. Frame 3 is emitted (to satisfy the second request) because a + // decision was made to skip over the no-show Frame 2. + testing_clock_->Advance(2 * time_advance_per_frame + target_playout_delay); + task_runner_->RunTasks(); + EXPECT_EQ(2, frame_client_.number_times_called()); + + // Receive Frame 4 and expect it to fulfill the third request immediately. + rtp_header_.frame_id = kFirstFrameId + 3; // "Frame 4" + rtp_header_.reference_frame_id = rtp_header_.frame_id; + rtp_header_.rtp_timestamp += rtp_advance_per_frame; + frame_client_.AddExpectedResult( + kFirstFrameId + 3, first_frame_capture_time + 3 * time_advance_per_frame + + target_playout_delay); + FeedOneFrameIntoReceiver(); // Frame 4 + task_runner_->RunTasks(); + EXPECT_EQ(3, frame_client_.number_times_called()); + + // Move forward to the playout time of an unreceived Frame 5. Expect no + // additional frames were emitted. + testing_clock_->Advance(3 * time_advance_per_frame); + task_runner_->RunTasks(); + EXPECT_EQ(3, frame_client_.number_times_called()); + + // Were only non-skipped frames logged? + std::vector<FrameEvent> frame_events; + event_subscriber.GetFrameEventsAndReset(&frame_events); + ASSERT_TRUE(!frame_events.empty()); + for (size_t i = 0; i < frame_events.size(); ++i) { + EXPECT_EQ(FRAME_ACK_SENT, frame_events[i].type); + EXPECT_EQ(AUDIO_EVENT, frame_events[i].media_type); + EXPECT_LE(kFirstFrameId, frame_events[i].frame_id); + EXPECT_GE(kFirstFrameId + 4, frame_events[i].frame_id); + const int frame_offset = frame_events[i].frame_id - kFirstFrameId; + EXPECT_NE(frame_offset, 1); // Frame 2 never received. + EXPECT_EQ(frame_offset * rtp_advance_per_frame, + frame_events[i].rtp_timestamp); + } + cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); +} + +TEST_F(FrameReceiverTest, ReceivesFramesRefusingToSkipAny) { + CreateFrameReceiverOfVideo(); + + SimpleEventSubscriber event_subscriber; + cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); + + EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) + .WillRepeatedly(testing::Return(true)); + + const uint32 rtp_advance_per_frame = + config_.frequency / config_.max_frame_rate; + const base::TimeDelta time_advance_per_frame = + base::TimeDelta::FromSeconds(1) / config_.max_frame_rate; + + // Feed and process lip sync in receiver. + FeedLipSyncInfoIntoReceiver(); + task_runner_->RunTasks(); + const base::TimeTicks first_frame_capture_time = testing_clock_->NowTicks(); + + // Enqueue a request for a frame. + const ReceiveEncodedFrameCallback frame_encoded_callback = + base::Bind(&FakeFrameClient::DeliverEncodedFrame, + base::Unretained(&frame_client_)); + receiver_->RequestEncodedFrame(frame_encoded_callback); + task_runner_->RunTasks(); + EXPECT_EQ(0, frame_client_.number_times_called()); + + // Receive one frame and expect to see the first request satisfied. + const base::TimeDelta target_playout_delay = + base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); + frame_client_.AddExpectedResult( + kFirstFrameId, first_frame_capture_time + target_playout_delay); + rtp_header_.rtp_timestamp = 0; + FeedOneFrameIntoReceiver(); // Frame 1 + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Enqueue a second request for a frame, but it should not be fulfilled yet. + receiver_->RequestEncodedFrame(frame_encoded_callback); + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Receive one frame out-of-order: Make sure that we are not continuous and + // that the RTP timestamp represents a time in the future. + rtp_header_.is_key_frame = false; + rtp_header_.frame_id = kFirstFrameId + 2; // "Frame 3" + rtp_header_.reference_frame_id = kFirstFrameId + 1; // "Frame 2" + rtp_header_.rtp_timestamp += 2 * rtp_advance_per_frame; + FeedOneFrameIntoReceiver(); // Frame 3 + + // Frame 2 should not come out at this point in time. + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Enqueue a third request for a frame. + receiver_->RequestEncodedFrame(frame_encoded_callback); + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Now, advance time forward such that Frame 2 is now too late for playback. + // Regardless, the receiver must NOT emit Frame 3 yet because it is not + // allowed to skip frames when dependencies are not satisfied. In other + // words, Frame 3 is not decodable without Frame 2. + testing_clock_->Advance(2 * time_advance_per_frame + target_playout_delay); + task_runner_->RunTasks(); + EXPECT_EQ(1, frame_client_.number_times_called()); + + // Now receive Frame 2 and expect both the second and third requests to be + // fulfilled immediately. + frame_client_.AddExpectedResult( + kFirstFrameId + 1, // "Frame 2" + first_frame_capture_time + 1 * time_advance_per_frame + + target_playout_delay); + frame_client_.AddExpectedResult( + kFirstFrameId + 2, // "Frame 3" + first_frame_capture_time + 2 * time_advance_per_frame + + target_playout_delay); + --rtp_header_.frame_id; // "Frame 2" + --rtp_header_.reference_frame_id; // "Frame 1" + rtp_header_.rtp_timestamp -= rtp_advance_per_frame; + FeedOneFrameIntoReceiver(); // Frame 2 + task_runner_->RunTasks(); + EXPECT_EQ(3, frame_client_.number_times_called()); + + // Move forward to the playout time of an unreceived Frame 5. Expect no + // additional frames were emitted. + testing_clock_->Advance(3 * time_advance_per_frame); + task_runner_->RunTasks(); + EXPECT_EQ(3, frame_client_.number_times_called()); + + // Sanity-check logging results. + std::vector<FrameEvent> frame_events; + event_subscriber.GetFrameEventsAndReset(&frame_events); + ASSERT_TRUE(!frame_events.empty()); + for (size_t i = 0; i < frame_events.size(); ++i) { + EXPECT_EQ(FRAME_ACK_SENT, frame_events[i].type); + EXPECT_EQ(VIDEO_EVENT, frame_events[i].media_type); + EXPECT_LE(kFirstFrameId, frame_events[i].frame_id); + EXPECT_GE(kFirstFrameId + 3, frame_events[i].frame_id); + const int frame_offset = frame_events[i].frame_id - kFirstFrameId; + EXPECT_EQ(frame_offset * rtp_advance_per_frame, + frame_events[i].rtp_timestamp); + } + cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); +} + +} // namespace cast +} // namespace media diff --git a/media/cast/video_receiver/video_decoder.cc b/media/cast/receiver/video_decoder.cc index c2d2562..21f49d8 100644 --- a/media/cast/video_receiver/video_decoder.cc +++ b/media/cast/receiver/video_decoder.cc @@ -1,8 +1,8 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/cast/video_receiver/video_decoder.h" +#include "media/cast/receiver/video_decoder.h" #include "base/bind.h" #include "base/bind_helpers.h" @@ -209,9 +209,9 @@ class VideoDecoder::FakeImpl : public VideoDecoder::ImplBase { VideoDecoder::VideoDecoder( const scoped_refptr<CastEnvironment>& cast_environment, - const FrameReceiverConfig& video_config) + transport::VideoCodec codec) : cast_environment_(cast_environment) { - switch (video_config.codec.video) { + switch (codec) { #ifndef OFFICIAL_BUILD case transport::kFakeSoftwareVideo: impl_ = new FakeImpl(cast_environment); diff --git a/media/cast/video_receiver/video_decoder.h b/media/cast/receiver/video_decoder.h index ea40173..66dc36b 100644 --- a/media/cast/video_receiver/video_decoder.h +++ b/media/cast/receiver/video_decoder.h @@ -1,9 +1,9 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_CAST_VIDEO_RECEIVER_VIDEO_DECODER_H_ -#define MEDIA_CAST_VIDEO_RECEIVER_VIDEO_DECODER_H_ +#ifndef MEDIA_CAST_RECEIVER_VIDEO_DECODER_H_ +#define MEDIA_CAST_RECEIVER_VIDEO_DECODER_H_ #include "base/callback.h" #include "base/memory/ref_counted.h" @@ -28,7 +28,7 @@ class VideoDecoder { bool is_continuous)> DecodeFrameCallback; VideoDecoder(const scoped_refptr<CastEnvironment>& cast_environment, - const FrameReceiverConfig& video_config); + transport::VideoCodec codec); virtual ~VideoDecoder(); // Returns STATUS_VIDEO_INITIALIZED if the decoder was successfully @@ -60,4 +60,4 @@ class VideoDecoder { } // namespace cast } // namespace media -#endif // MEDIA_CAST_VIDEO_RECEIVER_VIDEO_DECODER_H_ +#endif // MEDIA_CAST_RECEIVER_VIDEO_DECODER_H_ diff --git a/media/cast/video_receiver/video_decoder_unittest.cc b/media/cast/receiver/video_decoder_unittest.cc index 2ca28b0..1d16534 100644 --- a/media/cast/video_receiver/video_decoder_unittest.cc +++ b/media/cast/receiver/video_decoder_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,10 +10,9 @@ #include "base/synchronization/lock.h" #include "base/time/time.h" #include "media/cast/cast_config.h" -#include "media/cast/test/utility/default_config.h" +#include "media/cast/receiver/video_decoder.h" #include "media/cast/test/utility/standalone_cast_environment.h" #include "media/cast/test/utility/video_utility.h" -#include "media/cast/video_receiver/video_decoder.h" #include "media/cast/video_sender/codecs/vp8/vp8_encoder.h" #include "testing/gtest/include/gtest/gtest.h" @@ -48,9 +47,7 @@ class VideoDecoderTest protected: virtual void SetUp() OVERRIDE { - FrameReceiverConfig decoder_config = GetDefaultVideoReceiverConfig(); - decoder_config.codec.video = GetParam(); - video_decoder_.reset(new VideoDecoder(cast_environment_, decoder_config)); + video_decoder_.reset(new VideoDecoder(cast_environment_, GetParam())); CHECK_EQ(STATUS_VIDEO_INITIALIZED, video_decoder_->InitializationResult()); next_frame_timestamp_ = base::TimeDelta(); diff --git a/media/cast/rtcp/rtcp.cc b/media/cast/rtcp/rtcp.cc index 3aa936b..480b2ac 100644 --- a/media/cast/rtcp/rtcp.cc +++ b/media/cast/rtcp/rtcp.cc @@ -79,7 +79,8 @@ Rtcp::Rtcp(scoped_refptr<CastEnvironment> cast_environment, transport::PacedPacketSender* paced_packet_sender, RtpReceiverStatistics* rtp_receiver_statistics, RtcpMode rtcp_mode, const base::TimeDelta& rtcp_interval, uint32 local_ssrc, - uint32 remote_ssrc, const std::string& c_name, bool is_audio) + uint32 remote_ssrc, const std::string& c_name, + EventMediaType event_media_type) : cast_environment_(cast_environment), transport_sender_(transport_sender), rtcp_interval_(rtcp_interval), @@ -87,6 +88,7 @@ Rtcp::Rtcp(scoped_refptr<CastEnvironment> cast_environment, local_ssrc_(local_ssrc), remote_ssrc_(remote_ssrc), c_name_(c_name), + event_media_type_(event_media_type), rtp_receiver_statistics_(rtp_receiver_statistics), rtt_feedback_(new LocalRtcpRttFeedback(this)), receiver_feedback_(new LocalRtcpReceiverFeedback(this, cast_environment)), @@ -97,8 +99,7 @@ Rtcp::Rtcp(scoped_refptr<CastEnvironment> cast_environment, lip_sync_rtp_timestamp_(0), lip_sync_ntp_timestamp_(0), min_rtt_(base::TimeDelta::FromMilliseconds(kMaxRttMs)), - number_of_rtt_in_avg_(0), - is_audio_(is_audio) { + number_of_rtt_in_avg_(0) { rtcp_receiver_.reset(new RtcpReceiver(cast_environment, sender_feedback, receiver_feedback_.get(), rtt_feedback_.get(), local_ssrc)); @@ -394,7 +395,6 @@ void Rtcp::UpdateNextTimeToSendRtcp() { void Rtcp::OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log) { // Add received log messages into our log system. RtcpReceiverLogMessage::const_iterator it = receiver_log.begin(); - EventMediaType media_type = is_audio_ ? AUDIO_EVENT : VIDEO_EVENT; for (; it != receiver_log.end(); ++it) { uint32 rtp_timestamp = it->rtp_timestamp_; @@ -405,18 +405,18 @@ void Rtcp::OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log) { case PACKET_RECEIVED: cast_environment_->Logging()->InsertPacketEvent( event_it->event_timestamp, event_it->type, - media_type, rtp_timestamp, + event_media_type_, rtp_timestamp, kFrameIdUnknown, event_it->packet_id, 0, 0); break; case FRAME_ACK_SENT: case FRAME_DECODED: cast_environment_->Logging()->InsertFrameEvent( - event_it->event_timestamp, event_it->type, media_type, + event_it->event_timestamp, event_it->type, event_media_type_, rtp_timestamp, kFrameIdUnknown); break; case FRAME_PLAYOUT: cast_environment_->Logging()->InsertFrameEventWithDelay( - event_it->event_timestamp, event_it->type, media_type, + event_it->event_timestamp, event_it->type, event_media_type_, rtp_timestamp, kFrameIdUnknown, event_it->delay_delta); break; default: diff --git a/media/cast/rtcp/rtcp.h b/media/cast/rtcp/rtcp.h index ff81bb9..9d0184f 100644 --- a/media/cast/rtcp/rtcp.h +++ b/media/cast/rtcp/rtcp.h @@ -68,7 +68,7 @@ class Rtcp { uint32 local_ssrc, uint32 remote_ssrc, const std::string& c_name, - bool is_audio); + EventMediaType event_media_type); virtual ~Rtcp(); @@ -156,6 +156,7 @@ class Rtcp { const uint32 local_ssrc_; const uint32 remote_ssrc_; const std::string c_name_; + const EventMediaType event_media_type_; // Not owned by this class. RtpReceiverStatistics* const rtp_receiver_statistics_; @@ -195,7 +196,6 @@ class Rtcp { int number_of_rtt_in_avg_; double avg_rtt_ms_; uint16 target_delay_ms_; - bool is_audio_; DISALLOW_COPY_AND_ASSIGN(Rtcp); }; diff --git a/media/cast/rtcp/rtcp_unittest.cc b/media/cast/rtcp/rtcp_unittest.cc index 3cae9b9..d5bf312 100644 --- a/media/cast/rtcp/rtcp_unittest.cc +++ b/media/cast/rtcp/rtcp_unittest.cc @@ -144,7 +144,7 @@ class RtcpPeer : public Rtcp { local_ssrc, remote_ssrc, c_name, - true) {} + AUDIO_EVENT) {} using Rtcp::OnReceivedNtp; using Rtcp::OnReceivedLipSyncInfo; @@ -219,7 +219,7 @@ TEST_F(RtcpTest, TimeToSend) { kSenderSsrc, kReceiverSsrc, kCName, - true); + AUDIO_EVENT); receiver_to_sender_.set_rtcp_receiver(&rtcp); EXPECT_LE(start_time, rtcp.TimeToSendNextRtcpReport()); EXPECT_GE( @@ -241,7 +241,7 @@ TEST_F(RtcpTest, BasicSenderReport) { kSenderSsrc, kReceiverSsrc, kCName, - true); + AUDIO_EVENT); sender_to_receiver_.set_rtcp_receiver(&rtcp); rtcp.SendRtcpFromRtpSender(base::TimeTicks(), 0); } @@ -257,7 +257,7 @@ TEST_F(RtcpTest, BasicReceiverReport) { kSenderSsrc, kReceiverSsrc, kCName, - true); + AUDIO_EVENT); receiver_to_sender_.set_rtcp_receiver(&rtcp); rtcp.SendRtcpFromRtpReceiver(NULL, NULL); } @@ -276,7 +276,7 @@ TEST_F(RtcpTest, BasicCast) { kSenderSsrc, kSenderSsrc, kCName, - true); + AUDIO_EVENT); receiver_to_sender_.set_rtcp_receiver(&rtcp); RtcpCastMessage cast_message(kSenderSsrc); cast_message.ack_frame_id_ = kAckFrameId; @@ -303,7 +303,7 @@ TEST_F(RtcpTest, RttReducedSizeRtcp) { kReceiverSsrc, kSenderSsrc, kCName, - true); + AUDIO_EVENT); // Media sender. Rtcp rtcp_sender(cast_environment_, @@ -316,7 +316,7 @@ TEST_F(RtcpTest, RttReducedSizeRtcp) { kSenderSsrc, kReceiverSsrc, kCName, - true); + AUDIO_EVENT); sender_to_receiver_.set_rtcp_receiver(&rtcp_receiver); receiver_to_sender_.set_rtcp_receiver(&rtcp_sender); @@ -359,7 +359,7 @@ TEST_F(RtcpTest, Rtt) { kReceiverSsrc, kSenderSsrc, kCName, - true); + AUDIO_EVENT); // Media sender. Rtcp rtcp_sender(cast_environment_, @@ -372,7 +372,7 @@ TEST_F(RtcpTest, Rtt) { kSenderSsrc, kReceiverSsrc, kCName, - true); + AUDIO_EVENT); receiver_to_sender_.set_rtcp_receiver(&rtcp_sender); sender_to_receiver_.set_rtcp_receiver(&rtcp_receiver); @@ -452,7 +452,7 @@ TEST_F(RtcpTest, RttWithPacketLoss) { kReceiverSsrc, kSenderSsrc, kCName, - true); + AUDIO_EVENT); // Media sender. Rtcp rtcp_sender(cast_environment_, @@ -465,7 +465,7 @@ TEST_F(RtcpTest, RttWithPacketLoss) { kSenderSsrc, kReceiverSsrc, kCName, - true); + AUDIO_EVENT); receiver_to_sender_.set_rtcp_receiver(&rtcp_sender); sender_to_receiver_.set_rtcp_receiver(&rtcp_receiver); diff --git a/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc b/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc index d75696a..f44e82d 100644 --- a/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc +++ b/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc @@ -7,7 +7,6 @@ #include "base/big_endian.h" #include "base/logging.h" #include "media/cast/cast_defines.h" -#include "media/cast/rtp_receiver/rtp_receiver.h" namespace media { namespace cast { diff --git a/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc b/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc index 4e3560a9..47c7913 100644 --- a/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc +++ b/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc @@ -6,7 +6,6 @@ #include "base/rand_util.h" #include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h" #include "media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.h" -#include "media/cast/rtp_receiver/rtp_receiver.h" #include "media/cast/rtp_receiver/rtp_receiver_defines.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/media/cast/rtp_receiver/rtp_receiver.cc b/media/cast/rtp_receiver/rtp_receiver.cc deleted file mode 100644 index f7ff50b..0000000 --- a/media/cast/rtp_receiver/rtp_receiver.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/cast/rtp_receiver/rtp_receiver.h" - -#include "base/big_endian.h" -#include "base/logging.h" -#include "media/cast/rtp_receiver/receiver_stats.h" -#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h" -#include "media/cast/rtp_receiver/rtp_receiver_defines.h" - -namespace media { -namespace cast { - -RtpReceiver::RtpReceiver(base::TickClock* clock, - const FrameReceiverConfig* audio_config, - const FrameReceiverConfig* video_config) : - packet_parser_(audio_config ? audio_config->incoming_ssrc : - (video_config ? video_config->incoming_ssrc : 0), - audio_config ? audio_config->rtp_payload_type : - (video_config ? video_config->rtp_payload_type : 0)), - stats_(clock) { - DCHECK(audio_config || video_config); -} - -RtpReceiver::~RtpReceiver() {} - -// static -uint32 RtpReceiver::GetSsrcOfSender(const uint8* rtcp_buffer, size_t length) { - DCHECK_GE(length, kMinLengthOfRtp) << "Invalid RTP packet"; - uint32 ssrc_of_sender; - base::BigEndianReader big_endian_reader( - reinterpret_cast<const char*>(rtcp_buffer), length); - big_endian_reader.Skip(8); // Skip header - big_endian_reader.ReadU32(&ssrc_of_sender); - return ssrc_of_sender; -} - -bool RtpReceiver::ReceivedPacket(const uint8* packet, size_t length) { - RtpCastHeader rtp_header; - const uint8* payload_data; - size_t payload_size; - if (!packet_parser_.ParsePacket( - packet, length, &rtp_header, &payload_data, &payload_size)) { - return false; - } - - OnReceivedPayloadData(payload_data, payload_size, rtp_header); - stats_.UpdateStatistics(rtp_header); - return true; -} - -} // namespace cast -} // namespace media diff --git a/media/cast/rtp_receiver/rtp_receiver.h b/media/cast/rtp_receiver/rtp_receiver.h deleted file mode 100644 index 35c7c93..0000000 --- a/media/cast/rtp_receiver/rtp_receiver.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Interface to the rtp receiver. - -#ifndef MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_ -#define MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_ - -#include "base/memory/scoped_ptr.h" -#include "media/cast/cast_config.h" -#include "media/cast/rtcp/rtcp.h" -#include "media/cast/rtp_receiver/receiver_stats.h" -#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h" -#include "media/cast/rtp_receiver/rtp_receiver_defines.h" - -namespace media { -namespace cast { - -// TODO(miu): This is a good candidate to contain common functionality that's -// identical in both AudioReceiver and VideoReceiver. -class RtpReceiver { - public: - RtpReceiver(base::TickClock* clock, - const FrameReceiverConfig* audio_config, - const FrameReceiverConfig* video_config); - virtual ~RtpReceiver(); - - static uint32 GetSsrcOfSender(const uint8* rtcp_buffer, size_t length); - - bool ReceivedPacket(const uint8* packet, size_t length); - - RtpReceiverStatistics* GetStatistics() { - return &stats_; - } - - protected: - // Subclasses implement this to consume and process deserialized packets. - virtual void OnReceivedPayloadData(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header) = 0; - - private: - RtpParser packet_parser_; - ReceiverStats stats_; - - DISALLOW_COPY_AND_ASSIGN(RtpReceiver); -}; - -} // namespace cast -} // namespace media - -#endif // MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_ diff --git a/media/cast/test/end2end_unittest.cc b/media/cast/test/end2end_unittest.cc index cfba81d..4a0d820 100644 --- a/media/cast/test/end2end_unittest.cc +++ b/media/cast/test/end2end_unittest.cc @@ -566,7 +566,7 @@ class End2EndTest : public ::testing::Test { void RequestAudioFrames(int count, bool with_check) { for (int i = 0; i < count; ++i) { - frame_receiver_->GetRawAudioFrame( + cast_receiver_->RequestDecodedAudioFrame( base::Bind(with_check ? &TestReceiverAudioCallback::CheckAudioFrame : &TestReceiverAudioCallback::IgnoreAudioFrame, test_receiver_audio_callback_)); @@ -611,8 +611,6 @@ class End2EndTest : public ::testing::Test { audio_frame_input_ = cast_sender_->audio_frame_input(); video_frame_input_ = cast_sender_->video_frame_input(); - frame_receiver_ = cast_receiver_->frame_receiver(); - audio_bus_factory_.reset( new TestAudioBusFactory(audio_sender_config_.channels, audio_sender_config_.frequency, @@ -684,7 +682,7 @@ class End2EndTest : public ::testing::Test { video_ticks_.push_back(std::make_pair( testing_clock_receiver_->NowTicks(), playout_time)); - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&End2EndTest::BasicPlayerGotVideoFrame, base::Unretained(this))); } @@ -701,16 +699,16 @@ class End2EndTest : public ::testing::Test { audio_ticks_.push_back(std::make_pair( testing_clock_receiver_->NowTicks(), playout_time)); - frame_receiver_->GetRawAudioFrame( + cast_receiver_->RequestDecodedAudioFrame( base::Bind(&End2EndTest::BasicPlayerGotAudioFrame, base::Unretained(this))); } void StartBasicPlayer() { - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&End2EndTest::BasicPlayerGotVideoFrame, base::Unretained(this))); - frame_receiver_->GetRawAudioFrame( + cast_receiver_->RequestDecodedAudioFrame( base::Bind(&End2EndTest::BasicPlayerGotAudioFrame, base::Unretained(this))); } @@ -768,7 +766,6 @@ class End2EndTest : public ::testing::Test { scoped_ptr<CastSender> cast_sender_; scoped_refptr<AudioFrameInput> audio_frame_input_; scoped_refptr<VideoFrameInput> video_frame_input_; - scoped_refptr<FrameReceiver> frame_receiver_; scoped_refptr<TestReceiverAudioCallback> test_receiver_audio_callback_; scoped_refptr<TestReceiverVideoCallback> test_receiver_video_callback_; @@ -820,7 +817,7 @@ TEST_F(End2EndTest, LoopNoLossPcm16) { RequestAudioFrames(num_audio_frames, true); num_audio_frames_requested += num_audio_frames; - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); @@ -846,7 +843,7 @@ TEST_F(End2EndTest, LoopNoLossPcm16ExternalDecoder) { for (int i = 0; i < kNumIterations; ++i) { FeedAudioFrames(1, true); RunTasks(kAudioFrameDurationMs); - frame_receiver_->GetCodedAudioFrame( + cast_receiver_->RequestEncodedAudioFrame( base::Bind(&TestReceiverAudioCallback::CheckCodedAudioFrame, test_receiver_audio_callback_)); } @@ -954,7 +951,7 @@ TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) { RequestAudioFrames(num_audio_frames, true); num_audio_frames_requested += num_audio_frames; - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); @@ -990,7 +987,7 @@ TEST_F(End2EndTest, DISABLED_GlitchWith3Buffers) { video_sender_config_.height, capture_time + base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs), true); - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); RunTasks(kFrameTimerMs); @@ -1023,7 +1020,7 @@ TEST_F(End2EndTest, DISABLED_GlitchWith3Buffers) { capture_time + base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs), true); - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); @@ -1059,7 +1056,7 @@ TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) { // GetRawVideoFrame will not return the frame until we are close in // time before we should render the frame. - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); } @@ -1100,7 +1097,7 @@ TEST_F(End2EndTest, CryptoVideo) { RunTasks(kFrameTimerMs); - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); } @@ -1156,7 +1153,7 @@ TEST_F(End2EndTest, VideoLogging) { SendVideoFrame(video_start, capture_time); RunTasks(kFrameTimerMs); - frame_receiver_->GetRawVideoFrame( + cast_receiver_->RequestDecodedVideoFrame( base::Bind(&TestReceiverVideoCallback::CheckVideoFrame, test_receiver_video_callback_)); diff --git a/media/cast/test/utility/in_process_receiver.cc b/media/cast/test/utility/in_process_receiver.cc index ba190ce..cfcc1fc 100644 --- a/media/cast/test/utility/in_process_receiver.cc +++ b/media/cast/test/utility/in_process_receiver.cc @@ -114,14 +114,14 @@ void InProcessReceiver::GotVideoFrame( void InProcessReceiver::PullNextAudioFrame() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - cast_receiver_->frame_receiver()->GetRawAudioFrame( + cast_receiver_->RequestDecodedAudioFrame( base::Bind(&InProcessReceiver::GotAudioFrame, weak_factory_.GetWeakPtr())); } void InProcessReceiver::PullNextVideoFrame() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - cast_receiver_->frame_receiver()->GetRawVideoFrame(base::Bind( + cast_receiver_->RequestDecodedVideoFrame(base::Bind( &InProcessReceiver::GotVideoFrame, weak_factory_.GetWeakPtr())); } diff --git a/media/cast/video_receiver/video_receiver.cc b/media/cast/video_receiver/video_receiver.cc deleted file mode 100644 index d9000de..0000000 --- a/media/cast/video_receiver/video_receiver.cc +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/cast/video_receiver/video_receiver.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/debug/trace_event.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "media/base/video_frame.h" -#include "media/cast/logging/logging_defines.h" -#include "media/cast/transport/cast_transport_defines.h" -#include "media/cast/video_receiver/video_decoder.h" - -namespace { -const int kMinSchedulingDelayMs = 1; -} // namespace - -namespace media { -namespace cast { - -VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment, - const FrameReceiverConfig& video_config, - transport::PacedPacketSender* const packet_sender) - : RtpReceiver(cast_environment->Clock(), NULL, &video_config), - cast_environment_(cast_environment), - event_subscriber_(kReceiverRtcpEventHistorySize, VIDEO_EVENT), - codec_(video_config.codec.video), - target_playout_delay_( - base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)), - expected_frame_duration_( - base::TimeDelta::FromSeconds(1) / video_config.max_frame_rate), - reports_are_scheduled_(false), - framer_(cast_environment->Clock(), - this, - video_config.incoming_ssrc, - true, - video_config.rtp_max_delay_ms * video_config.max_frame_rate / - 1000), - rtcp_(cast_environment_, - NULL, - NULL, - packet_sender, - GetStatistics(), - video_config.rtcp_mode, - base::TimeDelta::FromMilliseconds(video_config.rtcp_interval), - video_config.feedback_ssrc, - video_config.incoming_ssrc, - video_config.rtcp_c_name, - false), - is_waiting_for_consecutive_frame_(false), - lip_sync_drift_(ClockDriftSmoother::GetDefaultTimeConstant()), - weak_factory_(this) { - DCHECK_GT(video_config.rtp_max_delay_ms, 0); - DCHECK_GT(video_config.max_frame_rate, 0); - video_decoder_.reset(new VideoDecoder(cast_environment, video_config)); - decryptor_.Initialize(video_config.aes_key, video_config.aes_iv_mask); - rtcp_.SetTargetDelay(target_playout_delay_); - cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber_); - memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_)); -} - -VideoReceiver::~VideoReceiver() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber_); -} - -void VideoReceiver::GetRawVideoFrame( - const VideoFrameDecodedCallback& callback) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - DCHECK(!callback.is_null()); - DCHECK(video_decoder_.get()); - GetEncodedVideoFrame(base::Bind( - &VideoReceiver::DecodeEncodedVideoFrame, - // Note: Use of Unretained is safe since this Closure is guaranteed to be - // invoked before destruction of |this|. - base::Unretained(this), - callback)); -} - -void VideoReceiver::DecodeEncodedVideoFrame( - const VideoFrameDecodedCallback& callback, - scoped_ptr<transport::EncodedFrame> encoded_frame) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - if (!encoded_frame) { - callback.Run( - make_scoped_refptr<VideoFrame>(NULL), base::TimeTicks(), false); - return; - } - const uint32 frame_id = encoded_frame->frame_id; - const uint32 rtp_timestamp = encoded_frame->rtp_timestamp; - const base::TimeTicks playout_time = encoded_frame->reference_time; - video_decoder_->DecodeFrame(encoded_frame.Pass(), - base::Bind(&VideoReceiver::EmitRawVideoFrame, - cast_environment_, - callback, - frame_id, - rtp_timestamp, - playout_time)); -} - -// static -void VideoReceiver::EmitRawVideoFrame( - const scoped_refptr<CastEnvironment>& cast_environment, - const VideoFrameDecodedCallback& callback, - uint32 frame_id, - uint32 rtp_timestamp, - const base::TimeTicks& playout_time, - const scoped_refptr<VideoFrame>& video_frame, - bool is_continuous) { - DCHECK(cast_environment->CurrentlyOn(CastEnvironment::MAIN)); - if (video_frame) { - const base::TimeTicks now = cast_environment->Clock()->NowTicks(); - cast_environment->Logging()->InsertFrameEvent( - now, FRAME_DECODED, VIDEO_EVENT, rtp_timestamp, frame_id); - cast_environment->Logging()->InsertFrameEventWithDelay( - now, FRAME_PLAYOUT, VIDEO_EVENT, rtp_timestamp, frame_id, - playout_time - now); - // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc - TRACE_EVENT_INSTANT1( - "cast_perf_test", "FrameDecoded", - TRACE_EVENT_SCOPE_THREAD, - "rtp_timestamp", rtp_timestamp); - } - callback.Run(video_frame, playout_time, is_continuous); -} - -void VideoReceiver::GetEncodedVideoFrame(const FrameEncodedCallback& callback) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - frame_request_queue_.push_back(callback); - EmitAvailableEncodedFrames(); -} - -void VideoReceiver::EmitAvailableEncodedFrames() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - - while (!frame_request_queue_.empty()) { - // Attempt to peek at the next completed frame from the |framer_|. - // TODO(miu): We should only be peeking at the metadata, and not copying the - // payload yet! Or, at least, peek using a StringPiece instead of a copy. - scoped_ptr<transport::EncodedFrame> encoded_frame( - new transport::EncodedFrame()); - bool is_consecutively_next_frame = false; - bool have_multiple_complete_frames = false; - - if (!framer_.GetEncodedFrame(encoded_frame.get(), - &is_consecutively_next_frame, - &have_multiple_complete_frames)) { - VLOG(1) << "Wait for more video packets to produce a completed frame."; - return; // OnReceivedPayloadData() will invoke this method in the future. - } - - const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); - const base::TimeTicks playout_time = - GetPlayoutTime(encoded_frame->rtp_timestamp); - - // If we have multiple decodable frames, and the current frame is - // too old, then skip it and decode the next frame instead. - if (have_multiple_complete_frames && now > playout_time) { - framer_.ReleaseFrame(encoded_frame->frame_id); - continue; - } - - // If |framer_| has a frame ready that is out of sequence, examine the - // playout time to determine whether it's acceptable to continue, thereby - // skipping one or more frames. Skip if the missing frame wouldn't complete - // playing before the start of playback of the available frame. - if (!is_consecutively_next_frame) { - // TODO(miu): Also account for expected decode time here? - const base::TimeTicks earliest_possible_end_time_of_missing_frame = - now + expected_frame_duration_; - if (earliest_possible_end_time_of_missing_frame < playout_time) { - VLOG(1) << "Wait for next consecutive frame instead of skipping."; - if (!is_waiting_for_consecutive_frame_) { - is_waiting_for_consecutive_frame_ = true; - cast_environment_->PostDelayedTask( - CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&VideoReceiver::EmitAvailableEncodedFramesAfterWaiting, - weak_factory_.GetWeakPtr()), - playout_time - now); - } - return; - } - } - - // Decrypt the payload data in the frame, if crypto is being used. - if (decryptor_.initialized()) { - std::string decrypted_video_data; - if (!decryptor_.Decrypt(encoded_frame->frame_id, - encoded_frame->data, - &decrypted_video_data)) { - // Decryption failed. Give up on this frame, releasing it from the - // jitter buffer. - framer_.ReleaseFrame(encoded_frame->frame_id); - continue; - } - encoded_frame->data.swap(decrypted_video_data); - } - - // At this point, we have a decrypted EncodedFrame ready to be emitted. - encoded_frame->reference_time = playout_time; - framer_.ReleaseFrame(encoded_frame->frame_id); - // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc - TRACE_EVENT_INSTANT2( - "cast_perf_test", "PullEncodedVideoFrame", - TRACE_EVENT_SCOPE_THREAD, - "rtp_timestamp", encoded_frame->rtp_timestamp, - // TODO(miu): Need to find an alternative to using ToInternalValue(): - "render_time", playout_time.ToInternalValue()); - cast_environment_->PostTask(CastEnvironment::MAIN, - FROM_HERE, - base::Bind(frame_request_queue_.front(), - base::Passed(&encoded_frame))); - frame_request_queue_.pop_front(); - } -} - -void VideoReceiver::EmitAvailableEncodedFramesAfterWaiting() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - DCHECK(is_waiting_for_consecutive_frame_); - is_waiting_for_consecutive_frame_ = false; - EmitAvailableEncodedFrames(); -} - -base::TimeTicks VideoReceiver::GetPlayoutTime(uint32 rtp_timestamp) const { - return lip_sync_reference_time_ + - lip_sync_drift_.Current() + - RtpDeltaToTimeDelta( - static_cast<int32>(rtp_timestamp - lip_sync_rtp_timestamp_), - kVideoFrequency) + - target_playout_delay_; -} - -void VideoReceiver::IncomingPacket(scoped_ptr<Packet> packet) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - if (Rtcp::IsRtcpPacket(&packet->front(), packet->size())) { - rtcp_.IncomingRtcpPacket(&packet->front(), packet->size()); - } else { - ReceivedPacket(&packet->front(), packet->size()); - } - if (!reports_are_scheduled_) { - ScheduleNextRtcpReport(); - ScheduleNextCastMessage(); - reports_are_scheduled_ = true; - } -} - -void VideoReceiver::OnReceivedPayloadData(const uint8* payload_data, - size_t payload_size, - const RtpCastHeader& rtp_header) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - - const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); - - frame_id_to_rtp_timestamp_[rtp_header.frame_id & 0xff] = - rtp_header.rtp_timestamp; - cast_environment_->Logging()->InsertPacketEvent( - now, - PACKET_RECEIVED, - VIDEO_EVENT, - rtp_header.rtp_timestamp, - rtp_header.frame_id, - rtp_header.packet_id, - rtp_header.max_packet_id, - payload_size); - - bool duplicate = false; - const bool complete = - framer_.InsertPacket(payload_data, payload_size, rtp_header, &duplicate); - - // Duplicate packets are ignored. - if (duplicate) - return; - - // Update lip-sync values upon receiving the first packet of each frame, or if - // they have never been set yet. - if (rtp_header.packet_id == 0 || lip_sync_reference_time_.is_null()) { - RtpTimestamp fresh_sync_rtp; - base::TimeTicks fresh_sync_reference; - if (!rtcp_.GetLatestLipSyncTimes(&fresh_sync_rtp, &fresh_sync_reference)) { - // HACK: The sender should have provided Sender Reports before the first - // frame was sent. However, the spec does not currently require this. - // Therefore, when the data is missing, the local clock is used to - // generate reference timestamps. - VLOG(2) << "Lip sync info missing. Falling-back to local clock."; - fresh_sync_rtp = rtp_header.rtp_timestamp; - fresh_sync_reference = now; - } - // |lip_sync_reference_time_| is always incremented according to the time - // delta computed from the difference in RTP timestamps. Then, - // |lip_sync_drift_| accounts for clock drift and also smoothes-out any - // sudden/discontinuous shifts in the series of reference time values. - if (lip_sync_reference_time_.is_null()) { - lip_sync_reference_time_ = fresh_sync_reference; - } else { - lip_sync_reference_time_ += RtpDeltaToTimeDelta( - static_cast<int32>(fresh_sync_rtp - lip_sync_rtp_timestamp_), - kVideoFrequency); - } - lip_sync_rtp_timestamp_ = fresh_sync_rtp; - lip_sync_drift_.Update( - now, fresh_sync_reference - lip_sync_reference_time_); - } - - // Video frame not complete; wait for more packets. - if (!complete) - return; - - EmitAvailableEncodedFrames(); -} - -// 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)); - - base::TimeTicks now = cast_environment_->Clock()->NowTicks(); - RtpTimestamp rtp_timestamp = - frame_id_to_rtp_timestamp_[cast_message.ack_frame_id_ & 0xff]; - cast_environment_->Logging()->InsertFrameEvent( - now, FRAME_ACK_SENT, VIDEO_EVENT, - rtp_timestamp, cast_message.ack_frame_id_); - - ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events; - event_subscriber_.GetRtcpEventsAndReset(&rtcp_events); - rtcp_.SendRtcpFromRtpReceiver(&cast_message, &rtcp_events); -} - -// 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); - base::TimeDelta time_to_send = - send_time - cast_environment_->Clock()->NowTicks(); - time_to_send = std::max( - time_to_send, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs)); - cast_environment_->PostDelayedTask( - CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&VideoReceiver::SendNextCastMessage, - weak_factory_.GetWeakPtr()), - time_to_send); -} - -void VideoReceiver::SendNextCastMessage() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - framer_.SendCastMessage(); // Will only send a message if it is time. - ScheduleNextCastMessage(); -} - -void VideoReceiver::ScheduleNextRtcpReport() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - base::TimeDelta time_to_next = rtcp_.TimeToSendNextRtcpReport() - - cast_environment_->Clock()->NowTicks(); - - time_to_next = std::max( - time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs)); - - cast_environment_->PostDelayedTask( - CastEnvironment::MAIN, - FROM_HERE, - base::Bind(&VideoReceiver::SendNextRtcpReport, - weak_factory_.GetWeakPtr()), - time_to_next); -} - -void VideoReceiver::SendNextRtcpReport() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - rtcp_.SendRtcpFromRtpReceiver(NULL, NULL); - ScheduleNextRtcpReport(); -} - -} // namespace cast -} // namespace media diff --git a/media/cast/video_receiver/video_receiver_unittest.cc b/media/cast/video_receiver/video_receiver_unittest.cc deleted file mode 100644 index d158d7e..0000000 --- a/media/cast/video_receiver/video_receiver_unittest.cc +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <deque> -#include <utility> - -#include "base/bind.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/test/simple_test_tick_clock.h" -#include "media/cast/cast_defines.h" -#include "media/cast/cast_environment.h" -#include "media/cast/logging/simple_event_subscriber.h" -#include "media/cast/rtcp/test_rtcp_packet_builder.h" -#include "media/cast/test/fake_single_thread_task_runner.h" -#include "media/cast/test/utility/default_config.h" -#include "media/cast/transport/pacing/mock_paced_packet_sender.h" -#include "media/cast/video_receiver/video_receiver.h" -#include "testing/gmock/include/gmock/gmock.h" - -using ::testing::_; - -namespace media { -namespace cast { - -namespace { - -const int kPacketSize = 1500; -const uint32 kFirstFrameId = 1234; -const int kPlayoutDelayMillis = 100; - -class FakeVideoClient { - public: - FakeVideoClient() : num_called_(0) {} - virtual ~FakeVideoClient() {} - - void AddExpectedResult(uint32 expected_frame_id, - const base::TimeTicks& expected_playout_time) { - expected_results_.push_back( - std::make_pair(expected_frame_id, expected_playout_time)); - } - - void DeliverEncodedVideoFrame( - scoped_ptr<transport::EncodedFrame> video_frame) { - SCOPED_TRACE(::testing::Message() << "num_called_ is " << num_called_); - ASSERT_FALSE(!video_frame) - << "If at shutdown: There were unsatisfied requests enqueued."; - ASSERT_FALSE(expected_results_.empty()); - EXPECT_EQ(expected_results_.front().first, video_frame->frame_id); - EXPECT_EQ(expected_results_.front().second, video_frame->reference_time); - expected_results_.pop_front(); - ++num_called_; - } - - int number_times_called() const { return num_called_; } - - private: - std::deque<std::pair<uint32, base::TimeTicks> > expected_results_; - int num_called_; - - DISALLOW_COPY_AND_ASSIGN(FakeVideoClient); -}; -} // namespace - -class VideoReceiverTest : public ::testing::Test { - protected: - VideoReceiverTest() { - config_ = GetDefaultVideoReceiverConfig(); - config_.rtp_max_delay_ms = kPlayoutDelayMillis; - // Note: Frame rate must divide 1000 without remainder so the test code - // doesn't have to account for rounding errors. - config_.max_frame_rate = 25; - config_.codec.video = transport::kVp8; // Frame skipping not allowed. - testing_clock_ = new base::SimpleTestTickClock(); - testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks()); - start_time_ = testing_clock_->NowTicks(); - task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_); - - cast_environment_ = - new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(), - task_runner_, - task_runner_, - task_runner_); - - receiver_.reset(new VideoReceiver( - cast_environment_, config_, &mock_transport_)); - } - - virtual ~VideoReceiverTest() {} - - virtual void SetUp() { - payload_.assign(kPacketSize, 0); - - // Always start with a key frame. - rtp_header_.is_key_frame = true; - rtp_header_.frame_id = kFirstFrameId; - rtp_header_.reference_frame_id = rtp_header_.frame_id; - rtp_header_.packet_id = 0; - rtp_header_.max_packet_id = 0; - } - - void FeedOneFrameIntoReceiver() { - receiver_->OnReceivedPayloadData( - payload_.data(), payload_.size(), rtp_header_); - } - - void FeedLipSyncInfoIntoReceiver() { - const base::TimeTicks now = testing_clock_->NowTicks(); - const int64 rtp_timestamp = (now - start_time_) * - kVideoFrequency / base::TimeDelta::FromSeconds(1); - CHECK_LE(0, rtp_timestamp); - uint32 ntp_seconds; - uint32 ntp_fraction; - ConvertTimeTicksToNtp(now, &ntp_seconds, &ntp_fraction); - TestRtcpPacketBuilder rtcp_packet; - rtcp_packet.AddSrWithNtp(config_.incoming_ssrc, - ntp_seconds, ntp_fraction, - static_cast<uint32>(rtp_timestamp)); - receiver_->IncomingPacket(rtcp_packet.GetPacket().Pass()); - } - - FrameReceiverConfig config_; - std::vector<uint8> payload_; - RtpCastHeader rtp_header_; - base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment. - base::TimeTicks start_time_; - transport::MockPacedPacketSender mock_transport_; - scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_; - scoped_refptr<CastEnvironment> cast_environment_; - FakeVideoClient fake_video_client_; - - // Important for the VideoReceiver to be declared last, since its dependencies - // must remain alive until after its destruction. - scoped_ptr<VideoReceiver> receiver_; - - DISALLOW_COPY_AND_ASSIGN(VideoReceiverTest); -}; - -TEST_F(VideoReceiverTest, ReceivesOneFrame) { - SimpleEventSubscriber event_subscriber; - cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); - - EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) - .WillRepeatedly(testing::Return(true)); - - FeedLipSyncInfoIntoReceiver(); - task_runner_->RunTasks(); - - // Enqueue a request for a video frame. - receiver_->GetEncodedVideoFrame( - base::Bind(&FakeVideoClient::DeliverEncodedVideoFrame, - base::Unretained(&fake_video_client_))); - - // The request should not be satisfied since no packets have been received. - task_runner_->RunTasks(); - EXPECT_EQ(0, fake_video_client_.number_times_called()); - - // Deliver one video frame to the receiver and expect to get one frame back. - const base::TimeDelta target_playout_delay = - base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); - fake_video_client_.AddExpectedResult( - kFirstFrameId, testing_clock_->NowTicks() + target_playout_delay); - FeedOneFrameIntoReceiver(); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_video_client_.number_times_called()); - - std::vector<FrameEvent> frame_events; - event_subscriber.GetFrameEventsAndReset(&frame_events); - - ASSERT_TRUE(!frame_events.empty()); - EXPECT_EQ(FRAME_ACK_SENT, frame_events.begin()->type); - EXPECT_EQ(rtp_header_.frame_id, frame_events.begin()->frame_id); - EXPECT_EQ(rtp_header_.rtp_timestamp, frame_events.begin()->rtp_timestamp); - - cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); -} - -TEST_F(VideoReceiverTest, ReceivesFramesRefusingToSkipAny) { - EXPECT_CALL(mock_transport_, SendRtcpPacket(_, _)) - .WillRepeatedly(testing::Return(true)); - - const uint32 rtp_advance_per_frame = - config_.frequency / config_.max_frame_rate; - const base::TimeDelta time_advance_per_frame = - base::TimeDelta::FromSeconds(1) / config_.max_frame_rate; - - // Feed and process lip sync in receiver. - FeedLipSyncInfoIntoReceiver(); - task_runner_->RunTasks(); - const base::TimeTicks first_frame_capture_time = testing_clock_->NowTicks(); - - // Enqueue a request for a video frame. - const FrameEncodedCallback frame_encoded_callback = - base::Bind(&FakeVideoClient::DeliverEncodedVideoFrame, - base::Unretained(&fake_video_client_)); - receiver_->GetEncodedVideoFrame(frame_encoded_callback); - task_runner_->RunTasks(); - EXPECT_EQ(0, fake_video_client_.number_times_called()); - - // Receive one video frame and expect to see the first request satisfied. - const base::TimeDelta target_playout_delay = - base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis); - fake_video_client_.AddExpectedResult( - kFirstFrameId, first_frame_capture_time + target_playout_delay); - rtp_header_.rtp_timestamp = 0; - FeedOneFrameIntoReceiver(); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_video_client_.number_times_called()); - - // Enqueue a second request for a video frame, but it should not be - // fulfilled yet. - receiver_->GetEncodedVideoFrame(frame_encoded_callback); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_video_client_.number_times_called()); - - // Receive one video frame out-of-order: Make sure that we are not continuous - // and that the RTP timestamp represents a time in the future. - rtp_header_.is_key_frame = false; - rtp_header_.frame_id = kFirstFrameId + 2; // "Frame 3" - rtp_header_.reference_frame_id = kFirstFrameId + 1; // "Frame 2" - rtp_header_.rtp_timestamp += 2 * rtp_advance_per_frame; - FeedOneFrameIntoReceiver(); - - // Frame 2 should not come out at this point in time. - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_video_client_.number_times_called()); - - // Enqueue a third request for a video frame. - receiver_->GetEncodedVideoFrame(frame_encoded_callback); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_video_client_.number_times_called()); - - // Now, advance time forward such that Frame 2 is now too late for playback. - // Regardless, the receiver must NOT emit Frame 3 yet because it is not - // allowed to skip frames for VP8. - testing_clock_->Advance(2 * time_advance_per_frame + target_playout_delay); - task_runner_->RunTasks(); - EXPECT_EQ(1, fake_video_client_.number_times_called()); - - // Now receive Frame 2 and expect both the second and third requests to be - // fulfilled immediately. - fake_video_client_.AddExpectedResult( - kFirstFrameId + 1, // "Frame 2" - first_frame_capture_time + 1 * time_advance_per_frame + - target_playout_delay); - fake_video_client_.AddExpectedResult( - kFirstFrameId + 2, // "Frame 3" - first_frame_capture_time + 2 * time_advance_per_frame + - target_playout_delay); - --rtp_header_.frame_id; // "Frame 2" - --rtp_header_.reference_frame_id; // "Frame 1" - rtp_header_.rtp_timestamp -= rtp_advance_per_frame; - FeedOneFrameIntoReceiver(); - task_runner_->RunTasks(); - EXPECT_EQ(3, fake_video_client_.number_times_called()); - - // Move forward to the playout time of an unreceived Frame 5. Expect no - // additional frames were emitted. - testing_clock_->Advance(3 * time_advance_per_frame); - task_runner_->RunTasks(); - EXPECT_EQ(3, fake_video_client_.number_times_called()); -} - -} // namespace cast -} // namespace media diff --git a/media/cast/video_sender/video_sender.cc b/media/cast/video_sender/video_sender.cc index 57b8ea7..2642e79 100644 --- a/media/cast/video_sender/video_sender.cc +++ b/media/cast/video_sender/video_sender.cc @@ -85,7 +85,7 @@ VideoSender::VideoSender( video_config.rtp_config.ssrc, video_config.incoming_feedback_ssrc, video_config.rtcp_c_name, - false)); + VIDEO_EVENT)); rtcp_->SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize); // TODO(pwestin): pass cast_initialization_cb to |video_encoder_| |