summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-03 10:45:13 +0000
committermiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-03 10:45:13 +0000
commita225993ef8292433068e9a3ec7d827fe3d69da8e (patch)
treefee085e128838947d2d71c8203a47d5a081c0f14
parentb5ced6e30cdee5631c76c0ba60ba5b0674ba0e5d (diff)
downloadchromium_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
-rw-r--r--media/cast/audio_receiver/audio_receiver.h210
-rw-r--r--media/cast/audio_receiver/audio_receiver_unittest.cc261
-rw-r--r--media/cast/audio_sender/audio_sender.cc2
-rw-r--r--media/cast/cast.gyp20
-rw-r--r--media/cast/cast_receiver.h42
-rw-r--r--media/cast/cast_receiver_impl.cc150
-rw-r--r--media/cast/cast_receiver_impl.h55
-rw-r--r--media/cast/cast_testing.gypi7
-rw-r--r--media/cast/receiver/audio_decoder.cc (renamed from media/cast/audio_receiver/audio_decoder.cc)18
-rw-r--r--media/cast/receiver/audio_decoder.h (renamed from media/cast/audio_receiver/audio_decoder.h)12
-rw-r--r--media/cast/receiver/audio_decoder_unittest.cc (renamed from media/cast/audio_receiver/audio_decoder_unittest.cc)14
-rw-r--r--media/cast/receiver/cast_receiver_impl.cc232
-rw-r--r--media/cast/receiver/cast_receiver_impl.h122
-rw-r--r--media/cast/receiver/frame_receiver.cc (renamed from media/cast/audio_receiver/audio_receiver.cc)287
-rw-r--r--media/cast/receiver/frame_receiver.h (renamed from media/cast/video_receiver/video_receiver.h)122
-rw-r--r--media/cast/receiver/frame_receiver_unittest.cc419
-rw-r--r--media/cast/receiver/video_decoder.cc (renamed from media/cast/video_receiver/video_decoder.cc)8
-rw-r--r--media/cast/receiver/video_decoder.h (renamed from media/cast/video_receiver/video_decoder.h)10
-rw-r--r--media/cast/receiver/video_decoder_unittest.cc (renamed from media/cast/video_receiver/video_decoder_unittest.cc)9
-rw-r--r--media/cast/rtcp/rtcp.cc14
-rw-r--r--media/cast/rtcp/rtcp.h4
-rw-r--r--media/cast/rtcp/rtcp_unittest.cc22
-rw-r--r--media/cast/rtp_receiver/rtp_parser/rtp_parser.cc1
-rw-r--r--media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc1
-rw-r--r--media/cast/rtp_receiver/rtp_receiver.cc55
-rw-r--r--media/cast/rtp_receiver/rtp_receiver.h53
-rw-r--r--media/cast/test/end2end_unittest.cc29
-rw-r--r--media/cast/test/utility/in_process_receiver.cc4
-rw-r--r--media/cast/video_receiver/video_receiver.cc380
-rw-r--r--media/cast/video_receiver/video_receiver_unittest.cc266
-rw-r--r--media/cast/video_sender/video_sender.cc2
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_|