summaryrefslogtreecommitdiffstats
path: root/chromium/media/cast
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/media/cast
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/media/cast')
-rw-r--r--chromium/media/cast/DEPS2
-rw-r--r--chromium/media/cast/OWNERS1
-rw-r--r--chromium/media/cast/README7
-rw-r--r--chromium/media/cast/audio_receiver/audio_decoder.cc161
-rw-r--r--chromium/media/cast/audio_receiver/audio_decoder.h71
-rw-r--r--chromium/media/cast/audio_receiver/audio_decoder_unittest.cc220
-rw-r--r--chromium/media/cast/audio_receiver/audio_receiver.cc490
-rw-r--r--chromium/media/cast/audio_receiver/audio_receiver.gypi29
-rw-r--r--chromium/media/cast/audio_receiver/audio_receiver.h143
-rw-r--r--chromium/media/cast/audio_receiver/audio_receiver_unittest.cc217
-rw-r--r--chromium/media/cast/audio_sender/audio_encoder.cc300
-rw-r--r--chromium/media/cast/audio_sender/audio_encoder.h34
-rw-r--r--chromium/media/cast/audio_sender/audio_encoder_unittest.cc246
-rw-r--r--chromium/media/cast/audio_sender/audio_sender.cc433
-rw-r--r--chromium/media/cast/audio_sender/audio_sender.gypi32
-rw-r--r--chromium/media/cast/audio_sender/audio_sender.h167
-rw-r--r--chromium/media/cast/audio_sender/audio_sender_unittest.cc137
-rw-r--r--chromium/media/cast/base/clock_drift_smoother.cc58
-rw-r--r--chromium/media/cast/base/clock_drift_smoother.h52
-rw-r--r--chromium/media/cast/cast.gyp315
-rw-r--r--chromium/media/cast/cast_config.cc69
-rw-r--r--chromium/media/cast/cast_config.h230
-rw-r--r--chromium/media/cast/cast_defines.h111
-rw-r--r--chromium/media/cast/cast_environment.cc112
-rw-r--r--chromium/media/cast/cast_environment.h68
-rw-r--r--chromium/media/cast/cast_receiver.gyp33
-rw-r--r--chromium/media/cast/cast_receiver.h100
-rw-r--r--chromium/media/cast/cast_receiver_impl.cc175
-rw-r--r--chromium/media/cast/cast_receiver_impl.h50
-rw-r--r--chromium/media/cast/cast_sender.gyp36
-rw-r--r--chromium/media/cast/cast_sender.h103
-rw-r--r--chromium/media/cast/cast_sender_impl.cc274
-rw-r--r--chromium/media/cast/cast_sender_impl.h57
-rw-r--r--chromium/media/cast/cast_testing.gypi276
-rw-r--r--chromium/media/cast/congestion_control/congestion_control.cc231
-rw-r--r--chromium/media/cast/congestion_control/congestion_control.gypi23
-rw-r--r--chromium/media/cast/congestion_control/congestion_control.h60
-rw-r--r--chromium/media/cast/congestion_control/congestion_control_unittest.cc234
-rw-r--r--chromium/media/cast/framer/cast_message_builder.cc90
-rw-r--r--chromium/media/cast/framer/cast_message_builder.h7
-rw-r--r--chromium/media/cast/framer/cast_message_builder_unittest.cc203
-rw-r--r--chromium/media/cast/framer/frame_buffer.cc70
-rw-r--r--chromium/media/cast/framer/frame_buffer.h10
-rw-r--r--chromium/media/cast/framer/frame_buffer_unittest.cc50
-rw-r--r--chromium/media/cast/framer/frame_id_map.cc129
-rw-r--r--chromium/media/cast/framer/frame_id_map.h15
-rw-r--r--chromium/media/cast/framer/framer.cc85
-rw-r--r--chromium/media/cast/framer/framer.gyp27
-rw-r--r--chromium/media/cast/framer/framer.h27
-rw-r--r--chromium/media/cast/framer/framer_unittest.cc419
-rw-r--r--chromium/media/cast/logging/encoding_event_subscriber.cc286
-rw-r--r--chromium/media/cast/logging/encoding_event_subscriber.h122
-rw-r--r--chromium/media/cast/logging/encoding_event_subscriber_unittest.cc668
-rw-r--r--chromium/media/cast/logging/log_deserializer.cc252
-rw-r--r--chromium/media/cast/logging/log_deserializer.h51
-rw-r--r--chromium/media/cast/logging/log_serializer.cc190
-rw-r--r--chromium/media/cast/logging/log_serializer.h37
-rw-r--r--chromium/media/cast/logging/logging_defines.cc110
-rw-r--r--chromium/media/cast/logging/logging_defines.h142
-rw-r--r--chromium/media/cast/logging/logging_impl.cc272
-rw-r--r--chromium/media/cast/logging/logging_impl.h89
-rw-r--r--chromium/media/cast/logging/logging_impl_unittest.cc234
-rw-r--r--chromium/media/cast/logging/logging_internal.cc79
-rw-r--r--chromium/media/cast/logging/logging_internal.h95
-rw-r--r--chromium/media/cast/logging/logging_raw.cc173
-rw-r--r--chromium/media/cast/logging/logging_raw.h94
-rw-r--r--chromium/media/cast/logging/logging_raw_unittest.cc196
-rw-r--r--chromium/media/cast/logging/logging_stats.cc150
-rw-r--r--chromium/media/cast/logging/logging_stats.h75
-rw-r--r--chromium/media/cast/logging/logging_unittest.cc248
-rw-r--r--chromium/media/cast/logging/proto/proto_utils.cc36
-rw-r--r--chromium/media/cast/logging/proto/proto_utils.h21
-rw-r--r--chromium/media/cast/logging/proto/raw_events.proto149
-rw-r--r--chromium/media/cast/logging/raw_event_subscriber.h32
-rw-r--r--chromium/media/cast/logging/raw_event_subscriber_bundle.cc99
-rw-r--r--chromium/media/cast/logging/raw_event_subscriber_bundle.h84
-rw-r--r--chromium/media/cast/logging/receiver_time_offset_estimator.h39
-rw-r--r--chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc129
-rw-r--r--chromium/media/cast/logging/receiver_time_offset_estimator_impl.h64
-rw-r--r--chromium/media/cast/logging/receiver_time_offset_estimator_impl_unittest.cc242
-rw-r--r--chromium/media/cast/logging/serialize_deserialize_test.cc214
-rw-r--r--chromium/media/cast/logging/simple_event_subscriber.cc46
-rw-r--r--chromium/media/cast/logging/simple_event_subscriber.h52
-rw-r--r--chromium/media/cast/logging/simple_event_subscriber_unittest.cc87
-rw-r--r--chromium/media/cast/logging/stats_event_subscriber.cc400
-rw-r--r--chromium/media/cast/logging/stats_event_subscriber.h176
-rw-r--r--chromium/media/cast/logging/stats_event_subscriber_unittest.cc401
-rw-r--r--chromium/media/cast/net/cast_net_defines.h81
-rw-r--r--chromium/media/cast/net/pacing/mock_paced_packet_sender.h27
-rw-r--r--chromium/media/cast/net/pacing/paced_sender.cc148
-rw-r--r--chromium/media/cast/net/pacing/paced_sender.gyp22
-rw-r--r--chromium/media/cast/net/pacing/paced_sender.h83
-rw-r--r--chromium/media/cast/net/pacing/paced_sender_unittest.cc257
-rw-r--r--chromium/media/cast/net/rtp_sender/mock_rtp_sender.h34
-rw-r--r--chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.cc174
-rw-r--r--chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.gyp23
-rw-r--r--chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.h55
-rw-r--r--chromium/media/cast/net/rtp_sender/packet_storage/packet_storage_unittest.cc110
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.cc153
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.gyp27
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.h73
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.cc21
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h39
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc153
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_sender.cc145
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_sender.gyp26
-rw-r--r--chromium/media/cast/net/rtp_sender/rtp_sender.h66
-rw-r--r--chromium/media/cast/receiver/audio_decoder.cc246
-rw-r--r--chromium/media/cast/receiver/audio_decoder.h64
-rw-r--r--chromium/media/cast/receiver/audio_decoder_unittest.cc241
-rw-r--r--chromium/media/cast/receiver/cast_receiver_impl.cc232
-rw-r--r--chromium/media/cast/receiver/cast_receiver_impl.h122
-rw-r--r--chromium/media/cast/receiver/frame_receiver.cc326
-rw-r--r--chromium/media/cast/receiver/frame_receiver.h184
-rw-r--r--chromium/media/cast/receiver/frame_receiver_unittest.cc419
-rw-r--r--chromium/media/cast/receiver/video_decoder.cc259
-rw-r--r--chromium/media/cast/receiver/video_decoder.h63
-rw-r--r--chromium/media/cast/receiver/video_decoder_unittest.cc183
-rw-r--r--chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.cc12
-rw-r--r--chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.h6
-rw-r--r--chromium/media/cast/rtcp/mock_rtcp_sender_feedback.cc6
-rw-r--r--chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.cc96
-rw-r--r--chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.h79
-rw-r--r--chromium/media/cast/rtcp/receiver_rtcp_event_subscriber_unittest.cc131
-rw-r--r--chromium/media/cast/rtcp/rtcp.cc433
-rw-r--r--chromium/media/cast/rtcp/rtcp.gyp46
-rw-r--r--chromium/media/cast/rtcp/rtcp.h132
-rw-r--r--chromium/media/cast/rtcp/rtcp_defines.cc49
-rw-r--r--chromium/media/cast/rtcp/rtcp_defines.h118
-rw-r--r--chromium/media/cast/rtcp/rtcp_receiver.cc197
-rw-r--r--chromium/media/cast/rtcp/rtcp_receiver.h26
-rw-r--r--chromium/media/cast/rtcp/rtcp_receiver_unittest.cc256
-rw-r--r--chromium/media/cast/rtcp/rtcp_sender.cc798
-rw-r--r--chromium/media/cast/rtcp/rtcp_sender.h115
-rw-r--r--chromium/media/cast/rtcp/rtcp_sender_unittest.cc616
-rw-r--r--chromium/media/cast/rtcp/rtcp_unittest.cc509
-rw-r--r--chromium/media/cast/rtcp/rtcp_utility.cc289
-rw-r--r--chromium/media/cast/rtcp/rtcp_utility.h101
-rw-r--r--chromium/media/cast/rtcp/test_rtcp_packet_builder.cc62
-rw-r--r--chromium/media/cast/rtcp/test_rtcp_packet_builder.h30
-rw-r--r--chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.cc6
-rw-r--r--chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.h3
-rw-r--r--chromium/media/cast/rtp_receiver/receiver_stats.cc25
-rw-r--r--chromium/media/cast/rtp_receiver/receiver_stats.h15
-rw-r--r--chromium/media/cast/rtp_receiver/receiver_stats_unittest.cc94
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h16
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc173
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.gyp23
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.h57
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc231
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_receiver.cc70
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_receiver.gyp26
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_receiver.h56
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_receiver_defines.cc25
-rw-r--r--chromium/media/cast/rtp_receiver/rtp_receiver_defines.h26
-rw-r--r--chromium/media/cast/rtp_timestamp_helper.cc36
-rw-r--r--chromium/media/cast/rtp_timestamp_helper.h41
-rw-r--r--chromium/media/cast/test/transport/transport.gyp22
-rw-r--r--chromium/media/cast/test/utility/utility.gyp28
-rw-r--r--chromium/media/cast/transport/cast_transport_config.cc82
-rw-r--r--chromium/media/cast/transport/cast_transport_config.h221
-rw-r--r--chromium/media/cast/transport/cast_transport_defines.h169
-rw-r--r--chromium/media/cast/transport/cast_transport_sender.h113
-rw-r--r--chromium/media/cast/transport/cast_transport_sender_impl.cc212
-rw-r--r--chromium/media/cast/transport/cast_transport_sender_impl.h110
-rw-r--r--chromium/media/cast/transport/cast_transport_sender_impl_unittest.cc113
-rw-r--r--chromium/media/cast/transport/frame_id_wrap_helper_test.cc (renamed from chromium/media/cast/net/frame_id_wrap_helper_test.cc)14
-rw-r--r--chromium/media/cast/transport/pacing/mock_paced_packet_sender.cc (renamed from chromium/media/cast/net/pacing/mock_paced_packet_sender.cc)10
-rw-r--r--chromium/media/cast/transport/pacing/mock_paced_packet_sender.h31
-rw-r--r--chromium/media/cast/transport/pacing/paced_sender.cc260
-rw-r--r--chromium/media/cast/transport/pacing/paced_sender.h147
-rw-r--r--chromium/media/cast/transport/pacing/paced_sender_unittest.cc351
-rw-r--r--chromium/media/cast/transport/rtcp/rtcp_builder.cc197
-rw-r--r--chromium/media/cast/transport/rtcp/rtcp_builder.h49
-rw-r--r--chromium/media/cast/transport/rtcp/rtcp_builder_unittest.cc164
-rw-r--r--chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.cc65
-rw-r--r--chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.h62
-rw-r--r--chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage_unittest.cc115
-rw-r--r--chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.cc137
-rw-r--r--chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h86
-rw-r--r--chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc175
-rw-r--r--chromium/media/cast/transport/rtp_sender/rtp_sender.cc150
-rw-r--r--chromium/media/cast/transport/rtp_sender/rtp_sender.h85
-rw-r--r--chromium/media/cast/transport/transport/udp_transport.cc242
-rw-r--r--chromium/media/cast/transport/transport/udp_transport.h97
-rw-r--r--chromium/media/cast/transport/transport/udp_transport_unittest.cc100
-rw-r--r--chromium/media/cast/transport/utility/transport_encryption_handler.cc76
-rw-r--r--chromium/media/cast/transport/utility/transport_encryption_handler.h58
-rw-r--r--chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.cc106
-rw-r--r--chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.gyp25
-rw-r--r--chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.h46
-rw-r--r--chromium/media/cast/video_receiver/video_decoder.cc44
-rw-r--r--chromium/media/cast/video_receiver/video_decoder.h43
-rw-r--r--chromium/media/cast/video_receiver/video_decoder_unittest.cc94
-rw-r--r--chromium/media/cast/video_receiver/video_receiver.cc465
-rw-r--r--chromium/media/cast/video_receiver/video_receiver.gypi31
-rw-r--r--chromium/media/cast/video_receiver/video_receiver.h133
-rw-r--r--chromium/media/cast/video_receiver/video_receiver_unittest.cc169
-rw-r--r--chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.cc193
-rw-r--r--chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.gypi20
-rw-r--r--chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.h34
-rw-r--r--chromium/media/cast/video_sender/external_video_encoder.cc436
-rw-r--r--chromium/media/cast/video_sender/external_video_encoder.h86
-rw-r--r--chromium/media/cast/video_sender/external_video_encoder_unittest.cc191
-rw-r--r--chromium/media/cast/video_sender/fake_software_video_encoder.cc69
-rw-r--r--chromium/media/cast/video_sender/fake_software_video_encoder.h38
-rw-r--r--chromium/media/cast/video_sender/mock_video_encoder_controller.cc17
-rw-r--r--chromium/media/cast/video_sender/mock_video_encoder_controller.h34
-rw-r--r--chromium/media/cast/video_sender/software_video_encoder.h46
-rw-r--r--chromium/media/cast/video_sender/video_encoder.cc123
-rw-r--r--chromium/media/cast/video_sender/video_encoder.h66
-rw-r--r--chromium/media/cast/video_sender/video_encoder_impl.cc139
-rw-r--r--chromium/media/cast/video_sender/video_encoder_impl.h72
-rw-r--r--chromium/media/cast/video_sender/video_encoder_impl_unittest.cc260
-rw-r--r--chromium/media/cast/video_sender/video_encoder_unittest.cc247
-rw-r--r--chromium/media/cast/video_sender/video_sender.cc632
-rw-r--r--chromium/media/cast/video_sender/video_sender.gypi34
-rw-r--r--chromium/media/cast/video_sender/video_sender.h198
-rw-r--r--chromium/media/cast/video_sender/video_sender_unittest.cc483
219 files changed, 18393 insertions, 11331 deletions
diff --git a/chromium/media/cast/DEPS b/chromium/media/cast/DEPS
index f84b3fbbf5e..abee2864b31 100644
--- a/chromium/media/cast/DEPS
+++ b/chromium/media/cast/DEPS
@@ -2,7 +2,7 @@ include_rules = [
"+crypto",
"+media",
"+net",
- "+third_party/webrtc",
"+third_party/libyuv",
+ "+third_party/zlib",
"+ui/gfx",
]
diff --git a/chromium/media/cast/OWNERS b/chromium/media/cast/OWNERS
index 49f41be49c0..f8e61c33ccc 100644
--- a/chromium/media/cast/OWNERS
+++ b/chromium/media/cast/OWNERS
@@ -1,4 +1,5 @@
hclam@chromium.org
hubbe@chromium.org
mikhal@chromium.org
+miu@chromium.org
pwestin@google.com
diff --git a/chromium/media/cast/README b/chromium/media/cast/README
index eca4cf6a1f9..0930c1e012d 100644
--- a/chromium/media/cast/README
+++ b/chromium/media/cast/README
@@ -16,7 +16,7 @@ cast/audio_sender/
cast/congestion_control/
Bandwidth estimation and network congestion handling.
-cast/net/pacing/
+cast/transport/pacing/
Module for rate limiting data outflow.
cast/rtcp/
@@ -28,7 +28,7 @@ cast/rtp_common/
cast/rtp_receiver/
Module for reciving RTP messages.
-cast/net/rtp_sender/
+cast/transport/rtp_sender/
Module for sending RTP messages.
cast/test/
@@ -56,9 +56,6 @@ third_party/libvpx
third_party/opus
Provides audio encoder.
-third_party/webrtc
- Provides audio signal processing.
-
OWNERS
See OWNERS for ownership.
diff --git a/chromium/media/cast/audio_receiver/audio_decoder.cc b/chromium/media/cast/audio_receiver/audio_decoder.cc
deleted file mode 100644
index a761a5a84de..00000000000
--- a/chromium/media/cast/audio_receiver/audio_decoder.cc
+++ /dev/null
@@ -1,161 +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 "base/logging.h"
-#include "media/cast/audio_receiver/audio_decoder.h"
-
-#include "third_party/webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
-#include "third_party/webrtc/modules/interface/module_common_types.h"
-
-namespace media {
-namespace cast {
-
-AudioDecoder::AudioDecoder(scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- RtpPayloadFeedback* incoming_payload_feedback)
- : cast_environment_(cast_environment),
- audio_decoder_(webrtc::AudioCodingModule::Create(0)),
- cast_message_builder_(cast_environment->Clock(),
- incoming_payload_feedback, &frame_id_map_, audio_config.incoming_ssrc,
- true, 0),
- have_received_packets_(false),
- last_played_out_timestamp_(0) {
- audio_decoder_->InitializeReceiver();
-
- webrtc::CodecInst receive_codec;
- switch (audio_config.codec) {
- case kPcm16:
- receive_codec.pltype = audio_config.rtp_payload_type;
- strncpy(receive_codec.plname, "L16", 4);
- receive_codec.plfreq = audio_config.frequency;
- receive_codec.pacsize = -1;
- receive_codec.channels = audio_config.channels;
- receive_codec.rate = -1;
- break;
- case kOpus:
- receive_codec.pltype = audio_config.rtp_payload_type;
- strncpy(receive_codec.plname, "opus", 5);
- receive_codec.plfreq = audio_config.frequency;
- receive_codec.pacsize = -1;
- receive_codec.channels = audio_config.channels;
- receive_codec.rate = -1;
- break;
- case kExternalAudio:
- NOTREACHED() << "Codec must be specified for audio decoder";
- break;
- }
- if (audio_decoder_->RegisterReceiveCodec(receive_codec) != 0) {
- NOTREACHED() << "Failed to register receive codec";
- }
-
- audio_decoder_->SetMaximumPlayoutDelay(audio_config.rtp_max_delay_ms);
- audio_decoder_->SetPlayoutMode(webrtc::streaming);
-}
-
-AudioDecoder::~AudioDecoder() {}
-
-bool AudioDecoder::GetRawAudioFrame(int number_of_10ms_blocks,
- int desired_frequency,
- PcmAudioFrame* audio_frame,
- uint32* rtp_timestamp) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::AUDIO_DECODER));
- // We don't care about the race case where a packet arrives at the same time
- // as this function in called. The data will be there the next time this
- // function is called.
- lock_.Acquire();
- // Get a local copy under lock.
- bool have_received_packets = have_received_packets_;
- lock_.Release();
-
- if (!have_received_packets) return false;
-
- audio_frame->samples.clear();
-
- for (int i = 0; i < number_of_10ms_blocks; ++i) {
- webrtc::AudioFrame webrtc_audio_frame;
- if (0 != audio_decoder_->PlayoutData10Ms(desired_frequency,
- &webrtc_audio_frame)) {
- return false;
- }
- if (webrtc_audio_frame.speech_type_ == webrtc::AudioFrame::kPLCCNG ||
- webrtc_audio_frame.speech_type_ == webrtc::AudioFrame::kUndefined) {
- // We are only interested in real decoded audio.
- return false;
- }
- audio_frame->frequency = webrtc_audio_frame.sample_rate_hz_;
- audio_frame->channels = webrtc_audio_frame.num_channels_;
-
- if (i == 0) {
- // Use the timestamp from the first 10ms block.
- if (0 != audio_decoder_->PlayoutTimestamp(rtp_timestamp)) {
- return false;
- }
- lock_.Acquire();
- last_played_out_timestamp_ = *rtp_timestamp;
- lock_.Release();
- }
- int samples_per_10ms = webrtc_audio_frame.samples_per_channel_;
-
- audio_frame->samples.insert(
- audio_frame->samples.end(),
- &webrtc_audio_frame.data_[0],
- &webrtc_audio_frame.data_[samples_per_10ms * audio_frame->channels]);
- }
- return true;
-}
-
-void AudioDecoder::IncomingParsedRtpPacket(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader& rtp_header) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK_LE(payload_size, kIpPacketSize);
- audio_decoder_->IncomingPacket(payload_data, static_cast<int32>(payload_size),
- rtp_header.webrtc);
- lock_.Acquire();
- have_received_packets_ = true;
- uint32 last_played_out_timestamp = last_played_out_timestamp_;
- lock_.Release();
-
- bool complete = false;
- if (!frame_id_map_.InsertPacket(rtp_header, &complete)) return;
- if (!complete) return;
-
- cast_message_builder_.CompleteFrameReceived(rtp_header.frame_id,
- rtp_header.is_key_frame);
-
- frame_id_rtp_timestamp_map_[rtp_header.frame_id] =
- rtp_header.webrtc.header.timestamp;
-
- if (last_played_out_timestamp == 0) return; // Nothing is played out yet.
-
- uint32 latest_frame_id_to_remove = 0;
- bool frame_to_remove = false;
-
- FrameIdRtpTimestampMap::iterator it = frame_id_rtp_timestamp_map_.begin();
- while (it != frame_id_rtp_timestamp_map_.end()) {
- if (IsNewerRtpTimestamp(it->second, last_played_out_timestamp)) {
- break;
- }
- frame_to_remove = true;
- latest_frame_id_to_remove = it->first;
- frame_id_rtp_timestamp_map_.erase(it);
- it = frame_id_rtp_timestamp_map_.begin();
- }
- if (!frame_to_remove) return;
-
- frame_id_map_.RemoveOldFrames(latest_frame_id_to_remove);
-}
-
-bool AudioDecoder::TimeToSendNextCastMessage(base::TimeTicks* time_to_send) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- return cast_message_builder_.TimeToSendNextCastMessage(time_to_send);
-}
-
-void AudioDecoder::SendCastMessage() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- cast_message_builder_.UpdateCastMessage();
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/audio_receiver/audio_decoder.h b/chromium/media/cast/audio_receiver/audio_decoder.h
deleted file mode 100644
index 8a77d79d070..00000000000
--- a/chromium/media/cast/audio_receiver/audio_decoder.h
+++ /dev/null
@@ -1,71 +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_DECODER_H_
-#define MEDIA_CAST_AUDIO_RECEIVER_AUDIO_DECODER_H_
-
-#include "base/callback.h"
-#include "base/synchronization/lock.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/framer/cast_message_builder.h"
-#include "media/cast/framer/frame_id_map.h"
-#include "media/cast/rtp_receiver/rtp_receiver_defines.h"
-
-namespace webrtc {
-class AudioCodingModule;
-}
-
-namespace media {
-namespace cast {
-
-typedef std::map<uint32, uint32> FrameIdRtpTimestampMap;
-
-// Thread safe class.
-class AudioDecoder {
- public:
- AudioDecoder(scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- RtpPayloadFeedback* incoming_payload_feedback);
- virtual ~AudioDecoder();
-
- // Extract a raw audio frame from the decoder.
- // Set the number of desired 10ms blocks and frequency.
- // Should be called from the cast audio decoder thread; however that is not
- // required.
- bool GetRawAudioFrame(int number_of_10ms_blocks,
- int desired_frequency,
- PcmAudioFrame* audio_frame,
- uint32* rtp_timestamp);
-
- // Insert an RTP packet to the decoder.
- // Should be called from the main cast thread; however that is not required.
- void IncomingParsedRtpPacket(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader& rtp_header);
-
- bool TimeToSendNextCastMessage(base::TimeTicks* time_to_send);
- void SendCastMessage();
-
- private:
- scoped_refptr<CastEnvironment> cast_environment_;
-
- // The webrtc AudioCodingModule is threadsafe.
- scoped_ptr<webrtc::AudioCodingModule> audio_decoder_;
-
- FrameIdMap frame_id_map_;
- CastMessageBuilder cast_message_builder_;
-
- base::Lock lock_;
- bool have_received_packets_;
- FrameIdRtpTimestampMap frame_id_rtp_timestamp_map_;
- uint32 last_played_out_timestamp_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioDecoder);
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_AUDIO_RECEIVER_AUDIO_DECODER_H_
diff --git a/chromium/media/cast/audio_receiver/audio_decoder_unittest.cc b/chromium/media/cast/audio_receiver/audio_decoder_unittest.cc
deleted file mode 100644
index 04df4728bd9..00000000000
--- a/chromium/media/cast/audio_receiver/audio_decoder_unittest.cc
+++ /dev/null
@@ -1,220 +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 "base/test/simple_test_tick_clock.h"
-#include "media/cast/audio_receiver/audio_decoder.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-namespace {
-class TestRtpPayloadFeedback : public RtpPayloadFeedback {
- public:
- TestRtpPayloadFeedback() {}
- virtual ~TestRtpPayloadFeedback() {}
-
- virtual void CastFeedback(const RtcpCastMessage& cast_feedback) OVERRIDE {
- EXPECT_EQ(1u, cast_feedback.ack_frame_id_);
- EXPECT_EQ(0u, cast_feedback.missing_frames_and_packets_.size());
- }
-};
-} // namespace.
-
-class AudioDecoderTest : public ::testing::Test {
- protected:
- AudioDecoderTest() {
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(1234));
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
- }
- virtual ~AudioDecoderTest() {}
-
- void Configure(const AudioReceiverConfig& audio_config) {
- audio_decoder_.reset(
- new AudioDecoder(cast_environment_, audio_config, &cast_feedback_));
- }
-
- TestRtpPayloadFeedback cast_feedback_;
- base::SimpleTestTickClock testing_clock_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_ptr<AudioDecoder> audio_decoder_;
-};
-
-TEST_F(AudioDecoderTest, Pcm16MonoNoResampleOnePacket) {
- AudioReceiverConfig audio_config;
- audio_config.rtp_payload_type = 127;
- audio_config.frequency = 16000;
- audio_config.channels = 1;
- audio_config.codec = kPcm16;
- audio_config.use_external_decoder = false;
- Configure(audio_config);
-
- RtpCastHeader rtp_header;
- rtp_header.webrtc.header.payloadType = 127;
- rtp_header.webrtc.header.sequenceNumber = 1234;
- rtp_header.webrtc.header.timestamp = 0x87654321;
- rtp_header.webrtc.header.ssrc = 0x12345678;
- rtp_header.webrtc.header.paddingLength = 0;
- rtp_header.webrtc.header.headerLength = 12;
- rtp_header.webrtc.type.Audio.channel = 1;
- rtp_header.webrtc.type.Audio.isCNG = false;
-
- std::vector<int16> payload(640, 0x1234);
- int number_of_10ms_blocks = 4;
- int desired_frequency = 16000;
- PcmAudioFrame audio_frame;
- uint32 rtp_timestamp;
-
- EXPECT_FALSE(audio_decoder_->GetRawAudioFrame(number_of_10ms_blocks,
- desired_frequency,
- &audio_frame,
- &rtp_timestamp));
-
- uint8* payload_data = reinterpret_cast<uint8*>(&payload[0]);
- size_t payload_size = payload.size() * sizeof(int16);
-
- audio_decoder_->IncomingParsedRtpPacket(payload_data,
- payload_size, rtp_header);
-
- EXPECT_TRUE(audio_decoder_->GetRawAudioFrame(number_of_10ms_blocks,
- desired_frequency,
- &audio_frame,
- &rtp_timestamp));
- EXPECT_EQ(1, audio_frame.channels);
- EXPECT_EQ(16000, audio_frame.frequency);
- EXPECT_EQ(640ul, audio_frame.samples.size());
- // First 10 samples per channel are 0 from NetEq.
- for (size_t i = 10; i < audio_frame.samples.size(); ++i) {
- EXPECT_EQ(0x3412, audio_frame.samples[i]);
- }
-}
-
-TEST_F(AudioDecoderTest, Pcm16StereoNoResampleTwoPackets) {
- AudioReceiverConfig audio_config;
- audio_config.rtp_payload_type = 127;
- audio_config.frequency = 16000;
- audio_config.channels = 2;
- audio_config.codec = kPcm16;
- audio_config.use_external_decoder = false;
- Configure(audio_config);
-
- RtpCastHeader rtp_header;
- rtp_header.frame_id = 0;
- rtp_header.webrtc.header.payloadType = 127;
- rtp_header.webrtc.header.sequenceNumber = 1234;
- rtp_header.webrtc.header.timestamp = 0x87654321;
- rtp_header.webrtc.header.ssrc = 0x12345678;
- rtp_header.webrtc.header.paddingLength = 0;
- rtp_header.webrtc.header.headerLength = 12;
-
- rtp_header.webrtc.type.Audio.isCNG = false;
- rtp_header.webrtc.type.Audio.channel = 2;
-
- std::vector<int16> payload(640, 0x1234);
-
- uint8* payload_data = reinterpret_cast<uint8*>(&payload[0]);
- size_t payload_size = payload.size() * sizeof(int16);
-
- audio_decoder_->IncomingParsedRtpPacket(payload_data,
- payload_size, rtp_header);
-
- int number_of_10ms_blocks = 2;
- int desired_frequency = 16000;
- PcmAudioFrame audio_frame;
- uint32 rtp_timestamp;
-
- EXPECT_TRUE(audio_decoder_->GetRawAudioFrame(number_of_10ms_blocks,
- desired_frequency,
- &audio_frame,
- &rtp_timestamp));
- EXPECT_EQ(2, audio_frame.channels);
- EXPECT_EQ(16000, audio_frame.frequency);
- EXPECT_EQ(640ul, audio_frame.samples.size());
- // First 10 samples per channel are 0 from NetEq.
- for (size_t i = 10 * audio_config.channels; i < audio_frame.samples.size();
- ++i) {
- EXPECT_EQ(0x3412, audio_frame.samples[i]);
- }
-
- rtp_header.frame_id++;
- rtp_header.webrtc.header.sequenceNumber++;
- rtp_header.webrtc.header.timestamp += (audio_config.frequency / 100) * 2 * 2;
-
- audio_decoder_->IncomingParsedRtpPacket(payload_data,
- payload_size, rtp_header);
-
- EXPECT_TRUE(audio_decoder_->GetRawAudioFrame(number_of_10ms_blocks,
- desired_frequency,
- &audio_frame,
- &rtp_timestamp));
- EXPECT_EQ(2, audio_frame.channels);
- EXPECT_EQ(16000, audio_frame.frequency);
- EXPECT_EQ(640ul, audio_frame.samples.size());
- for (size_t i = 0; i < audio_frame.samples.size(); ++i) {
- EXPECT_NEAR(0x3412, audio_frame.samples[i], 1000);
- }
- // Test cast callback.
- audio_decoder_->SendCastMessage();
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(33));
- audio_decoder_->SendCastMessage();
-}
-
-TEST_F(AudioDecoderTest, Pcm16Resample) {
- AudioReceiverConfig audio_config;
- audio_config.rtp_payload_type = 127;
- audio_config.frequency = 16000;
- audio_config.channels = 2;
- audio_config.codec = kPcm16;
- audio_config.use_external_decoder = false;
- Configure(audio_config);
-
- RtpCastHeader rtp_header;
- rtp_header.webrtc.header.payloadType = 127;
- rtp_header.webrtc.header.sequenceNumber = 1234;
- rtp_header.webrtc.header.timestamp = 0x87654321;
- rtp_header.webrtc.header.ssrc = 0x12345678;
- rtp_header.webrtc.header.paddingLength = 0;
- rtp_header.webrtc.header.headerLength = 12;
-
- rtp_header.webrtc.type.Audio.isCNG = false;
- rtp_header.webrtc.type.Audio.channel = 2;
-
- std::vector<int16> payload(640, 0x1234);
-
- uint8* payload_data = reinterpret_cast<uint8*>(&payload[0]);
- size_t payload_size = payload.size() * sizeof(int16);
-
- audio_decoder_->IncomingParsedRtpPacket(payload_data,
- payload_size, rtp_header);
-
- int number_of_10ms_blocks = 2;
- int desired_frequency = 48000;
- PcmAudioFrame audio_frame;
- uint32 rtp_timestamp;
-
- EXPECT_TRUE(audio_decoder_->GetRawAudioFrame(number_of_10ms_blocks,
- desired_frequency,
- &audio_frame,
- &rtp_timestamp));
-
- EXPECT_EQ(2, audio_frame.channels);
- EXPECT_EQ(48000, audio_frame.frequency);
- EXPECT_EQ(1920ul, audio_frame.samples.size()); // Upsampled to 48 KHz.
- int count = 0;
- // Resampling makes the variance worse.
- for (size_t i = 100 * audio_config.channels; i < audio_frame.samples.size();
- ++i) {
- EXPECT_NEAR(0x3412, audio_frame.samples[i], 400);
- if (0x3412 == audio_frame.samples[i]) count++;
- }
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/audio_receiver/audio_receiver.cc b/chromium/media/cast/audio_receiver/audio_receiver.cc
deleted file mode 100644
index 5aad22f628c..00000000000
--- a/chromium/media/cast/audio_receiver/audio_receiver.cc
+++ /dev/null
@@ -1,490 +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/audio_receiver/audio_receiver.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "crypto/encryptor.h"
-#include "crypto/symmetric_key.h"
-#include "media/cast/audio_receiver/audio_decoder.h"
-#include "media/cast/framer/framer.h"
-#include "media/cast/rtcp/rtcp.h"
-#include "media/cast/rtp_receiver/rtp_receiver.h"
-
-// Max time we wait until an audio frame is due to be played out is released.
-static const int64 kMaxAudioFrameWaitMs = 20;
-static const int64 kMinSchedulingDelayMs = 1;
-
-namespace media {
-namespace cast {
-
-DecodedAudioCallbackData::DecodedAudioCallbackData()
- : number_of_10ms_blocks(0),
- desired_frequency(0),
- callback() {}
-
-DecodedAudioCallbackData::~DecodedAudioCallbackData() {}
-
-// Local implementation of RtpData (defined in rtp_rtcp_defines.h).
-// Used to pass payload data into the audio receiver.
-class LocalRtpAudioData : public RtpData {
- public:
- explicit LocalRtpAudioData(AudioReceiver* audio_receiver)
- : audio_receiver_(audio_receiver) {}
-
- virtual void OnReceivedPayloadData(
- const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader* rtp_header) OVERRIDE {
- audio_receiver_->IncomingParsedRtpPacket(payload_data, payload_size,
- *rtp_header);
- }
-
- private:
- AudioReceiver* audio_receiver_;
-};
-
-// Local implementation of RtpPayloadFeedback (defined in rtp_defines.h)
-// Used to convey cast-specific feedback from receiver to sender.
-class LocalRtpAudioFeedback : public RtpPayloadFeedback {
- public:
- explicit LocalRtpAudioFeedback(AudioReceiver* audio_receiver)
- : audio_receiver_(audio_receiver) {
- }
-
- virtual void CastFeedback(const RtcpCastMessage& cast_message) OVERRIDE {
- audio_receiver_->CastFeedback(cast_message);
- }
-
- private:
- AudioReceiver* audio_receiver_;
-};
-
-class LocalRtpReceiverStatistics : public RtpReceiverStatistics {
- public:
- explicit LocalRtpReceiverStatistics(RtpReceiver* rtp_receiver)
- : rtp_receiver_(rtp_receiver) {
- }
-
- virtual void GetStatistics(uint8* fraction_lost,
- uint32* cumulative_lost, // 24 bits valid.
- uint32* extended_high_sequence_number,
- uint32* jitter) OVERRIDE {
- rtp_receiver_->GetStatistics(fraction_lost,
- cumulative_lost,
- extended_high_sequence_number,
- jitter);
- }
-
- private:
- RtpReceiver* rtp_receiver_;
-};
-
-AudioReceiver::AudioReceiver(scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- PacedPacketSender* const packet_sender)
- : cast_environment_(cast_environment),
- codec_(audio_config.codec),
- frequency_(audio_config.frequency),
- audio_buffer_(),
- audio_decoder_(),
- time_offset_(),
- weak_factory_(this) {
- target_delay_delta_ =
- base::TimeDelta::FromMilliseconds(audio_config.rtp_max_delay_ms);
- incoming_payload_callback_.reset(new LocalRtpAudioData(this));
- incoming_payload_feedback_.reset(new LocalRtpAudioFeedback(this));
- if (audio_config.use_external_decoder) {
- audio_buffer_.reset(new Framer(cast_environment->Clock(),
- incoming_payload_feedback_.get(),
- audio_config.incoming_ssrc,
- true,
- 0));
- } else {
- audio_decoder_.reset(new AudioDecoder(cast_environment,
- audio_config,
- incoming_payload_feedback_.get()));
- }
- if (audio_config.aes_iv_mask.size() == kAesKeySize &&
- audio_config.aes_key.size() == kAesKeySize) {
- iv_mask_ = audio_config.aes_iv_mask;
- crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
- crypto::SymmetricKey::AES, audio_config.aes_key);
- decryptor_.reset(new crypto::Encryptor());
- decryptor_->Init(key, crypto::Encryptor::CTR, std::string());
- } else if (audio_config.aes_iv_mask.size() != 0 ||
- audio_config.aes_key.size() != 0) {
- DCHECK(false) << "Invalid crypto configuration";
- }
-
- rtp_receiver_.reset(new RtpReceiver(cast_environment->Clock(),
- &audio_config,
- NULL,
- incoming_payload_callback_.get()));
- rtp_audio_receiver_statistics_.reset(
- new LocalRtpReceiverStatistics(rtp_receiver_.get()));
- base::TimeDelta rtcp_interval_delta =
- base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval);
- rtcp_.reset(new Rtcp(cast_environment,
- NULL,
- packet_sender,
- NULL,
- rtp_audio_receiver_statistics_.get(),
- audio_config.rtcp_mode,
- rtcp_interval_delta,
- audio_config.feedback_ssrc,
- audio_config.incoming_ssrc,
- audio_config.rtcp_c_name));
-}
-
-AudioReceiver::~AudioReceiver() {}
-
-void AudioReceiver::InitializeTimers() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- ScheduleNextRtcpReport();
- ScheduleNextCastMessage();
-}
-
-void AudioReceiver::IncomingParsedRtpPacket(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader& rtp_header) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- cast_environment_->Logging()->InsertPacketEvent(kPacketReceived,
- rtp_header.webrtc.header.timestamp, rtp_header.frame_id,
- rtp_header.packet_id, rtp_header.max_packet_id, payload_size);
-
- // TODO(pwestin): update this as video to refresh over time.
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (time_first_incoming_packet_.is_null()) {
- InitializeTimers();
- first_incoming_rtp_timestamp_ = rtp_header.webrtc.header.timestamp;
- time_first_incoming_packet_ = cast_environment_->Clock()->NowTicks();
- }
-
- if (audio_decoder_) {
- DCHECK(!audio_buffer_) << "Invalid internal state";
- std::string plaintext(reinterpret_cast<const char*>(payload_data),
- payload_size);
- if (decryptor_) {
- plaintext.clear();
- if (!decryptor_->SetCounter(GetAesNonce(rtp_header.frame_id, iv_mask_))) {
- NOTREACHED() << "Failed to set counter";
- return;
- }
- if (!decryptor_->Decrypt(base::StringPiece(reinterpret_cast<const char*>(
- payload_data), payload_size), &plaintext)) {
- VLOG(0) << "Decryption error";
- return;
- }
- }
- audio_decoder_->IncomingParsedRtpPacket(
- reinterpret_cast<const uint8*>(plaintext.data()), plaintext.size(),
- rtp_header);
- if (!queued_decoded_callbacks_.empty()) {
- DecodedAudioCallbackData decoded_data = queued_decoded_callbacks_.front();
- queued_decoded_callbacks_.pop_front();
- cast_environment_->PostTask(CastEnvironment::AUDIO_DECODER, FROM_HERE,
- base::Bind(&AudioReceiver::DecodeAudioFrameThread,
- base::Unretained(this),
- decoded_data.number_of_10ms_blocks,
- decoded_data.desired_frequency,
- decoded_data.callback));
- }
- return;
- }
-
- DCHECK(audio_buffer_) << "Invalid internal state";
- DCHECK(!audio_decoder_) << "Invalid internal state";
-
- bool complete = audio_buffer_->InsertPacket(payload_data, payload_size,
- rtp_header);
- if (!complete) return; // Audio frame not complete; wait for more packets.
- if (queued_encoded_callbacks_.empty()) return;
- AudioFrameEncodedCallback callback = queued_encoded_callbacks_.front();
- queued_encoded_callbacks_.pop_front();
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioReceiver::GetEncodedAudioFrame,
- weak_factory_.GetWeakPtr(), callback));
-}
-
-void AudioReceiver::GetRawAudioFrame(int number_of_10ms_blocks,
- int desired_frequency, const AudioFrameDecodedCallback& callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(audio_decoder_) << "Invalid function call in this configuration";
- // TODO(pwestin): we can skip this function by posting direct to the decoder.
- cast_environment_->PostTask(CastEnvironment::AUDIO_DECODER, FROM_HERE,
- base::Bind(&AudioReceiver::DecodeAudioFrameThread,
- base::Unretained(this),
- number_of_10ms_blocks,
- desired_frequency,
- callback));
-}
-
-void AudioReceiver::DecodeAudioFrameThread(
- int number_of_10ms_blocks,
- int desired_frequency,
- const AudioFrameDecodedCallback callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::AUDIO_DECODER));
- // TODO(mikhal): Allow the application to allocate this memory.
- scoped_ptr<PcmAudioFrame> audio_frame(new PcmAudioFrame());
-
- uint32 rtp_timestamp = 0;
- if (!audio_decoder_->GetRawAudioFrame(number_of_10ms_blocks,
- desired_frequency,
- audio_frame.get(),
- &rtp_timestamp)) {
- DecodedAudioCallbackData callback_data;
- callback_data.number_of_10ms_blocks = number_of_10ms_blocks;
- callback_data.desired_frequency = desired_frequency;
- callback_data.callback = callback;
- queued_decoded_callbacks_.push_back(callback_data);
- return;
- }
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
-
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioReceiver::ReturnDecodedFrameWithPlayoutDelay,
- base::Unretained(this), base::Passed(&audio_frame), rtp_timestamp,
- callback));
-}
-
-void AudioReceiver::ReturnDecodedFrameWithPlayoutDelay(
- scoped_ptr<PcmAudioFrame> audio_frame, uint32 rtp_timestamp,
- const AudioFrameDecodedCallback callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- cast_environment_->Logging()->InsertFrameEvent(kAudioFrameDecoded,
- rtp_timestamp, kFrameIdUnknown);
-
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
- base::TimeTicks playout_time = GetPlayoutTime(now, rtp_timestamp);
-
- cast_environment_->Logging()->InsertFrameEventWithDelay(kAudioPlayoutDelay,
- rtp_timestamp, kFrameIdUnknown, playout_time - now);
-
- // Frame is ready - Send back to the caller.
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(callback, base::Passed(&audio_frame), playout_time));
-}
-
-void AudioReceiver::PlayoutTimeout() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(audio_buffer_) << "Invalid function call in this configuration";
- if (queued_encoded_callbacks_.empty()) {
- // Already released by incoming packet.
- return;
- }
- uint32 rtp_timestamp = 0;
- bool next_frame = false;
- scoped_ptr<EncodedAudioFrame> encoded_frame(new EncodedAudioFrame());
-
- if (!audio_buffer_->GetEncodedAudioFrame(encoded_frame.get(),
- &rtp_timestamp, &next_frame)) {
- // We have no audio frames. Wait for new packet(s).
- // Since the application can post multiple AudioFrameEncodedCallback and
- // we only check the next frame to play out we might have multiple timeout
- // events firing after each other; however this should be a rare event.
- VLOG(1) << "Failed to retrieved a complete frame at this point in time";
- return;
- }
-
- if (decryptor_ && !DecryptAudioFrame(&encoded_frame)) {
- // Logging already done.
- return;
- }
-
- if (PostEncodedAudioFrame(queued_encoded_callbacks_.front(), rtp_timestamp,
- next_frame, &encoded_frame)) {
- // Call succeed remove callback from list.
- queued_encoded_callbacks_.pop_front();
- }
-}
-
-void AudioReceiver::GetEncodedAudioFrame(
- const AudioFrameEncodedCallback& callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(audio_buffer_) << "Invalid function call in this configuration";
-
- uint32 rtp_timestamp = 0;
- bool next_frame = false;
- scoped_ptr<EncodedAudioFrame> encoded_frame(new EncodedAudioFrame());
-
- if (!audio_buffer_->GetEncodedAudioFrame(encoded_frame.get(),
- &rtp_timestamp, &next_frame)) {
- // We have no audio frames. Wait for new packet(s).
- VLOG(1) << "Wait for more audio packets in frame";
- queued_encoded_callbacks_.push_back(callback);
- return;
- }
- if (decryptor_ && !DecryptAudioFrame(&encoded_frame)) {
- // Logging already done.
- queued_encoded_callbacks_.push_back(callback);
- return;
- }
- if (!PostEncodedAudioFrame(callback, rtp_timestamp, next_frame,
- &encoded_frame)) {
- // We have an audio frame; however we are missing packets and we have time
- // to wait for new packet(s).
- queued_encoded_callbacks_.push_back(callback);
- }
-}
-
-bool AudioReceiver::PostEncodedAudioFrame(
- const AudioFrameEncodedCallback& callback,
- uint32 rtp_timestamp,
- bool next_frame,
- scoped_ptr<EncodedAudioFrame>* encoded_frame) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(audio_buffer_) << "Invalid function call in this configuration";
-
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
- base::TimeTicks playout_time = GetPlayoutTime(now, rtp_timestamp);
- base::TimeDelta time_until_playout = playout_time - now;
- base::TimeDelta min_wait_delta =
- base::TimeDelta::FromMilliseconds(kMaxAudioFrameWaitMs);
-
- if (!next_frame && (time_until_playout > min_wait_delta)) {
- base::TimeDelta time_until_release = time_until_playout - min_wait_delta;
- cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioReceiver::PlayoutTimeout, weak_factory_.GetWeakPtr()),
- time_until_release);
- VLOG(1) << "Wait until time to playout:"
- << time_until_release.InMilliseconds();
- return false;
- }
- (*encoded_frame)->codec = codec_;
- audio_buffer_->ReleaseFrame((*encoded_frame)->frame_id);
-
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(callback, base::Passed(encoded_frame), playout_time));
- return true;
-}
-
-void AudioReceiver::IncomingPacket(const uint8* packet, size_t length,
- const base::Closure callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- bool rtcp_packet = Rtcp::IsRtcpPacket(packet, length);
- if (!rtcp_packet) {
- rtp_receiver_->ReceivedPacket(packet, length);
- } else {
- rtcp_->IncomingRtcpPacket(packet, length);
- }
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
-}
-
-void AudioReceiver::CastFeedback(const RtcpCastMessage& cast_message) {
- // TODO(pwestin): add logging.
- rtcp_->SendRtcpFromRtpReceiver(&cast_message, NULL);
-}
-
-base::TimeTicks AudioReceiver::GetPlayoutTime(base::TimeTicks now,
- uint32 rtp_timestamp) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- // Senders time in ms when this frame was recorded.
- // Note: the senders clock and our local clock might not be synced.
- base::TimeTicks rtp_timestamp_in_ticks;
- if (time_offset_ == base::TimeDelta()) {
- if (rtcp_->RtpTimestampInSenderTime(frequency_,
- first_incoming_rtp_timestamp_,
- &rtp_timestamp_in_ticks)) {
- time_offset_ = time_first_incoming_packet_ - rtp_timestamp_in_ticks;
- } else {
- // We have not received any RTCP to sync the stream play it out as soon as
- // possible.
- uint32 rtp_timestamp_diff = rtp_timestamp - first_incoming_rtp_timestamp_;
-
- int frequency_khz = frequency_ / 1000;
- base::TimeDelta rtp_time_diff_delta =
- base::TimeDelta::FromMilliseconds(rtp_timestamp_diff / frequency_khz);
- base::TimeDelta time_diff_delta = now - time_first_incoming_packet_;
-
- return now + std::max(rtp_time_diff_delta - time_diff_delta,
- base::TimeDelta());
- }
- }
- // This can fail if we have not received any RTCP packets in a long time.
- return rtcp_->RtpTimestampInSenderTime(frequency_, rtp_timestamp,
- &rtp_timestamp_in_ticks) ?
- rtp_timestamp_in_ticks + time_offset_ + target_delay_delta_ :
- now;
-}
-
-bool AudioReceiver::DecryptAudioFrame(
- scoped_ptr<EncodedAudioFrame>* audio_frame) {
- DCHECK(decryptor_) << "Invalid state";
-
- if (!decryptor_->SetCounter(GetAesNonce((*audio_frame)->frame_id,
- iv_mask_))) {
- NOTREACHED() << "Failed to set counter";
- return false;
- }
- std::string decrypted_audio_data;
- if (!decryptor_->Decrypt((*audio_frame)->data, &decrypted_audio_data)) {
- VLOG(0) << "Decryption error";
- // Give up on this frame, release it from jitter buffer.
- audio_buffer_->ReleaseFrame((*audio_frame)->frame_id);
- return false;
- }
- (*audio_frame)->data.swap(decrypted_audio_data);
- return true;
-}
-
-void AudioReceiver::ScheduleNextRtcpReport() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- base::TimeDelta time_to_send = rtcp_->TimeToSendNextRtcpReport() -
- 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,
- weak_factory_.GetWeakPtr()), time_to_send);
-}
-
-void AudioReceiver::SendNextRtcpReport() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- // TODO(pwestin): add logging.
- rtcp_->SendRtcpFromRtpReceiver(NULL, NULL);
- ScheduleNextRtcpReport();
-}
-
-// Cast messages should be sent within a maximum interval. Schedule a call
-// if not triggered elsewhere, e.g. by the cast message_builder.
-void AudioReceiver::ScheduleNextCastMessage() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- base::TimeTicks send_time;
- if (audio_buffer_) {
- audio_buffer_->TimeToSendNextCastMessage(&send_time);
- } else if (audio_decoder_) {
- audio_decoder_->TimeToSendNextCastMessage(&send_time);
- } else {
- NOTREACHED();
- }
- 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::SendNextCastMessage,
- weak_factory_.GetWeakPtr()), time_to_send);
-}
-
-void AudioReceiver::SendNextCastMessage() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
-
- if (audio_buffer_) {
- // Will only send a message if it is time.
- audio_buffer_->SendCastMessage();
- }
- if (audio_decoder_) {
- // Will only send a message if it is time.
- audio_decoder_->SendCastMessage();
- }
- ScheduleNextCastMessage();
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/audio_receiver/audio_receiver.gypi b/chromium/media/cast/audio_receiver/audio_receiver.gypi
deleted file mode 100644
index a851612f721..00000000000
--- a/chromium/media/cast/audio_receiver/audio_receiver.gypi
+++ /dev/null
@@ -1,29 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_audio_receiver',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc/',
- ],
- 'sources': [
- 'audio_decoder.h',
- 'audio_decoder.cc',
- 'audio_receiver.h',
- 'audio_receiver.cc',
- ], # source
- 'dependencies': [
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- '<(DEPTH)/media/cast/rtcp/rtcp.gyp:cast_rtcp',
- '<(DEPTH)/media/cast/rtp_receiver/rtp_receiver.gyp:*',
- '<(DEPTH)/third_party/webrtc/webrtc.gyp:webrtc',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/audio_receiver/audio_receiver.h b/chromium/media/cast/audio_receiver/audio_receiver.h
deleted file mode 100644
index c49e1c15c25..00000000000
--- a/chromium/media/cast/audio_receiver/audio_receiver.h
+++ /dev/null
@@ -1,143 +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/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/cast_config.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/cast_receiver.h"
-#include "media/cast/rtcp/rtcp.h" // RtcpCastMessage
-#include "media/cast/rtp_receiver/rtp_receiver_defines.h" // RtpCastHeader
-
-namespace crypto {
- class Encryptor;
-}
-
-namespace media {
-namespace cast {
-
-class AudioDecoder;
-class Framer;
-class LocalRtpAudioData;
-class LocalRtpAudioFeedback;
-class PacedPacketSender;
-class RtpReceiver;
-class RtpReceiverStatistics;
-
-struct DecodedAudioCallbackData {
- DecodedAudioCallbackData();
- ~DecodedAudioCallbackData();
- int number_of_10ms_blocks;
- int desired_frequency;
- AudioFrameDecodedCallback callback;
-};
-
-// This class is not thread safe. Should only be called from the Main cast
-// thread.
-class AudioReceiver : public base::NonThreadSafe,
- public base::SupportsWeakPtr<AudioReceiver> {
- public:
- AudioReceiver(scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- PacedPacketSender* const packet_sender);
-
- virtual ~AudioReceiver();
-
- // Extract a raw audio frame from the cast receiver.
- // Actual decoding will be preformed on a designated audio_decoder thread.
- void GetRawAudioFrame(int number_of_10ms_blocks,
- int desired_frequency,
- const AudioFrameDecodedCallback& callback);
-
- // Extract an encoded audio frame from the cast receiver.
- void GetEncodedAudioFrame(const AudioFrameEncodedCallback& callback);
-
- // Should only be called from the main cast thread.
- void IncomingPacket(const uint8* packet, size_t length,
- const base::Closure callback);
-
- protected:
- void IncomingParsedRtpPacket(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader& rtp_header);
- private:
- friend class LocalRtpAudioData;
- friend class LocalRtpAudioFeedback;
-
- void CastFeedback(const RtcpCastMessage& cast_message);
-
- // Time to pull out the audio even though we are missing data.
- void PlayoutTimeout();
-
- bool PostEncodedAudioFrame(const AudioFrameEncodedCallback& callback,
- uint32 rtp_timestamp,
- bool next_frame,
- scoped_ptr<EncodedAudioFrame>* encoded_frame);
-
- // Actual decoding implementation - should be called under the audio decoder
- // thread.
- void DecodeAudioFrameThread(int number_of_10ms_blocks,
- int desired_frequency,
- const AudioFrameDecodedCallback callback);
- void ReturnDecodedFrameWithPlayoutDelay(
- scoped_ptr<PcmAudioFrame> audio_frame, uint32 rtp_timestamp,
- const AudioFrameDecodedCallback callback);
-
- // Return the playout time based on the current time and rtp timestamp.
- base::TimeTicks GetPlayoutTime(base::TimeTicks now, uint32 rtp_timestamp);
-
- void InitializeTimers();
-
- // Decrypts the data within the |audio_frame| and replaces the data with the
- // decrypted string.
- bool DecryptAudioFrame(scoped_ptr<EncodedAudioFrame>* audio_frame);
-
- // 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();
-
- scoped_refptr<CastEnvironment> cast_environment_;
- base::WeakPtrFactory<AudioReceiver> weak_factory_;
-
- const AudioCodec codec_;
- const int frequency_;
- base::TimeDelta target_delay_delta_;
- scoped_ptr<Framer> audio_buffer_;
- scoped_ptr<AudioDecoder> audio_decoder_;
- scoped_ptr<LocalRtpAudioData> incoming_payload_callback_;
- scoped_ptr<LocalRtpAudioFeedback> incoming_payload_feedback_;
- scoped_ptr<RtpReceiver> rtp_receiver_;
- scoped_ptr<Rtcp> rtcp_;
- scoped_ptr<RtpReceiverStatistics> rtp_audio_receiver_statistics_;
- base::TimeDelta time_offset_;
- base::TimeTicks time_first_incoming_packet_;
- uint32 first_incoming_rtp_timestamp_;
- scoped_ptr<crypto::Encryptor> decryptor_;
- std::string iv_mask_;
-
- std::list<AudioFrameEncodedCallback> queued_encoded_callbacks_;
- std::list<DecodedAudioCallbackData> queued_decoded_callbacks_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_AUDIO_RECEIVER_AUDIO_RECEIVER_H_
diff --git a/chromium/media/cast/audio_receiver/audio_receiver_unittest.cc b/chromium/media/cast/audio_receiver/audio_receiver_unittest.cc
deleted file mode 100644
index a10af679925..00000000000
--- a/chromium/media/cast/audio_receiver/audio_receiver_unittest.cc
+++ /dev/null
@@ -1,217 +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 "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/net/pacing/mock_paced_packet_sender.h"
-#include "media/cast/rtcp/test_rtcp_packet_builder.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
-
-namespace {
-class TestAudioEncoderCallback :
- public base::RefCountedThreadSafe<TestAudioEncoderCallback> {
- public:
- TestAudioEncoderCallback()
- : num_called_(0) {}
-
- void SetExpectedResult(uint8 expected_frame_id,
- const base::TimeTicks& expected_playout_time) {
- expected_frame_id_ = expected_frame_id;
- expected_playout_time_ = expected_playout_time;
- }
-
- void DeliverEncodedAudioFrame(scoped_ptr<EncodedAudioFrame> audio_frame,
- const base::TimeTicks& playout_time) {
- EXPECT_EQ(expected_frame_id_, audio_frame->frame_id);
- EXPECT_EQ(kPcm16, audio_frame->codec);
- EXPECT_EQ(expected_playout_time_, playout_time);
- num_called_++;
- }
-
- int number_times_called() const { return num_called_;}
-
- protected:
- virtual ~TestAudioEncoderCallback() {}
-
- private:
- friend class base::RefCountedThreadSafe<TestAudioEncoderCallback>;
-
- int num_called_;
- uint8 expected_frame_id_;
- base::TimeTicks expected_playout_time_;
-};
-} // namespace
-
-class PeerAudioReceiver : public AudioReceiver {
- public:
- PeerAudioReceiver(scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- PacedPacketSender* const packet_sender)
- : AudioReceiver(cast_environment, audio_config, packet_sender) {}
-
- using AudioReceiver::IncomingParsedRtpPacket;
-};
-
-class AudioReceiverTest : public ::testing::Test {
- protected:
- AudioReceiverTest() {
- // Configure the audio receiver to use PCM16.
- audio_config_.rtp_payload_type = 127;
- audio_config_.frequency = 16000;
- audio_config_.channels = 1;
- audio_config_.codec = kPcm16;
- audio_config_.use_external_decoder = false;
- audio_config_.feedback_ssrc = 1234;
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
- test_audio_encoder_callback_ = new TestAudioEncoderCallback();
- }
-
- void Configure(bool use_external_decoder) {
- audio_config_.use_external_decoder = use_external_decoder;
- receiver_.reset(new PeerAudioReceiver(cast_environment_, audio_config_,
- &mock_transport_));
- }
-
- virtual ~AudioReceiverTest() {}
-
- static void DummyDeletePacket(const uint8* packet) {};
-
- virtual void SetUp() {
- payload_.assign(kIpPacketSize, 0);
- rtp_header_.is_key_frame = true;
- rtp_header_.frame_id = 0;
- rtp_header_.packet_id = 0;
- rtp_header_.max_packet_id = 0;
- rtp_header_.is_reference = false;
- rtp_header_.reference_frame_id = 0;
- rtp_header_.webrtc.header.timestamp = 0;
- }
-
- AudioReceiverConfig audio_config_;
- std::vector<uint8> payload_;
- RtpCastHeader rtp_header_;
- base::SimpleTestTickClock testing_clock_;
- MockPacedPacketSender mock_transport_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
- scoped_ptr<PeerAudioReceiver> receiver_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_refptr<TestAudioEncoderCallback> test_audio_encoder_callback_;
-};
-
-TEST_F(AudioReceiverTest, GetOnePacketEncodedframe) {
- Configure(true);
- EXPECT_CALL(mock_transport_, SendRtcpPacket(testing::_)).Times(1);
-
- receiver_->IncomingParsedRtpPacket(payload_.data(),
- payload_.size(), rtp_header_);
- EncodedAudioFrame audio_frame;
- base::TimeTicks playout_time;
- test_audio_encoder_callback_->SetExpectedResult(0, testing_clock_.NowTicks());
-
- AudioFrameEncodedCallback frame_encoded_callback =
- base::Bind(&TestAudioEncoderCallback::DeliverEncodedAudioFrame,
- test_audio_encoder_callback_.get());
-
- receiver_->GetEncodedAudioFrame(frame_encoded_callback);
- task_runner_->RunTasks();
- EXPECT_EQ(1, test_audio_encoder_callback_->number_times_called());
-}
-
-TEST_F(AudioReceiverTest, MultiplePendingGetCalls) {
- Configure(true);
- EXPECT_CALL(mock_transport_, SendRtcpPacket(testing::_)).WillRepeatedly(
- testing::Return(true));
-
- AudioFrameEncodedCallback frame_encoded_callback =
- base::Bind(&TestAudioEncoderCallback::DeliverEncodedAudioFrame,
- test_audio_encoder_callback_.get());
-
- receiver_->GetEncodedAudioFrame(frame_encoded_callback);
-
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
-
- EncodedAudioFrame audio_frame;
- base::TimeTicks playout_time;
- test_audio_encoder_callback_->SetExpectedResult(0, testing_clock_.NowTicks());
-
- task_runner_->RunTasks();
- EXPECT_EQ(1, test_audio_encoder_callback_->number_times_called());
-
- TestRtcpPacketBuilder rtcp_packet;
-
- uint32 ntp_high;
- uint32 ntp_low;
- ConvertTimeTicksToNtp(testing_clock_.NowTicks(), &ntp_high, &ntp_low);
- rtcp_packet.AddSrWithNtp(audio_config_.feedback_ssrc, ntp_high, ntp_low,
- rtp_header_.webrtc.header.timestamp);
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(20));
-
- receiver_->IncomingPacket(rtcp_packet.Packet(), rtcp_packet.Length(),
- base::Bind(AudioReceiverTest::DummyDeletePacket, rtcp_packet.Packet()));
-
- // Make sure that we are not continuous and that the RTP timestamp represent a
- // time in the future.
- rtp_header_.is_key_frame = false;
- rtp_header_.frame_id = 2;
- rtp_header_.is_reference = true;
- rtp_header_.reference_frame_id = 0;
- rtp_header_.webrtc.header.timestamp = 960;
- test_audio_encoder_callback_->SetExpectedResult(2,
- testing_clock_.NowTicks() + base::TimeDelta::FromMilliseconds(100));
-
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
- receiver_->GetEncodedAudioFrame(frame_encoded_callback);
- task_runner_->RunTasks();
-
- // Frame 2 should not come out at this point in time.
- EXPECT_EQ(1, test_audio_encoder_callback_->number_times_called());
-
- // Through on one more pending callback.
- receiver_->GetEncodedAudioFrame(frame_encoded_callback);
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(100));
-
- task_runner_->RunTasks();
- EXPECT_EQ(2, test_audio_encoder_callback_->number_times_called());
-
- test_audio_encoder_callback_->SetExpectedResult(3, testing_clock_.NowTicks());
-
- // Through on one more pending audio frame.
- rtp_header_.frame_id = 3;
- rtp_header_.is_reference = false;
- rtp_header_.reference_frame_id = 0;
- rtp_header_.webrtc.header.timestamp = 1280;
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
-
- receiver_->GetEncodedAudioFrame(frame_encoded_callback);
- task_runner_->RunTasks();
- EXPECT_EQ(3, test_audio_encoder_callback_->number_times_called());
-}
-
-// TODO(mikhal): Add encoded frames.
-TEST_F(AudioReceiverTest, GetRawFrame) {
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/audio_sender/audio_encoder.cc b/chromium/media/cast/audio_sender/audio_encoder.cc
index a82d1de39a5..8860c7dd2d8 100644
--- a/chromium/media/cast/audio_sender/audio_encoder.cc
+++ b/chromium/media/cast/audio_sender/audio_encoder.cc
@@ -8,8 +8,8 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
+#include "base/location.h"
+#include "base/stl_util.h"
#include "base/sys_byteorder.h"
#include "base/time/time.h"
#include "media/base/audio_bus.h"
@@ -20,121 +20,198 @@
namespace media {
namespace cast {
-void LogAudioEncodedEvent(CastEnvironment* const cast_environment,
- const base::TimeTicks& recorded_time) {
- // TODO(mikhal): Resolve timestamp calculation for audio.
- cast_environment->Logging()->InsertFrameEvent(kAudioFrameEncoded,
- GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown);
-}
+namespace {
+
+// The fixed number of audio frames per second and, inversely, the duration of
+// one frame's worth of samples.
+const int kFramesPerSecond = 100;
+const int kFrameDurationMillis = 1000 / kFramesPerSecond; // No remainder!
+
+// Threshold used to decide whether audio being delivered to the encoder is
+// coming in too slow with respect to the capture timestamps.
+const int kUnderrunThresholdMillis = 3 * kFrameDurationMillis;
+
+} // namespace
+
// Base class that handles the common problem of feeding one or more AudioBus'
-// data into a 10 ms buffer and then, once the buffer is full, encoding the
-// signal and emitting an EncodedAudioFrame via the FrameEncodedCallback.
+// data into a buffer and then, once the buffer is full, encoding the signal and
+// emitting an EncodedFrame via the FrameEncodedCallback.
//
// Subclasses complete the implementation by handling the actual encoding
// details.
-class AudioEncoder::ImplBase {
+class AudioEncoder::ImplBase
+ : public base::RefCountedThreadSafe<AudioEncoder::ImplBase> {
public:
- ImplBase(CastEnvironment* cast_environment,
- AudioCodec codec, int num_channels, int sampling_rate,
+ ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::AudioCodec codec,
+ int num_channels,
+ int sampling_rate,
const FrameEncodedCallback& callback)
: cast_environment_(cast_environment),
- codec_(codec), num_channels_(num_channels),
- samples_per_10ms_(sampling_rate / 100),
+ codec_(codec),
+ num_channels_(num_channels),
+ samples_per_frame_(sampling_rate / kFramesPerSecond),
callback_(callback),
+ cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
buffer_fill_end_(0),
- frame_id_(0) {
- CHECK_GT(num_channels_, 0);
- CHECK_GT(samples_per_10ms_, 0);
- CHECK_EQ(sampling_rate % 100, 0);
- CHECK_LE(samples_per_10ms_ * num_channels_,
- EncodedAudioFrame::kMaxNumberOfSamples);
+ frame_id_(0),
+ frame_rtp_timestamp_(0) {
+ // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration.
+ const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100;
+ if (num_channels_ <= 0 || samples_per_frame_ <= 0 ||
+ sampling_rate % kFramesPerSecond != 0 ||
+ samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) {
+ cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
+ }
}
- virtual ~ImplBase() {}
+ CastInitializationStatus InitializationResult() const {
+ return cast_initialization_status_;
+ }
+
+ void EncodeAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time) {
+ DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED);
+ DCHECK(!recorded_time.is_null());
+
+ // Determine whether |recorded_time| is consistent with the amount of audio
+ // data having been processed in the past. Resolve the underrun problem by
+ // dropping data from the internal buffer and skipping ahead the next
+ // frame's RTP timestamp by the estimated number of frames missed. On the
+ // other hand, don't attempt to resolve overruns: A receiver should
+ // gracefully deal with an excess of audio data.
+ const base::TimeDelta frame_duration =
+ base::TimeDelta::FromMilliseconds(kFrameDurationMillis);
+ base::TimeDelta buffer_fill_duration =
+ buffer_fill_end_ * frame_duration / samples_per_frame_;
+ if (!frame_capture_time_.is_null()) {
+ const base::TimeDelta amount_ahead_by =
+ recorded_time - (frame_capture_time_ + buffer_fill_duration);
+ if (amount_ahead_by >
+ base::TimeDelta::FromMilliseconds(kUnderrunThresholdMillis)) {
+ buffer_fill_end_ = 0;
+ buffer_fill_duration = base::TimeDelta();
+ const int64 num_frames_missed = amount_ahead_by /
+ base::TimeDelta::FromMilliseconds(kFrameDurationMillis);
+ frame_rtp_timestamp_ +=
+ static_cast<uint32>(num_frames_missed * samples_per_frame_);
+ DVLOG(1) << "Skipping RTP timestamp ahead to account for "
+ << num_frames_missed * samples_per_frame_
+ << " samples' worth of underrun.";
+ }
+ }
+ frame_capture_time_ = recorded_time - buffer_fill_duration;
- void EncodeAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback) {
+ // Encode all audio in |audio_bus| into zero or more frames.
int src_pos = 0;
while (src_pos < audio_bus->frames()) {
- const int num_samples_to_xfer =
- std::min(samples_per_10ms_ - buffer_fill_end_,
- audio_bus->frames() - src_pos);
+ const int num_samples_to_xfer = std::min(
+ samples_per_frame_ - buffer_fill_end_, audio_bus->frames() - src_pos);
DCHECK_EQ(audio_bus->channels(), num_channels_);
TransferSamplesIntoBuffer(
- audio_bus, src_pos, buffer_fill_end_, num_samples_to_xfer);
+ audio_bus.get(), src_pos, buffer_fill_end_, num_samples_to_xfer);
src_pos += num_samples_to_xfer;
buffer_fill_end_ += num_samples_to_xfer;
- if (src_pos == audio_bus->frames()) {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- done_callback);
- // Note: |audio_bus| is now invalid..
+ if (buffer_fill_end_ < samples_per_frame_)
+ break;
+
+ scoped_ptr<transport::EncodedFrame> audio_frame(
+ new transport::EncodedFrame());
+ audio_frame->dependency = transport::EncodedFrame::KEY;
+ audio_frame->frame_id = frame_id_;
+ audio_frame->referenced_frame_id = frame_id_;
+ audio_frame->rtp_timestamp = frame_rtp_timestamp_;
+ audio_frame->reference_time = frame_capture_time_;
+
+ if (EncodeFromFilledBuffer(&audio_frame->data)) {
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(callback_, base::Passed(&audio_frame)));
}
- if (buffer_fill_end_ == samples_per_10ms_) {
- scoped_ptr<EncodedAudioFrame> audio_frame(new EncodedAudioFrame());
- audio_frame->codec = codec_;
- audio_frame->frame_id = frame_id_++;
- audio_frame->samples = samples_per_10ms_;
- if (EncodeFromFilledBuffer(&audio_frame->data)) {
- // Compute an offset to determine the recorded time for the first
- // audio sample in the buffer.
- const base::TimeDelta buffer_time_offset =
- (buffer_fill_end_ - src_pos) *
- base::TimeDelta::FromMilliseconds(10) / samples_per_10ms_;
- // TODO(miu): Consider batching EncodedAudioFrames so we only post a
- // at most one task for each call to this method.
- cast_environment_->PostTask(
- CastEnvironment::MAIN, FROM_HERE,
- base::Bind(callback_, base::Passed(&audio_frame),
- recorded_time - buffer_time_offset));
- }
- buffer_fill_end_ = 0;
- }
+ // Reset the internal buffer, frame ID, and timestamps for the next frame.
+ buffer_fill_end_ = 0;
+ ++frame_id_;
+ frame_rtp_timestamp_ += samples_per_frame_;
+ frame_capture_time_ += frame_duration;
}
}
protected:
+ friend class base::RefCountedThreadSafe<ImplBase>;
+ virtual ~ImplBase() {}
+
virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
int source_offset,
int buffer_fill_offset,
int num_samples) = 0;
virtual bool EncodeFromFilledBuffer(std::string* out) = 0;
- CastEnvironment* const cast_environment_;
- const AudioCodec codec_;
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ const transport::AudioCodec codec_;
const int num_channels_;
- const int samples_per_10ms_;
+ const int samples_per_frame_;
const FrameEncodedCallback callback_;
+ // Subclass' ctor is expected to set this to STATUS_AUDIO_INITIALIZED.
+ CastInitializationStatus cast_initialization_status_;
+
private:
// In the case where a call to EncodeAudio() cannot completely fill the
// buffer, this points to the position at which to populate data in a later
// call.
int buffer_fill_end_;
- // A counter used to label EncodedAudioFrames.
+ // A counter used to label EncodedFrames.
uint32 frame_id_;
- private:
+ // The RTP timestamp for the next frame of encoded audio. This is defined as
+ // the number of audio samples encoded so far, plus the estimated number of
+ // samples that were missed due to data underruns. A receiver uses this value
+ // to detect gaps in the audio signal data being provided. Per the spec, RTP
+ // timestamp values are allowed to overflow and roll around past zero.
+ uint32 frame_rtp_timestamp_;
+
+ // The local system time associated with the start of the next frame of
+ // encoded audio. This value is passed on to a receiver as a reference clock
+ // timestamp for the purposes of synchronizing audio and video. Its
+ // progression is expected to drift relative to the elapsed time implied by
+ // the RTP timestamps.
+ base::TimeTicks frame_capture_time_;
+
DISALLOW_COPY_AND_ASSIGN(ImplBase);
};
class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
public:
- OpusImpl(CastEnvironment* cast_environment,
- int num_channels, int sampling_rate, int bitrate,
+ OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment,
+ int num_channels,
+ int sampling_rate,
+ int bitrate,
const FrameEncodedCallback& callback)
- : ImplBase(cast_environment, kOpus, num_channels, sampling_rate,
+ : ImplBase(cast_environment,
+ transport::kOpus,
+ num_channels,
+ sampling_rate,
callback),
encoder_memory_(new uint8[opus_encoder_get_size(num_channels)]),
opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get())),
- buffer_(new float[num_channels * samples_per_10ms_]) {
- CHECK_EQ(opus_encoder_init(opus_encoder_, sampling_rate, num_channels,
- OPUS_APPLICATION_AUDIO),
- OPUS_OK);
+ buffer_(new float[num_channels * samples_per_frame_]) {
+ if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
+ return;
+ if (opus_encoder_init(opus_encoder_,
+ sampling_rate,
+ num_channels,
+ OPUS_APPLICATION_AUDIO) != OPUS_OK) {
+ ImplBase::cast_initialization_status_ =
+ STATUS_INVALID_AUDIO_CONFIGURATION;
+ return;
+ }
+ ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
+
if (bitrate <= 0) {
// Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a
// variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms
@@ -146,9 +223,9 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
OPUS_OK);
}
+ private:
virtual ~OpusImpl() {}
- private:
virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
int source_offset,
int buffer_fill_offset,
@@ -165,9 +242,12 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
out->resize(kOpusMaxPayloadSize);
- const opus_int32 result = opus_encode_float(
- opus_encoder_, buffer_.get(), samples_per_10ms_,
- reinterpret_cast<uint8*>(&out->at(0)), kOpusMaxPayloadSize);
+ const opus_int32 result =
+ opus_encode_float(opus_encoder_,
+ buffer_.get(),
+ samples_per_frame_,
+ reinterpret_cast<uint8*>(string_as_array(out)),
+ kOpusMaxPayloadSize);
if (result > 1) {
out->resize(result);
return true;
@@ -198,30 +278,40 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
public:
- Pcm16Impl(CastEnvironment* cast_environment,
- int num_channels, int sampling_rate,
+ Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
+ int num_channels,
+ int sampling_rate,
const FrameEncodedCallback& callback)
- : ImplBase(cast_environment, kPcm16, num_channels, sampling_rate,
+ : ImplBase(cast_environment,
+ transport::kPcm16,
+ num_channels,
+ sampling_rate,
callback),
- buffer_(new int16[num_channels * samples_per_10ms_]) {}
+ buffer_(new int16[num_channels * samples_per_frame_]) {
+ if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
+ return;
+ cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
+ }
+ private:
virtual ~Pcm16Impl() {}
- private:
virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
int source_offset,
int buffer_fill_offset,
int num_samples) OVERRIDE {
audio_bus->ToInterleavedPartial(
- source_offset, num_samples, sizeof(int16),
+ source_offset,
+ num_samples,
+ sizeof(int16),
buffer_.get() + buffer_fill_offset * num_channels_);
}
virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
// Output 16-bit PCM integers in big-endian byte order.
- out->resize(num_channels_ * samples_per_10ms_ * sizeof(int16));
+ out->resize(num_channels_ * samples_per_frame_ * sizeof(int16));
const int16* src = buffer_.get();
- const int16* const src_end = src + num_channels_ * samples_per_10ms_;
+ const int16* const src_end = src + num_channels_ * samples_per_frame_;
uint16* dest = reinterpret_cast<uint16*>(&out->at(0));
for (; src < src_end; ++src, ++dest)
*dest = base::HostToNet16(*src);
@@ -242,17 +332,19 @@ AudioEncoder::AudioEncoder(
// Note: It doesn't matter which thread constructs AudioEncoder, just so long
// as all calls to InsertAudio() are by the same thread.
insert_thread_checker_.DetachFromThread();
-
switch (audio_config.codec) {
- case kOpus:
- impl_.reset(new OpusImpl(
- cast_environment, audio_config.channels, audio_config.frequency,
- audio_config.bitrate, frame_encoded_callback));
+ case transport::kOpus:
+ impl_ = new OpusImpl(cast_environment,
+ audio_config.channels,
+ audio_config.frequency,
+ audio_config.bitrate,
+ frame_encoded_callback);
break;
- case kPcm16:
- impl_.reset(new Pcm16Impl(
- cast_environment, audio_config.channels, audio_config.frequency,
- frame_encoded_callback));
+ case transport::kPcm16:
+ impl_ = new Pcm16Impl(cast_environment,
+ audio_config.channels,
+ audio_config.frequency,
+ frame_encoded_callback);
break;
default:
NOTREACHED() << "Unsupported or unspecified codec for audio encoder";
@@ -262,30 +354,28 @@ AudioEncoder::AudioEncoder(
AudioEncoder::~AudioEncoder() {}
-void AudioEncoder::InsertAudio(
- const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback) {
+CastInitializationStatus AudioEncoder::InitializationResult() const {
+ DCHECK(insert_thread_checker_.CalledOnValidThread());
+ if (impl_) {
+ return impl_->InitializationResult();
+ }
+ return STATUS_UNSUPPORTED_AUDIO_CODEC;
+}
+
+void AudioEncoder::InsertAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time) {
DCHECK(insert_thread_checker_.CalledOnValidThread());
+ DCHECK(audio_bus.get());
if (!impl_) {
NOTREACHED();
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- done_callback);
return;
}
- cast_environment_->PostTask(CastEnvironment::AUDIO_ENCODER, FROM_HERE,
- base::Bind(&AudioEncoder::EncodeAudio, this, audio_bus, recorded_time,
- done_callback));
-}
-
-void AudioEncoder::EncodeAudio(
- const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::AUDIO_ENCODER));
- impl_->EncodeAudio(audio_bus, recorded_time, done_callback);
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(LogAudioEncodedEvent, cast_environment_, recorded_time));
+ cast_environment_->PostTask(CastEnvironment::AUDIO,
+ FROM_HERE,
+ base::Bind(&AudioEncoder::ImplBase::EncodeAudio,
+ impl_,
+ base::Passed(&audio_bus),
+ recorded_time));
}
} // namespace cast
diff --git a/chromium/media/cast/audio_sender/audio_encoder.h b/chromium/media/cast/audio_sender/audio_encoder.h
index 4a22d1983bd..2297672b74b 100644
--- a/chromium/media/cast/audio_sender/audio_encoder.h
+++ b/chromium/media/cast/audio_sender/audio_encoder.h
@@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
+#include "media/base/audio_bus.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
@@ -16,47 +17,30 @@ class TimeTicks;
}
namespace media {
-class AudioBus;
-}
-
-namespace media {
namespace cast {
-class AudioEncoder : public base::RefCountedThreadSafe<AudioEncoder> {
+class AudioEncoder {
public:
- typedef base::Callback<void(scoped_ptr<EncodedAudioFrame>,
- const base::TimeTicks&)> FrameEncodedCallback;
+ typedef base::Callback<void(scoped_ptr<transport::EncodedFrame>)>
+ FrameEncodedCallback;
AudioEncoder(const scoped_refptr<CastEnvironment>& cast_environment,
const AudioSenderConfig& audio_config,
const FrameEncodedCallback& frame_encoded_callback);
+ virtual ~AudioEncoder();
- // The |audio_bus| must be valid until the |done_callback| is called.
- // The callback is called from the main cast thread as soon as the encoder is
- // done with |audio_bus|; it does not mean that the encoded data has been
- // sent out.
- void InsertAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback);
+ CastInitializationStatus InitializationResult() const;
- protected:
- virtual ~AudioEncoder();
+ void InsertAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time);
private:
- friend class base::RefCountedThreadSafe<AudioEncoder>;
-
class ImplBase;
class OpusImpl;
class Pcm16Impl;
- // Invokes |impl_|'s encode method on the AUDIO_ENCODER thread while holding
- // a ref-count on AudioEncoder.
- void EncodeAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback);
-
const scoped_refptr<CastEnvironment> cast_environment_;
- scoped_ptr<ImplBase> impl_;
+ scoped_refptr<ImplBase> impl_;
// Used to ensure only one thread invokes InsertAudio().
base::ThreadChecker insert_thread_checker_;
diff --git a/chromium/media/cast/audio_sender/audio_encoder_unittest.cc b/chromium/media/cast/audio_sender/audio_encoder_unittest.cc
index d721f71ef29..b521099243b 100644
--- a/chromium/media/cast/audio_sender/audio_encoder_unittest.cc
+++ b/chromium/media/cast/audio_sender/audio_encoder_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include <sstream>
#include <string>
@@ -13,53 +15,56 @@
#include "media/cast/audio_sender/audio_encoder.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/test/audio_utility.h"
-#include "media/cast/test/fake_task_runner.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/test/utility/audio_utility.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace cast {
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
+static const int64 kStartMillisecond = INT64_C(12345678900000);
namespace {
class TestEncodedAudioFrameReceiver {
public:
- explicit TestEncodedAudioFrameReceiver(AudioCodec codec) :
- codec_(codec), frames_received_(0) {}
+ explicit TestEncodedAudioFrameReceiver(transport::AudioCodec codec)
+ : codec_(codec), frames_received_(0), rtp_lower_bound_(0) {}
virtual ~TestEncodedAudioFrameReceiver() {}
- int frames_received() const {
- return frames_received_;
- }
-
- void SetRecordedTimeLowerBound(const base::TimeTicks& t) {
- lower_bound_ = t;
- }
+ int frames_received() const { return frames_received_; }
- void SetRecordedTimeUpperBound(const base::TimeTicks& t) {
- upper_bound_ = t;
+ void SetCaptureTimeBounds(const base::TimeTicks& lower_bound,
+ const base::TimeTicks& upper_bound) {
+ lower_bound_ = lower_bound;
+ upper_bound_ = upper_bound;
}
- void FrameEncoded(scoped_ptr<EncodedAudioFrame> encoded_frame,
- const base::TimeTicks& recorded_time) {
- EXPECT_EQ(codec_, encoded_frame->codec);
+ void FrameEncoded(scoped_ptr<transport::EncodedFrame> encoded_frame) {
+ EXPECT_EQ(encoded_frame->dependency, transport::EncodedFrame::KEY);
EXPECT_EQ(static_cast<uint8>(frames_received_ & 0xff),
encoded_frame->frame_id);
- EXPECT_LT(0, encoded_frame->samples);
+ EXPECT_EQ(encoded_frame->frame_id, encoded_frame->referenced_frame_id);
+ // RTP timestamps should be monotonically increasing and integer multiples
+ // of the fixed frame size.
+ EXPECT_LE(rtp_lower_bound_, encoded_frame->rtp_timestamp);
+ rtp_lower_bound_ = encoded_frame->rtp_timestamp;
+ // Note: In audio_encoder.cc, 100 is the fixed audio frame rate.
+ const int kSamplesPerFrame = kDefaultAudioSamplingRate / 100;
+ EXPECT_EQ(0u, encoded_frame->rtp_timestamp % kSamplesPerFrame);
EXPECT_TRUE(!encoded_frame->data.empty());
- EXPECT_LE(lower_bound_, recorded_time);
- lower_bound_ = recorded_time;
- EXPECT_GT(upper_bound_, recorded_time);
+ EXPECT_LE(lower_bound_, encoded_frame->reference_time);
+ lower_bound_ = encoded_frame->reference_time;
+ EXPECT_GT(upper_bound_, encoded_frame->reference_time);
++frames_received_;
}
private:
- const AudioCodec codec_;
+ const transport::AudioCodec codec_;
int frames_received_;
+ uint32 rtp_lower_bound_;
base::TimeTicks lower_bound_;
base::TimeTicks upper_bound_;
@@ -90,46 +95,48 @@ class AudioEncoderTest : public ::testing::TestWithParam<TestScenario> {
public:
AudioEncoderTest() {
InitializeMediaLibraryForTesting();
- testing_clock_.Advance(
+ testing_clock_ = new base::SimpleTestTickClock();
+ testing_clock_->Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
}
virtual void SetUp() {
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
+ 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 ~AudioEncoderTest() {}
- void RunTestForCodec(AudioCodec codec) {
+ void RunTestForCodec(transport::AudioCodec codec) {
const TestScenario& scenario = GetParam();
- SCOPED_TRACE(::testing::Message()
- << "Durations: " << scenario.ToString());
+ SCOPED_TRACE(::testing::Message() << "Durations: " << scenario.ToString());
CreateObjectsForCodec(codec);
- receiver_->SetRecordedTimeLowerBound(testing_clock_.NowTicks());
+ // Note: In audio_encoder.cc, 10 ms is the fixed frame duration.
+ const base::TimeDelta frame_duration =
+ base::TimeDelta::FromMilliseconds(10);
+
for (size_t i = 0; i < scenario.num_durations; ++i) {
- const base::TimeDelta duration =
- base::TimeDelta::FromMilliseconds(scenario.durations_in_ms[i]);
- receiver_->SetRecordedTimeUpperBound(
- testing_clock_.NowTicks() + duration);
-
- const scoped_ptr<AudioBus> bus(
- audio_bus_factory_->NextAudioBus(duration));
-
- const int last_count = release_callback_count_;
- audio_encoder_->InsertAudio(
- bus.get(), testing_clock_.NowTicks(),
- base::Bind(&AudioEncoderTest::IncrementReleaseCallbackCounter,
- base::Unretained(this)));
- task_runner_->RunTasks();
- EXPECT_EQ(1, release_callback_count_ - last_count)
- << "Release callback was not invoked once.";
-
- testing_clock_.Advance(duration);
+ const bool simulate_missing_data = scenario.durations_in_ms[i] < 0;
+ const base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
+ std::abs(scenario.durations_in_ms[i]));
+ receiver_->SetCaptureTimeBounds(
+ testing_clock_->NowTicks() - frame_duration,
+ testing_clock_->NowTicks() + duration);
+ if (simulate_missing_data) {
+ task_runner_->RunTasks();
+ testing_clock_->Advance(duration);
+ } else {
+ audio_encoder_->InsertAudio(audio_bus_factory_->NextAudioBus(duration),
+ testing_clock_->NowTicks());
+ task_runner_->RunTasks();
+ testing_clock_->Advance(duration);
+ }
}
DVLOG(1) << "Received " << receiver_->frames_received()
@@ -137,98 +144,101 @@ class AudioEncoderTest : public ::testing::TestWithParam<TestScenario> {
}
private:
- void CreateObjectsForCodec(AudioCodec codec) {
+ void CreateObjectsForCodec(transport::AudioCodec codec) {
AudioSenderConfig audio_config;
audio_config.codec = codec;
audio_config.use_external_encoder = false;
audio_config.frequency = kDefaultAudioSamplingRate;
audio_config.channels = 2;
audio_config.bitrate = kDefaultAudioEncoderBitrate;
- audio_config.rtp_payload_type = 127;
+ audio_config.rtp_config.payload_type = 127;
- audio_bus_factory_.reset(new TestAudioBusFactory(
- audio_config.channels, audio_config.frequency,
- TestAudioBusFactory::kMiddleANoteFreq, 0.5f));
+ audio_bus_factory_.reset(
+ new TestAudioBusFactory(audio_config.channels,
+ audio_config.frequency,
+ TestAudioBusFactory::kMiddleANoteFreq,
+ 0.5f));
receiver_.reset(new TestEncodedAudioFrameReceiver(codec));
- audio_encoder_ = new AudioEncoder(
- cast_environment_, audio_config,
+ audio_encoder_.reset(new AudioEncoder(
+ cast_environment_,
+ audio_config,
base::Bind(&TestEncodedAudioFrameReceiver::FrameEncoded,
- base::Unretained(receiver_.get())));
- release_callback_count_ = 0;
- }
-
- void IncrementReleaseCallbackCounter() {
- ++release_callback_count_;
+ base::Unretained(receiver_.get()))));
}
- base::SimpleTestTickClock testing_clock_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<TestAudioBusFactory> audio_bus_factory_;
scoped_ptr<TestEncodedAudioFrameReceiver> receiver_;
- scoped_refptr<AudioEncoder> audio_encoder_;
+ scoped_ptr<AudioEncoder> audio_encoder_;
scoped_refptr<CastEnvironment> cast_environment_;
- int release_callback_count_;
DISALLOW_COPY_AND_ASSIGN(AudioEncoderTest);
};
-TEST_P(AudioEncoderTest, EncodeOpus) {
- RunTestForCodec(kOpus);
-}
-
-TEST_P(AudioEncoderTest, EncodePcm16) {
- RunTestForCodec(kPcm16);
-}
-
-static const int64 kOneCall_3Millis[] = { 3 };
-static const int64 kOneCall_10Millis[] = { 10 };
-static const int64 kOneCall_13Millis[] = { 13 };
-static const int64 kOneCall_20Millis[] = { 20 };
-
-static const int64 kTwoCalls_3Millis[] = { 3, 3 };
-static const int64 kTwoCalls_10Millis[] = { 10, 10 };
-static const int64 kTwoCalls_Mixed1[] = { 3, 10 };
-static const int64 kTwoCalls_Mixed2[] = { 10, 3 };
-static const int64 kTwoCalls_Mixed3[] = { 3, 17 };
-static const int64 kTwoCalls_Mixed4[] = { 17, 3 };
-
-static const int64 kManyCalls_3Millis[] =
- { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
-static const int64 kManyCalls_10Millis[] =
- { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 };
-static const int64 kManyCalls_Mixed1[] =
- { 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10 };
-static const int64 kManyCalls_Mixed2[] =
- { 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3 };
-static const int64 kManyCalls_Mixed3[] =
- { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4 };
-static const int64 kManyCalls_Mixed4[] =
- { 31, 4, 15, 9, 26, 53, 5, 8, 9, 7, 9, 32, 38, 4, 62, 64, 3 };
-static const int64 kManyCalls_Mixed5[] =
- { 3, 14, 15, 9, 26, 53, 58, 9, 7, 9, 3, 23, 8, 4, 6, 2, 6, 43 };
+TEST_P(AudioEncoderTest, EncodeOpus) { RunTestForCodec(transport::kOpus); }
+
+TEST_P(AudioEncoderTest, EncodePcm16) { RunTestForCodec(transport::kPcm16); }
+
+static const int64 kOneCall_3Millis[] = {3};
+static const int64 kOneCall_10Millis[] = {10};
+static const int64 kOneCall_13Millis[] = {13};
+static const int64 kOneCall_20Millis[] = {20};
+
+static const int64 kTwoCalls_3Millis[] = {3, 3};
+static const int64 kTwoCalls_10Millis[] = {10, 10};
+static const int64 kTwoCalls_Mixed1[] = {3, 10};
+static const int64 kTwoCalls_Mixed2[] = {10, 3};
+static const int64 kTwoCalls_Mixed3[] = {3, 17};
+static const int64 kTwoCalls_Mixed4[] = {17, 3};
+
+static const int64 kManyCalls_3Millis[] = {3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3};
+static const int64 kManyCalls_10Millis[] = {10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10};
+static const int64 kManyCalls_Mixed1[] = {3, 10, 3, 10, 3, 10, 3, 10, 3,
+ 10, 3, 10, 3, 10, 3, 10, 3, 10};
+static const int64 kManyCalls_Mixed2[] = {10, 3, 10, 3, 10, 3, 10, 3, 10, 3,
+ 10, 3, 10, 3, 10, 3, 10, 3, 10, 3};
+static const int64 kManyCalls_Mixed3[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8,
+ 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4};
+static const int64 kManyCalls_Mixed4[] = {31, 4, 15, 9, 26, 53, 5, 8, 9,
+ 7, 9, 32, 38, 4, 62, 64, 3};
+static const int64 kManyCalls_Mixed5[] = {3, 14, 15, 9, 26, 53, 58, 9, 7,
+ 9, 3, 23, 8, 4, 6, 2, 6, 43};
+
+static const int64 kOneBigUnderrun[] = {10, 10, 10, 10, -1000, 10, 10, 10};
+static const int64 kTwoBigUnderruns[] = {10, 10, 10, 10, -712, 10, 10, 10,
+ -1311, 10, 10, 10};
+static const int64 kMixedUnderruns[] = {31, -64, 4, 15, 9, 26, -53, 5, 8, -9,
+ 7, 9, 32, 38, -4, 62, -64, 3};
INSTANTIATE_TEST_CASE_P(
- AudioEncoderTestScenarios, AudioEncoderTest,
+ AudioEncoderTestScenarios,
+ AudioEncoderTest,
::testing::Values(
- TestScenario(kOneCall_3Millis, arraysize(kOneCall_3Millis)),
- TestScenario(kOneCall_10Millis, arraysize(kOneCall_10Millis)),
- TestScenario(kOneCall_13Millis, arraysize(kOneCall_13Millis)),
- TestScenario(kOneCall_20Millis, arraysize(kOneCall_20Millis)),
- TestScenario(kTwoCalls_3Millis, arraysize(kTwoCalls_3Millis)),
- TestScenario(kTwoCalls_10Millis, arraysize(kTwoCalls_10Millis)),
- TestScenario(kTwoCalls_Mixed1, arraysize(kTwoCalls_Mixed1)),
- TestScenario(kTwoCalls_Mixed2, arraysize(kTwoCalls_Mixed2)),
- TestScenario(kTwoCalls_Mixed3, arraysize(kTwoCalls_Mixed3)),
- TestScenario(kTwoCalls_Mixed4, arraysize(kTwoCalls_Mixed4)),
- TestScenario(kManyCalls_3Millis, arraysize(kManyCalls_3Millis)),
- TestScenario(kManyCalls_10Millis, arraysize(kManyCalls_10Millis)),
- TestScenario(kManyCalls_Mixed1, arraysize(kManyCalls_Mixed1)),
- TestScenario(kManyCalls_Mixed2, arraysize(kManyCalls_Mixed2)),
- TestScenario(kManyCalls_Mixed3, arraysize(kManyCalls_Mixed3)),
- TestScenario(kManyCalls_Mixed4, arraysize(kManyCalls_Mixed4)),
- TestScenario(kManyCalls_Mixed5, arraysize(kManyCalls_Mixed5))));
+ TestScenario(kOneCall_3Millis, arraysize(kOneCall_3Millis)),
+ TestScenario(kOneCall_10Millis, arraysize(kOneCall_10Millis)),
+ TestScenario(kOneCall_13Millis, arraysize(kOneCall_13Millis)),
+ TestScenario(kOneCall_20Millis, arraysize(kOneCall_20Millis)),
+ TestScenario(kTwoCalls_3Millis, arraysize(kTwoCalls_3Millis)),
+ TestScenario(kTwoCalls_10Millis, arraysize(kTwoCalls_10Millis)),
+ TestScenario(kTwoCalls_Mixed1, arraysize(kTwoCalls_Mixed1)),
+ TestScenario(kTwoCalls_Mixed2, arraysize(kTwoCalls_Mixed2)),
+ TestScenario(kTwoCalls_Mixed3, arraysize(kTwoCalls_Mixed3)),
+ TestScenario(kTwoCalls_Mixed4, arraysize(kTwoCalls_Mixed4)),
+ TestScenario(kManyCalls_3Millis, arraysize(kManyCalls_3Millis)),
+ TestScenario(kManyCalls_10Millis, arraysize(kManyCalls_10Millis)),
+ TestScenario(kManyCalls_Mixed1, arraysize(kManyCalls_Mixed1)),
+ TestScenario(kManyCalls_Mixed2, arraysize(kManyCalls_Mixed2)),
+ TestScenario(kManyCalls_Mixed3, arraysize(kManyCalls_Mixed3)),
+ TestScenario(kManyCalls_Mixed4, arraysize(kManyCalls_Mixed4)),
+ TestScenario(kManyCalls_Mixed5, arraysize(kManyCalls_Mixed5)),
+ TestScenario(kOneBigUnderrun, arraysize(kOneBigUnderrun)),
+ TestScenario(kTwoBigUnderruns, arraysize(kTwoBigUnderruns)),
+ TestScenario(kMixedUnderruns, arraysize(kMixedUnderruns))));
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/audio_sender/audio_sender.cc b/chromium/media/cast/audio_sender/audio_sender.cc
index b1b177d3ec3..e56d634782c 100644
--- a/chromium/media/cast/audio_sender/audio_sender.cc
+++ b/chromium/media/cast/audio_sender/audio_sender.cc
@@ -7,203 +7,338 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
-#include "crypto/encryptor.h"
-#include "crypto/symmetric_key.h"
#include "media/cast/audio_sender/audio_encoder.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/net/rtp_sender/rtp_sender.h"
-#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
namespace media {
namespace cast {
+namespace {
-const int64 kMinSchedulingDelayMs = 1;
+const int kNumAggressiveReportsSentAtStart = 100;
+const int kMinSchedulingDelayMs = 1;
-class LocalRtcpAudioSenderFeedback : public RtcpSenderFeedback {
- public:
- explicit LocalRtcpAudioSenderFeedback(AudioSender* audio_sender)
- : audio_sender_(audio_sender) {
- }
+// TODO(miu): This should be specified in AudioSenderConfig, but currently it is
+// fixed to 100 FPS (i.e., 10 ms per frame), and AudioEncoder assumes this as
+// well.
+const int kAudioFrameRate = 100;
- virtual void OnReceivedCastFeedback(
- const RtcpCastMessage& cast_feedback) OVERRIDE {
- if (!cast_feedback.missing_frames_and_packets_.empty()) {
- audio_sender_->ResendPackets(cast_feedback.missing_frames_and_packets_);
- }
- VLOG(1) << "Received audio ACK "
- << static_cast<int>(cast_feedback.ack_frame_id_);
- }
+// Helper function to compute the maximum unacked audio frames that is sent.
+int GetMaxUnackedFrames(base::TimeDelta target_delay) {
+ // As long as it doesn't go over |kMaxUnackedFrames|, it is okay to send more
+ // audio data than the target delay would suggest. Audio packets are tiny and
+ // receiver has the ability to drop any one of the packets.
+ // We send up to three times of the target delay of audio frames.
+ int frames =
+ 1 + 3 * target_delay * kAudioFrameRate / base::TimeDelta::FromSeconds(1);
+ return std::min(kMaxUnackedFrames, frames);
+}
+} // namespace
- private:
- AudioSender* audio_sender_;
-};
+AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
+ const AudioSenderConfig& audio_config,
+ transport::CastTransportSender* const transport_sender)
+ : cast_environment_(cast_environment),
+ target_playout_delay_(base::TimeDelta::FromMilliseconds(
+ audio_config.rtp_config.max_delay_ms)),
+ transport_sender_(transport_sender),
+ max_unacked_frames_(GetMaxUnackedFrames(target_playout_delay_)),
+ configured_encoder_bitrate_(audio_config.bitrate),
+ rtcp_(cast_environment,
+ this,
+ transport_sender_,
+ NULL, // paced sender.
+ NULL,
+ audio_config.rtcp_mode,
+ base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval),
+ audio_config.rtp_config.ssrc,
+ audio_config.incoming_feedback_ssrc,
+ audio_config.rtcp_c_name,
+ AUDIO_EVENT),
+ rtp_timestamp_helper_(audio_config.frequency),
+ num_aggressive_rtcp_reports_sent_(0),
+ last_sent_frame_id_(0),
+ latest_acked_frame_id_(0),
+ duplicate_ack_counter_(0),
+ cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
+ weak_factory_(this) {
+ VLOG(1) << "max_unacked_frames " << max_unacked_frames_;
+ DCHECK_GT(max_unacked_frames_, 0);
-class LocalRtpSenderStatistics : public RtpSenderStatistics {
- public:
- explicit LocalRtpSenderStatistics(RtpSender* rtp_sender)
- : rtp_sender_(rtp_sender) {
+ if (!audio_config.use_external_encoder) {
+ audio_encoder_.reset(
+ new AudioEncoder(cast_environment,
+ audio_config,
+ base::Bind(&AudioSender::SendEncodedAudioFrame,
+ weak_factory_.GetWeakPtr())));
+ cast_initialization_status_ = audio_encoder_->InitializationResult();
+ } else {
+ NOTREACHED(); // No support for external audio encoding.
+ cast_initialization_status_ = STATUS_AUDIO_UNINITIALIZED;
}
- virtual void GetStatistics(const base::TimeTicks& now,
- RtcpSenderInfo* sender_info) OVERRIDE {
- rtp_sender_->RtpStatistics(now, sender_info);
- }
+ media::cast::transport::CastTransportAudioConfig transport_config;
+ transport_config.codec = audio_config.codec;
+ transport_config.rtp.config = audio_config.rtp_config;
+ transport_config.frequency = audio_config.frequency;
+ transport_config.channels = audio_config.channels;
+ transport_config.rtp.max_outstanding_frames = max_unacked_frames_;
+ transport_sender_->InitializeAudio(transport_config);
- private:
- RtpSender* rtp_sender_;
-};
+ rtcp_.SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize);
-AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig& audio_config,
- PacedPacketSender* const paced_packet_sender)
- : cast_environment_(cast_environment),
- rtp_sender_(cast_environment, &audio_config, NULL,
- paced_packet_sender),
- rtcp_feedback_(new LocalRtcpAudioSenderFeedback(this)),
- rtp_audio_sender_statistics_(
- new LocalRtpSenderStatistics(&rtp_sender_)),
- rtcp_(cast_environment,
- rtcp_feedback_.get(),
- paced_packet_sender,
- rtp_audio_sender_statistics_.get(),
- NULL,
- audio_config.rtcp_mode,
- base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval),
- audio_config.sender_ssrc,
- audio_config.incoming_feedback_ssrc,
- audio_config.rtcp_c_name),
- initialized_(false),
- weak_factory_(this) {
- if (audio_config.aes_iv_mask.size() == kAesKeySize &&
- audio_config.aes_key.size() == kAesKeySize) {
- iv_mask_ = audio_config.aes_iv_mask;
- crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
- crypto::SymmetricKey::AES, audio_config.aes_key);
- encryptor_.reset(new crypto::Encryptor());
- encryptor_->Init(key, crypto::Encryptor::CTR, std::string());
- } else if (audio_config.aes_iv_mask.size() != 0 ||
- audio_config.aes_key.size() != 0) {
- DCHECK(false) << "Invalid crypto configuration";
- }
- if (!audio_config.use_external_encoder) {
- audio_encoder_ = new AudioEncoder(
- cast_environment, audio_config,
- base::Bind(&AudioSender::SendEncodedAudioFrame,
- weak_factory_.GetWeakPtr()));
- }
+ memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_));
}
AudioSender::~AudioSender() {}
-void AudioSender::InitializeTimers() {
+void AudioSender::InsertAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (!initialized_) {
- initialized_ = true;
- ScheduleNextRtcpReport();
+ if (cast_initialization_status_ != STATUS_AUDIO_INITIALIZED) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(audio_encoder_.get()) << "Invalid internal state";
+
+ if (AreTooManyFramesInFlight()) {
+ VLOG(1) << "Dropping frame due to too many frames currently in-flight.";
+ return;
}
+
+ audio_encoder_->InsertAudio(audio_bus.Pass(), recorded_time);
}
-void AudioSender::InsertAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback) {
+void AudioSender::SendEncodedAudioFrame(
+ scoped_ptr<transport::EncodedFrame> encoded_frame) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(audio_encoder_.get()) << "Invalid internal state";
- // TODO(mikhal): Resolve calculation of the audio rtp_timestamp for logging.
- // This is a tmp solution to allow the code to build.
- cast_environment_->Logging()->InsertFrameEvent(kAudioFrameReceived,
- GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown);
- audio_encoder_->InsertAudio(audio_bus, recorded_time, done_callback);
+
+ const uint32 frame_id = encoded_frame->frame_id;
+
+ const bool is_first_frame_to_be_sent = last_send_time_.is_null();
+ last_send_time_ = cast_environment_->Clock()->NowTicks();
+ last_sent_frame_id_ = frame_id;
+ // If this is the first frame about to be sent, fake the value of
+ // |latest_acked_frame_id_| to indicate the receiver starts out all caught up.
+ // Also, schedule the periodic frame re-send checks.
+ if (is_first_frame_to_be_sent) {
+ latest_acked_frame_id_ = frame_id - 1;
+ ScheduleNextResendCheck();
+ }
+
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ last_send_time_, FRAME_ENCODED, AUDIO_EVENT, encoded_frame->rtp_timestamp,
+ frame_id, static_cast<int>(encoded_frame->data.size()),
+ encoded_frame->dependency == transport::EncodedFrame::KEY,
+ configured_encoder_bitrate_);
+ // Only use lowest 8 bits as key.
+ frame_id_to_rtp_timestamp_[frame_id & 0xff] = encoded_frame->rtp_timestamp;
+
+ DCHECK(!encoded_frame->reference_time.is_null());
+ rtp_timestamp_helper_.StoreLatestTime(encoded_frame->reference_time,
+ encoded_frame->rtp_timestamp);
+
+ // At the start of the session, it's important to send reports before each
+ // frame so that the receiver can properly compute playout times. The reason
+ // more than one report is sent is because transmission is not guaranteed,
+ // only best effort, so we send enough that one should almost certainly get
+ // through.
+ if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) {
+ // SendRtcpReport() will schedule future reports to be made if this is the
+ // last "aggressive report."
+ ++num_aggressive_rtcp_reports_sent_;
+ const bool is_last_aggressive_report =
+ (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart);
+ VLOG_IF(1, is_last_aggressive_report) << "Sending last aggressive report.";
+ SendRtcpReport(is_last_aggressive_report);
+ }
+
+ transport_sender_->InsertCodedAudioFrame(*encoded_frame);
+}
+
+void AudioSender::IncomingRtcpPacket(scoped_ptr<Packet> packet) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ rtcp_.IncomingRtcpPacket(&packet->front(), packet->size());
}
-void AudioSender::InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time,
- const base::Closure callback) {
+void AudioSender::ScheduleNextRtcpReport() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(audio_encoder_.get() == NULL) << "Invalid internal state";
+ base::TimeDelta time_to_next =
+ rtcp_.TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks();
- cast_environment_->Logging()->InsertFrameEvent(kAudioFrameReceived,
- GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown);
+ time_to_next = std::max(
+ time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
- if (encryptor_) {
- EncodedAudioFrame encrypted_frame;
- if (!EncryptAudioFrame(*audio_frame, &encrypted_frame)) {
- // Logging already done.
- return;
- }
- rtp_sender_.IncomingEncodedAudioFrame(&encrypted_frame, recorded_time);
+ cast_environment_->PostDelayedTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&AudioSender::SendRtcpReport,
+ weak_factory_.GetWeakPtr(),
+ true),
+ time_to_next);
+}
+
+void AudioSender::SendRtcpReport(bool schedule_future_reports) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
+ uint32 now_as_rtp_timestamp = 0;
+ if (rtp_timestamp_helper_.GetCurrentTimeAsRtpTimestamp(
+ now, &now_as_rtp_timestamp)) {
+ rtcp_.SendRtcpFromRtpSender(now, now_as_rtp_timestamp);
} else {
- rtp_sender_.IncomingEncodedAudioFrame(audio_frame, recorded_time);
+ // |rtp_timestamp_helper_| should have stored a mapping by this point.
+ NOTREACHED();
}
- callback.Run();
+ if (schedule_future_reports)
+ ScheduleNextRtcpReport();
}
-void AudioSender::SendEncodedAudioFrame(
- scoped_ptr<EncodedAudioFrame> audio_frame,
- const base::TimeTicks& recorded_time) {
+void AudioSender::ScheduleNextResendCheck() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- InitializeTimers();
- if (encryptor_) {
- EncodedAudioFrame encrypted_frame;
- if (!EncryptAudioFrame(*audio_frame.get(), &encrypted_frame)) {
- // Logging already done.
- return;
+ DCHECK(!last_send_time_.is_null());
+ base::TimeDelta time_to_next =
+ last_send_time_ - cast_environment_->Clock()->NowTicks() +
+ target_playout_delay_;
+ time_to_next = std::max(
+ time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+ cast_environment_->PostDelayedTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&AudioSender::ResendCheck, weak_factory_.GetWeakPtr()),
+ time_to_next);
+}
+
+void AudioSender::ResendCheck() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK(!last_send_time_.is_null());
+ const base::TimeDelta time_since_last_send =
+ cast_environment_->Clock()->NowTicks() - last_send_time_;
+ if (time_since_last_send > target_playout_delay_) {
+ if (latest_acked_frame_id_ == last_sent_frame_id_) {
+ // Last frame acked, no point in doing anything
+ } else {
+ VLOG(1) << "ACK timeout; last acked frame: " << latest_acked_frame_id_;
+ ResendForKickstart();
}
- rtp_sender_.IncomingEncodedAudioFrame(&encrypted_frame, recorded_time);
- } else {
- rtp_sender_.IncomingEncodedAudioFrame(audio_frame.get(), recorded_time);
}
+ ScheduleNextResendCheck();
}
-bool AudioSender::EncryptAudioFrame(const EncodedAudioFrame& audio_frame,
- EncodedAudioFrame* encrypted_frame) {
- DCHECK(encryptor_) << "Invalid state";
+void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (!encryptor_->SetCounter(GetAesNonce(audio_frame.frame_id, iv_mask_))) {
- NOTREACHED() << "Failed to set counter";
- return false;
+ if (rtcp_.is_rtt_available()) {
+ // Having the RTT values implies the receiver sent back a receiver report
+ // based on it having received a report from here. Therefore, ensure this
+ // sender stops aggressively sending reports.
+ if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) {
+ VLOG(1) << "No longer a need to send reports aggressively (sent "
+ << num_aggressive_rtcp_reports_sent_ << ").";
+ num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart;
+ ScheduleNextRtcpReport();
+ }
}
- if (!encryptor_->Encrypt(audio_frame.data, &encrypted_frame->data)) {
- NOTREACHED() << "Encrypt error";
- return false;
+
+ if (last_send_time_.is_null())
+ return; // Cannot get an ACK without having first sent a frame.
+
+ if (cast_feedback.missing_frames_and_packets_.empty()) {
+ // We only count duplicate ACKs when we have sent newer frames.
+ if (latest_acked_frame_id_ == cast_feedback.ack_frame_id_ &&
+ latest_acked_frame_id_ != last_sent_frame_id_) {
+ duplicate_ack_counter_++;
+ } else {
+ duplicate_ack_counter_ = 0;
+ }
+ // TODO(miu): The values "2" and "3" should be derived from configuration.
+ if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) {
+ VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_;
+ ResendForKickstart();
+ }
+ } else {
+ // Only count duplicated ACKs if there is no NACK request in between.
+ // This is to avoid aggresive resend.
+ duplicate_ack_counter_ = 0;
+
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+ rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
+
+ // A NACK is also used to cancel pending re-transmissions.
+ transport_sender_->ResendPackets(
+ true, cast_feedback.missing_frames_and_packets_, false, min_rtt);
}
- encrypted_frame->codec = audio_frame.codec;
- encrypted_frame->frame_id = audio_frame.frame_id;
- encrypted_frame->samples = audio_frame.samples;
- return true;
-}
-void AudioSender::ResendPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- rtp_sender_.ResendPackets(missing_frames_and_packets);
+ const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
+
+ const RtpTimestamp rtp_timestamp =
+ frame_id_to_rtp_timestamp_[cast_feedback.ack_frame_id_ & 0xff];
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_ACK_RECEIVED,
+ AUDIO_EVENT,
+ rtp_timestamp,
+ cast_feedback.ack_frame_id_);
+
+ const bool is_acked_out_of_order =
+ static_cast<int32>(cast_feedback.ack_frame_id_ -
+ latest_acked_frame_id_) < 0;
+ VLOG(2) << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "")
+ << " for frame " << cast_feedback.ack_frame_id_;
+ if (!is_acked_out_of_order) {
+ // Cancel resends of acked frames.
+ MissingFramesAndPacketsMap missing_frames_and_packets;
+ PacketIdSet missing;
+ while (latest_acked_frame_id_ != cast_feedback.ack_frame_id_) {
+ latest_acked_frame_id_++;
+ missing_frames_and_packets[latest_acked_frame_id_] = missing;
+ }
+ transport_sender_->ResendPackets(
+ true, missing_frames_and_packets, true, base::TimeDelta());
+ latest_acked_frame_id_ = cast_feedback.ack_frame_id_;
+ }
}
-void AudioSender::IncomingRtcpPacket(const uint8* packet, size_t length,
- const base::Closure callback) {
+bool AudioSender::AreTooManyFramesInFlight() const {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- rtcp_.IncomingRtcpPacket(packet, length);
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
+ int frames_in_flight = 0;
+ if (!last_send_time_.is_null()) {
+ frames_in_flight +=
+ static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_);
+ }
+ VLOG(2) << frames_in_flight
+ << " frames in flight; last sent: " << last_sent_frame_id_
+ << " latest acked: " << latest_acked_frame_id_;
+ return frames_in_flight >= max_unacked_frames_;
}
-void AudioSender::ScheduleNextRtcpReport() {
+void AudioSender::ResendForKickstart() {
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));
+ DCHECK(!last_send_time_.is_null());
+ VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
+ << " to kick-start.";
+ // Send the first packet of the last encoded frame to kick start
+ // retransmission. This gives enough information to the receiver what
+ // packets and frames are missing.
+ MissingFramesAndPacketsMap missing_frames_and_packets;
+ PacketIdSet missing;
+ missing.insert(kRtcpCastLastPacket);
+ missing_frames_and_packets.insert(
+ std::make_pair(last_sent_frame_id_, missing));
+ last_send_time_ = cast_environment_->Clock()->NowTicks();
- cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioSender::SendRtcpReport, weak_factory_.GetWeakPtr()),
- time_to_next);
-}
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+ rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
-void AudioSender::SendRtcpReport() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- // We don't send audio logging messages since all captured audio frames will
- // be sent.
- rtcp_.SendRtcpFromRtpSender(NULL);
- ScheduleNextRtcpReport();
+ // Sending this extra packet is to kick-start the session. There is
+ // no need to optimize re-transmission for this case.
+ transport_sender_->ResendPackets(
+ true, missing_frames_and_packets, false, min_rtt);
}
} // namespace cast
diff --git a/chromium/media/cast/audio_sender/audio_sender.gypi b/chromium/media/cast/audio_sender/audio_sender.gypi
deleted file mode 100644
index 9d84b79af8d..00000000000
--- a/chromium/media/cast/audio_sender/audio_sender.gypi
+++ /dev/null
@@ -1,32 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'audio_sender',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- ],
- 'sources': [
- 'audio_encoder.h',
- 'audio_encoder.cc',
- 'audio_sender.h',
- 'audio_sender.cc',
- ], # source
- 'dependencies': [
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- '<(DEPTH)/media/media.gyp:media',
- '<(DEPTH)/media/media.gyp:shared_memory_support',
- '<(DEPTH)/media/cast/rtcp/rtcp.gyp:cast_rtcp',
- '<(DEPTH)/media/cast/net/rtp_sender/rtp_sender.gyp:*',
- '<(DEPTH)/third_party/opus/opus.gyp:opus',
- ],
- },
- ],
-}
-
-
diff --git a/chromium/media/cast/audio_sender/audio_sender.h b/chromium/media/cast/audio_sender/audio_sender.h
index 68f9e7a4172..80cf8a4e9e9 100644
--- a/chromium/media/cast/audio_sender/audio_sender.h
+++ b/chromium/media/cast/audio_sender/audio_sender.h
@@ -12,89 +12,146 @@
#include "base/threading/non_thread_safe.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
+#include "media/base/audio_bus.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/rtp_sender/rtp_sender.h"
+#include "media/cast/logging/logging_defines.h"
#include "media/cast/rtcp/rtcp.h"
-
-namespace crypto {
- class Encryptor;
-}
-
-namespace media {
-class AudioBus;
-}
+#include "media/cast/rtp_timestamp_helper.h"
namespace media {
namespace cast {
class AudioEncoder;
-class LocalRtcpAudioSenderFeedback;
-class LocalRtpSenderStatistics;
-class PacedPacketSender;
-// This class is not thread safe.
-// It's only called from the main cast thread.
-class AudioSender : public base::NonThreadSafe,
+// Not thread safe. Only called from the main cast thread.
+// This class owns all objects related to sending audio, objects that create RTP
+// packets, congestion control, audio encoder, parsing and sending of
+// RTCP packets.
+// Additionally it posts a bunch of delayed tasks to the main thread for various
+// timeouts.
+class AudioSender : public RtcpSenderFeedback,
+ public base::NonThreadSafe,
public base::SupportsWeakPtr<AudioSender> {
public:
AudioSender(scoped_refptr<CastEnvironment> cast_environment,
const AudioSenderConfig& audio_config,
- PacedPacketSender* const paced_packet_sender);
+ transport::CastTransportSender* const transport_sender);
virtual ~AudioSender();
- // The |audio_bus| must be valid until the |done_callback| is called.
- // The callback is called from the main cast thread as soon as the encoder is
- // done with |audio_bus|; it does not mean that the encoded data has been
- // sent out.
- void InsertAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback);
-
- // The audio_frame must be valid until the closure callback is called.
- // The closure callback is called from the main cast thread as soon as
- // the cast sender is done with the frame; it does not mean that the encoded
- // frame has been sent out.
- void InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time,
- const base::Closure callback);
+ CastInitializationStatus InitializationResult() const {
+ return cast_initialization_status_;
+ }
+
+ // Note: It is not guaranteed that |audio_frame| will actually be encoded and
+ // sent, if AudioSender detects too many frames in flight. Therefore, clients
+ // should be careful about the rate at which this method is called.
+ //
+ // Note: It is invalid to call this method if InitializationResult() returns
+ // anything but STATUS_AUDIO_INITIALIZED.
+ void InsertAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time);
// Only called from the main cast thread.
- void IncomingRtcpPacket(const uint8* packet, size_t length,
- const base::Closure callback);
+ void IncomingRtcpPacket(scoped_ptr<Packet> packet);
protected:
- void SendEncodedAudioFrame(scoped_ptr<EncodedAudioFrame> audio_frame,
- const base::TimeTicks& recorded_time);
+ // Protected for testability.
+ virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback)
+ OVERRIDE;
private:
- friend class LocalRtcpAudioSenderFeedback;
+ // Schedule and execute periodic sending of RTCP report.
+ void ScheduleNextRtcpReport();
+ void SendRtcpReport(bool schedule_future_reports);
+
+ // Schedule and execute periodic checks for re-sending packets. If no
+ // acknowledgements have been received for "too long," AudioSender will
+ // speculatively re-send certain packets of an unacked frame to kick-start
+ // re-transmission. This is a last resort tactic to prevent the session from
+ // getting stuck after a long outage.
+ void ScheduleNextResendCheck();
+ void ResendCheck();
+ void ResendForKickstart();
+
+ // Returns true if there are too many frames in flight, as defined by the
+ // configured target playout delay plus simple logic. When this is true,
+ // InsertAudio() will silenty drop frames instead of sending them to the audio
+ // encoder.
+ bool AreTooManyFramesInFlight() const;
+
+ // Called by the |audio_encoder_| with the next EncodedFrame to send.
+ void SendEncodedAudioFrame(scoped_ptr<transport::EncodedFrame> audio_frame);
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+
+ // 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_;
+
+ // Sends encoded frames over the configured transport (e.g., UDP). In
+ // Chromium, this could be a proxy that first sends the frames from a renderer
+ // process to the browser process over IPC, with the browser process being
+ // responsible for "packetizing" the frames and pushing packets into the
+ // network layer.
+ transport::CastTransportSender* const transport_sender_;
+
+ // Maximum number of outstanding frames before the encoding and sending of
+ // new frames shall halt.
+ const int max_unacked_frames_;
+
+ // Encodes AudioBuses into EncodedFrames.
+ scoped_ptr<AudioEncoder> audio_encoder_;
+ const int configured_encoder_bitrate_;
+
+ // Manages sending/receiving of RTCP packets, including sender/receiver
+ // reports.
+ Rtcp rtcp_;
- void ResendPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets);
+ // Records lip-sync (i.e., mapping of RTP <--> NTP timestamps), and
+ // extrapolates this mapping to any other point in time.
+ RtpTimestampHelper rtp_timestamp_helper_;
- // Caller must allocate the destination |encrypted_frame|. The data member
- // will be resized to hold the encrypted size.
- bool EncryptAudioFrame(const EncodedAudioFrame& audio_frame,
- EncodedAudioFrame* encrypted_frame);
+ // Counts how many RTCP reports are being "aggressively" sent (i.e., one per
+ // frame) at the start of the session. Once a threshold is reached, RTCP
+ // reports are instead sent at the configured interval + random drift.
+ int num_aggressive_rtcp_reports_sent_;
- void ScheduleNextRtcpReport();
- void SendRtcpReport();
+ // This is "null" until the first frame is sent. Thereafter, this tracks the
+ // last time any frame was sent or re-sent.
+ base::TimeTicks last_send_time_;
- void InitializeTimers();
+ // The ID of the last frame sent. Logic throughout AudioSender assumes this
+ // can safely wrap-around. This member is invalid until
+ // |!last_send_time_.is_null()|.
+ uint32 last_sent_frame_id_;
- base::WeakPtrFactory<AudioSender> weak_factory_;
+ // The ID of the latest (not necessarily the last) frame that has been
+ // acknowledged. Logic throughout AudioSender assumes this can safely
+ // wrap-around. This member is invalid until |!last_send_time_.is_null()|.
+ uint32 latest_acked_frame_id_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_refptr<AudioEncoder> audio_encoder_;
- RtpSender rtp_sender_;
- scoped_ptr<LocalRtpSenderStatistics> rtp_audio_sender_statistics_;
- scoped_ptr<LocalRtcpAudioSenderFeedback> rtcp_feedback_;
- Rtcp rtcp_;
- bool initialized_;
- scoped_ptr<crypto::Encryptor> encryptor_;
- std::string iv_mask_;
+ // Counts the number of duplicate ACK that are being received. When this
+ // number reaches a threshold, the sender will take this as a sign that the
+ // receiver hasn't yet received the first packet of the next frame. In this
+ // case, AudioSender will trigger a re-send of the next frame.
+ int duplicate_ack_counter_;
+
+ // If this sender is ready for use, this is STATUS_AUDIO_INITIALIZED.
+ CastInitializationStatus cast_initialization_status_;
+
+ // This is a "good enough" mapping for finding the RTP timestamp associated
+ // with a video frame. The key is the lowest 8 bits of frame id (which is
+ // what is sent via RTCP). This map is used for logging purposes.
+ RtpTimestamp frame_id_to_rtp_timestamp_[256];
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<AudioSender> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AudioSender);
};
diff --git a/chromium/media/cast/audio_sender/audio_sender_unittest.cc b/chromium/media/cast/audio_sender/audio_sender_unittest.cc
index 65c2e622d8f..51edd496028 100644
--- a/chromium/media/cast/audio_sender/audio_sender_unittest.cc
+++ b/chromium/media/cast/audio_sender/audio_sender_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
@@ -10,92 +12,129 @@
#include "media/cast/audio_sender/audio_sender.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/pacing/mock_paced_packet_sender.h"
-#include "media/cast/test/audio_utility.h"
-#include "media/cast/test/fake_task_runner.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/test/utility/audio_utility.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_sender_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace cast {
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
+class TestPacketSender : public transport::PacketSender {
+ public:
+ TestPacketSender() : number_of_rtp_packets_(0), number_of_rtcp_packets_(0) {}
+
+ virtual bool SendPacket(transport::PacketRef packet,
+ const base::Closure& cb) OVERRIDE {
+ if (Rtcp::IsRtcpPacket(&packet->data[0], packet->data.size())) {
+ ++number_of_rtcp_packets_;
+ } else {
+ // Check that at least one RTCP packet was sent before the first RTP
+ // packet. This confirms that the receiver will have the necessary lip
+ // sync info before it has to calculate the playout time of the first
+ // frame.
+ if (number_of_rtp_packets_ == 0)
+ EXPECT_LE(1, number_of_rtcp_packets_);
+ ++number_of_rtp_packets_;
+ }
+ return true;
+ }
+
+ int number_of_rtp_packets() const { return number_of_rtp_packets_; }
+
+ int number_of_rtcp_packets() const { return number_of_rtcp_packets_; }
-using testing::_;
-using testing::AtLeast;
+ private:
+ int number_of_rtp_packets_;
+ int number_of_rtcp_packets_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
+};
class AudioSenderTest : public ::testing::Test {
protected:
AudioSenderTest() {
InitializeMediaLibraryForTesting();
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- }
-
- virtual void SetUp() {
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
- audio_config_.codec = kOpus;
+ testing_clock_ = new base::SimpleTestTickClock();
+ testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
+ task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_);
+ cast_environment_ =
+ new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_);
+ audio_config_.codec = transport::kOpus;
audio_config_.use_external_encoder = false;
audio_config_.frequency = kDefaultAudioSamplingRate;
audio_config_.channels = 2;
audio_config_.bitrate = kDefaultAudioEncoderBitrate;
- audio_config_.rtp_payload_type = 127;
-
- audio_sender_.reset(
- new AudioSender(cast_environment_, audio_config_, &mock_transport_));
+ audio_config_.rtp_config.payload_type = 127;
+
+ net::IPEndPoint dummy_endpoint;
+
+ transport_sender_.reset(new transport::CastTransportSenderImpl(
+ NULL,
+ testing_clock_,
+ dummy_endpoint,
+ base::Bind(&UpdateCastTransportStatus),
+ transport::BulkRawEventsCallback(),
+ base::TimeDelta(),
+ task_runner_,
+ &transport_));
+ audio_sender_.reset(new AudioSender(
+ cast_environment_, audio_config_, transport_sender_.get()));
+ task_runner_->RunTasks();
}
virtual ~AudioSenderTest() {}
- base::SimpleTestTickClock testing_clock_;
- MockPacedPacketSender mock_transport_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
+ static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
+ EXPECT_EQ(transport::TRANSPORT_AUDIO_INITIALIZED, status);
+ }
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ TestPacketSender transport_;
+ scoped_ptr<transport::CastTransportSenderImpl> transport_sender_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<AudioSender> audio_sender_;
scoped_refptr<CastEnvironment> cast_environment_;
AudioSenderConfig audio_config_;
};
TEST_F(AudioSenderTest, Encode20ms) {
- EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1));
-
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(20);
- scoped_ptr<AudioBus> bus(TestAudioBusFactory(
- audio_config_.channels, audio_config_.frequency,
- TestAudioBusFactory::kMiddleANoteFreq, 0.5f).NextAudioBus(kDuration));
-
- base::TimeTicks recorded_time = base::TimeTicks::Now();
- audio_sender_->InsertAudio(
- bus.get(), recorded_time,
- base::Bind(base::IgnoreResult(&scoped_ptr<AudioBus>::release),
- base::Unretained(&bus)));
- task_runner_->RunTasks();
+ scoped_ptr<AudioBus> bus(
+ TestAudioBusFactory(audio_config_.channels,
+ audio_config_.frequency,
+ TestAudioBusFactory::kMiddleANoteFreq,
+ 0.5f).NextAudioBus(kDuration));
- EXPECT_TRUE(!bus) << "AudioBus wasn't released after use.";
+ audio_sender_->InsertAudio(bus.Pass(), testing_clock_->NowTicks());
+ task_runner_->RunTasks();
+ EXPECT_LE(1, transport_.number_of_rtp_packets());
+ EXPECT_LE(1, transport_.number_of_rtcp_packets());
}
TEST_F(AudioSenderTest, RtcpTimer) {
- EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1));
- EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).Times(1);
-
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(20);
- scoped_ptr<AudioBus> bus(TestAudioBusFactory(
- audio_config_.channels, audio_config_.frequency,
- TestAudioBusFactory::kMiddleANoteFreq, 0.5f).NextAudioBus(kDuration));
-
- base::TimeTicks recorded_time = base::TimeTicks::Now();
- audio_sender_->InsertAudio(
- bus.get(), recorded_time,
- base::Bind(base::IgnoreResult(&scoped_ptr<AudioBus>::release),
- base::Unretained(&bus)));
+ scoped_ptr<AudioBus> bus(
+ TestAudioBusFactory(audio_config_.channels,
+ audio_config_.frequency,
+ TestAudioBusFactory::kMiddleANoteFreq,
+ 0.5f).NextAudioBus(kDuration));
+
+ audio_sender_->InsertAudio(bus.Pass(), testing_clock_->NowTicks());
task_runner_->RunTasks();
// Make sure that we send at least one RTCP packet.
base::TimeDelta max_rtcp_timeout =
base::TimeDelta::FromMilliseconds(1 + kDefaultRtcpIntervalMs * 3 / 2);
- testing_clock_.Advance(max_rtcp_timeout);
+ testing_clock_->Advance(max_rtcp_timeout);
task_runner_->RunTasks();
+ EXPECT_LE(1, transport_.number_of_rtp_packets());
+ EXPECT_LE(1, transport_.number_of_rtcp_packets());
}
} // namespace cast
diff --git a/chromium/media/cast/base/clock_drift_smoother.cc b/chromium/media/cast/base/clock_drift_smoother.cc
new file mode 100644
index 00000000000..ca0380533ee
--- /dev/null
+++ b/chromium/media/cast/base/clock_drift_smoother.cc
@@ -0,0 +1,58 @@
+// 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/base/clock_drift_smoother.h"
+
+#include "base/logging.h"
+
+namespace media {
+namespace cast {
+
+ClockDriftSmoother::ClockDriftSmoother(base::TimeDelta time_constant)
+ : time_constant_(time_constant),
+ estimate_us_(0.0) {
+ DCHECK(time_constant_ > base::TimeDelta());
+}
+
+ClockDriftSmoother::~ClockDriftSmoother() {}
+
+base::TimeDelta ClockDriftSmoother::Current() const {
+ DCHECK(!last_update_time_.is_null());
+ return base::TimeDelta::FromMicroseconds(
+ static_cast<int64>(estimate_us_ + 0.5)); // Round to nearest microsecond.
+}
+
+void ClockDriftSmoother::Reset(base::TimeTicks now,
+ base::TimeDelta measured_offset) {
+ DCHECK(!now.is_null());
+ last_update_time_ = now;
+ estimate_us_ = measured_offset.InMicroseconds();
+}
+
+void ClockDriftSmoother::Update(base::TimeTicks now,
+ base::TimeDelta measured_offset) {
+ DCHECK(!now.is_null());
+ if (last_update_time_.is_null()) {
+ Reset(now, measured_offset);
+ } else if (now < last_update_time_) {
+ // |now| is not monotonically non-decreasing.
+ NOTREACHED();
+ } else {
+ const double elapsed_us = (now - last_update_time_).InMicroseconds();
+ last_update_time_ = now;
+ const double weight =
+ elapsed_us / (elapsed_us + time_constant_.InMicroseconds());
+ estimate_us_ = weight * measured_offset.InMicroseconds() +
+ (1.0 - weight) * estimate_us_;
+ }
+}
+
+// static
+base::TimeDelta ClockDriftSmoother::GetDefaultTimeConstant() {
+ static const int kDefaultTimeConstantInSeconds = 30;
+ return base::TimeDelta::FromSeconds(kDefaultTimeConstantInSeconds);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/base/clock_drift_smoother.h b/chromium/media/cast/base/clock_drift_smoother.h
new file mode 100644
index 00000000000..67de4cb51a8
--- /dev/null
+++ b/chromium/media/cast/base/clock_drift_smoother.h
@@ -0,0 +1,52 @@
+// 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_BASE_CLOCK_DRIFT_SMOOTHER_H_
+#define MEDIA_CAST_BASE_CLOCK_DRIFT_SMOOTHER_H_
+
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+// Tracks the jitter and drift between clocks, providing a smoothed offset.
+// Internally, a Simple IIR filter is used to maintain a running average that
+// moves at a rate based on the passage of time.
+class ClockDriftSmoother {
+ public:
+ // |time_constant| is the amount of time an impulse signal takes to decay by
+ // ~62.6%. Interpretation: If the value passed to several Update() calls is
+ // held constant for T seconds, then the running average will have moved
+ // towards the value by ~62.6% from where it started.
+ explicit ClockDriftSmoother(base::TimeDelta time_constant);
+ ~ClockDriftSmoother();
+
+ // Returns the current offset.
+ base::TimeDelta Current() const;
+
+ // Discard all history and reset to exactly |offset|, measured |now|.
+ void Reset(base::TimeTicks now, base::TimeDelta offset);
+
+ // Update the current offset, which was measured |now|. The weighting that
+ // |measured_offset| will have on the running average is influenced by how
+ // much time has passed since the last call to this method (or Reset()).
+ // |now| should be monotonically non-decreasing over successive calls of this
+ // method.
+ void Update(base::TimeTicks now, base::TimeDelta measured_offset);
+
+ // Returns a time constant suitable for most use cases, where the clocks
+ // are expected to drift very little with respect to each other, and the
+ // jitter caused by clock imprecision is effectively canceled out.
+ static base::TimeDelta GetDefaultTimeConstant();
+
+ private:
+ const base::TimeDelta time_constant_;
+ base::TimeTicks last_update_time_;
+ double estimate_us_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_BASE_CLOCK_DRIFT_SMOOTHER_H_
diff --git a/chromium/media/cast/cast.gyp b/chromium/media/cast/cast.gyp
index 702272fb289..5de8796079a 100644
--- a/chromium/media/cast/cast.gyp
+++ b/chromium/media/cast/cast.gyp
@@ -7,15 +7,25 @@
'include_tests%': 1,
'chromium_code': 1,
},
+ 'conditions': [
+ ['include_tests==1', {
+ 'includes': [ 'cast_testing.gypi' ]
+ }],
+ ],
'targets': [
{
- 'target_name': 'cast_config',
+ 'target_name': 'cast_base',
'type': 'static_library',
'include_dirs': [
'<(DEPTH)/',
],
'dependencies': [
+ 'cast_logging_proto',
'<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/net/net.gyp:net',
+ ],
+ 'export_dependent_settings': [
+ 'cast_logging_proto',
],
'sources': [
'cast_config.cc',
@@ -23,136 +33,193 @@
'cast_defines.h',
'cast_environment.cc',
'cast_environment.h',
+ 'base/clock_drift_smoother.cc',
+ 'base/clock_drift_smoother.h',
+ 'logging/encoding_event_subscriber.cc',
+ 'logging/encoding_event_subscriber.h',
+ 'logging/log_deserializer.cc',
+ 'logging/log_deserializer.h',
+ 'logging/log_serializer.cc',
+ 'logging/log_serializer.h',
'logging/logging_defines.cc',
'logging/logging_defines.h',
'logging/logging_impl.cc',
'logging/logging_impl.h',
'logging/logging_raw.cc',
'logging/logging_raw.h',
- 'logging/logging_stats.cc',
- 'logging/logging_stats.h',
+ 'logging/raw_event_subscriber.h',
+ 'logging/raw_event_subscriber_bundle.cc',
+ 'logging/raw_event_subscriber_bundle.h',
+ 'logging/receiver_time_offset_estimator.h',
+ 'logging/receiver_time_offset_estimator_impl.cc',
+ 'logging/receiver_time_offset_estimator_impl.h',
+ 'logging/simple_event_subscriber.cc',
+ 'logging/simple_event_subscriber.h',
+ 'logging/stats_event_subscriber.cc',
+ 'logging/stats_event_subscriber.h',
+ 'rtp_timestamp_helper.cc',
+ 'rtp_timestamp_helper.h',
+ 'transport/cast_transport_config.cc',
+ 'transport/cast_transport_config.h',
+ 'transport/cast_transport_defines.h',
+ 'transport/cast_transport_sender.h',
+ ], # source
+ },
+ {
+ 'target_name': 'cast_logging_proto',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'sources': [
+ 'logging/proto/proto_utils.cc',
+ 'logging/proto/raw_events.proto',
+ ],
+ 'variables': {
+ 'proto_in_dir': 'logging/proto',
+ 'proto_out_dir': 'media/cast/logging/proto',
+ },
+ 'includes': ['../../build/protoc.gypi'],
+ },
+ {
+ 'target_name': 'cast_receiver',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_rtcp',
+ 'cast_transport',
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/media/media.gyp:media',
+ '<(DEPTH)/media/media.gyp:shared_memory_support',
+ '<(DEPTH)/third_party/opus/opus.gyp:opus',
+ '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx',
+ '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry',
+ ],
+ 'sources': [
+ 'cast_receiver.h',
+ 'framer/cast_message_builder.cc',
+ 'framer/cast_message_builder.h',
+ 'framer/frame_buffer.cc',
+ 'framer/frame_buffer.h',
+ 'framer/frame_id_map.cc',
+ '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_defines.cc',
+ 'rtp_receiver/rtp_receiver_defines.h',
+ 'rtp_receiver/rtp_parser/rtp_parser.cc',
+ 'rtp_receiver/rtp_parser/rtp_parser.h',
+ ], # source
+ },
+ {
+ 'target_name': 'cast_rtcp',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_transport',
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/net/net.gyp:net',
+ ],
+ 'sources': [
+ 'rtcp/rtcp_defines.cc',
+ 'rtcp/rtcp_defines.h',
+ 'rtcp/rtcp.h',
+ 'rtcp/rtcp.cc',
+ 'rtcp/rtcp_receiver.cc',
+ 'rtcp/rtcp_receiver.h',
+ 'rtcp/rtcp_sender.cc',
+ 'rtcp/rtcp_sender.h',
+ 'rtcp/rtcp_utility.cc',
+ 'rtcp/rtcp_utility.h',
+ 'rtcp/receiver_rtcp_event_subscriber.cc',
+ 'rtcp/receiver_rtcp_event_subscriber.cc',
+ ], # source
+ },
+ {
+ 'target_name': 'cast_sender',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_rtcp',
+ 'cast_transport',
+ '<(DEPTH)/media/media.gyp:media',
+ '<(DEPTH)/media/media.gyp:shared_memory_support',
+ '<(DEPTH)/third_party/opus/opus.gyp:opus',
+ '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx',
+ ], # dependencies
+ 'sources': [
+ 'audio_sender/audio_encoder.h',
+ 'audio_sender/audio_encoder.cc',
+ 'audio_sender/audio_sender.h',
+ 'audio_sender/audio_sender.cc',
+ 'cast_sender.h',
+ 'cast_sender_impl.cc',
+ 'cast_sender_impl.h',
+ 'congestion_control/congestion_control.h',
+ 'congestion_control/congestion_control.cc',
+ 'video_sender/codecs/vp8/vp8_encoder.cc',
+ 'video_sender/codecs/vp8/vp8_encoder.h',
+ 'video_sender/external_video_encoder.h',
+ 'video_sender/external_video_encoder.cc',
+ 'video_sender/fake_software_video_encoder.h',
+ 'video_sender/fake_software_video_encoder.cc',
+ 'video_sender/software_video_encoder.h',
+ 'video_sender/video_encoder.h',
+ 'video_sender/video_encoder_impl.h',
+ 'video_sender/video_encoder_impl.cc',
+ 'video_sender/video_sender.h',
+ 'video_sender/video_sender.cc',
+ ], # source
+ },
+ {
+ 'target_name': 'cast_transport',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/crypto/crypto.gyp:crypto',
+ '<(DEPTH)/net/net.gyp:net',
+ ],
+ 'sources': [
+ 'transport/cast_transport_sender_impl.cc',
+ 'transport/cast_transport_sender_impl.h',
+ 'transport/pacing/paced_sender.cc',
+ 'transport/pacing/paced_sender.h',
+ 'transport/rtcp/rtcp_builder.cc',
+ 'transport/rtcp/rtcp_builder.h',
+ 'transport/rtp_sender/packet_storage/packet_storage.cc',
+ 'transport/rtp_sender/packet_storage/packet_storage.h',
+ 'transport/rtp_sender/rtp_packetizer/rtp_packetizer.cc',
+ 'transport/rtp_sender/rtp_packetizer/rtp_packetizer.h',
+ 'transport/rtp_sender/rtp_sender.cc',
+ 'transport/rtp_sender/rtp_sender.h',
+ 'transport/transport/udp_transport.cc',
+ 'transport/transport/udp_transport.h',
+ 'transport/utility/transport_encryption_handler.cc',
+ 'transport/utility/transport_encryption_handler.h',
], # source
},
- ], # targets,
- 'conditions': [
- ['include_tests==1', {
- 'targets': [
- {
- 'target_name': 'cast_unittests',
- 'type': '<(gtest_target_type)',
- 'dependencies': [
- 'cast_config',
- 'cast_receiver.gyp:cast_receiver',
- 'cast_sender.gyp:cast_sender',
- 'test/utility/utility.gyp:cast_test_utility',
- '<(DEPTH)/base/base.gyp:run_all_unittests',
- '<(DEPTH)/base/base.gyp:test_support_base',
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- '<(DEPTH)/net/net.gyp:net',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- ],
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc/',
- ],
- 'sources': [
- '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',
- 'framer/cast_message_builder_unittest.cc',
- 'framer/frame_buffer_unittest.cc',
- 'framer/framer_unittest.cc',
- 'net/pacing/mock_paced_packet_sender.cc',
- 'net/pacing/mock_paced_packet_sender.h',
- 'net/pacing/paced_sender_unittest.cc',
- 'rtcp/mock_rtcp_receiver_feedback.cc',
- 'rtcp/mock_rtcp_receiver_feedback.h',
- 'rtcp/mock_rtcp_sender_feedback.cc',
- 'rtcp/mock_rtcp_sender_feedback.h',
- 'rtcp/rtcp_receiver_unittest.cc',
- 'rtcp/rtcp_sender_unittest.cc',
- 'rtcp/rtcp_unittest.cc',
- 'rtp_receiver/rtp_receiver_defines.h',
- 'rtp_receiver/mock_rtp_payload_feedback.cc',
- 'rtp_receiver/mock_rtp_payload_feedback.h',
- 'rtp_receiver/receiver_stats_unittest.cc',
- 'rtp_receiver/rtp_parser/test/rtp_packet_builder.cc',
- 'rtp_receiver/rtp_parser/rtp_parser_unittest.cc',
- 'net/rtp_sender/packet_storage/packet_storage_unittest.cc',
- 'net/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc',
- 'net/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc',
- 'net/rtp_sender/rtp_packetizer/test/rtp_header_parser.h',
- 'test/crypto_utility.cc',
- 'test/crypto_utility.h',
- 'test/encode_decode_test.cc',
- 'test/end2end_unittest.cc',
- 'video_receiver/video_decoder_unittest.cc',
- 'video_receiver/video_receiver_unittest.cc',
- 'video_sender/mock_video_encoder_controller.cc',
- 'video_sender/mock_video_encoder_controller.h',
- 'video_sender/video_encoder_unittest.cc',
- 'video_sender/video_sender_unittest.cc',
- ], # source
- },
- {
- 'target_name': 'cast_sender_app',
- 'type': 'executable',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'dependencies': [
- 'cast_config',
- '<(DEPTH)/ui/gfx/gfx.gyp:gfx',
- '<(DEPTH)/net/net.gyp:net_test_support',
- '<(DEPTH)/media/cast/cast_sender.gyp:*',
- '<(DEPTH)/media/media.gyp:media',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- '<(DEPTH)/third_party/opus/opus.gyp:opus',
- '<(DEPTH)/media/cast/test/transport/transport.gyp:cast_transport',
- '<(DEPTH)/media/cast/test/utility/utility.gyp:cast_test_utility',
- ],
- 'sources': [
- '<(DEPTH)/media/cast/test/sender.cc',
- ],
- },
- {
- 'target_name': 'cast_receiver_app',
- 'type': 'executable',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'dependencies': [
- 'cast_config',
- '<(DEPTH)/ui/gfx/gfx.gyp:gfx',
- '<(DEPTH)/net/net.gyp:net_test_support',
- '<(DEPTH)/media/cast/cast_receiver.gyp:*',
- '<(DEPTH)/media/media.gyp:media',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- '<(DEPTH)/media/cast/test/transport/transport.gyp:cast_transport',
- '<(DEPTH)/media/cast/test/utility/utility.gyp:cast_test_utility',
- '<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
- ],
- 'sources': [
- '<(DEPTH)/media/cast/test/receiver.cc',
- ],
- 'conditions': [
- ['OS == "linux"', {
- 'sources': [
- '<(DEPTH)/media/cast/test/linux_output_window.cc',
- '<(DEPTH)/media/cast/test/linux_output_window.h',
- ],
- 'libraries': [
- '-lXext',
- '-lX11',
- ],
- }],
- ],
- },
- ], # targets
- }], # include_tests
],
}
diff --git a/chromium/media/cast/cast_config.cc b/chromium/media/cast/cast_config.cc
index 6c324bd8759..0e7953af01e 100644
--- a/chromium/media/cast/cast_config.cc
+++ b/chromium/media/cast/cast_config.cc
@@ -7,48 +7,59 @@
namespace media {
namespace cast {
+// TODO(miu): Revisit code factoring of these structs. There are a number of
+// common elements between them all, so it might be reasonable to only have one
+// or two structs; or, at least a common base class.
+
+// TODO(miu): Make sure all POD members are initialized by ctors. Policy
+// decision: Reasonable defaults or use invalid placeholder values to expose
+// unset members?
+
+// TODO(miu): Provide IsValidConfig() functions?
+
+// TODO(miu): Throughout the code, there is a lot of copy-and-paste of the same
+// calculations based on these config values. So, why don't we add methods to
+// these classes to centralize the logic?
+
VideoSenderConfig::VideoSenderConfig()
- : rtcp_interval(kDefaultRtcpIntervalMs),
+ : incoming_feedback_ssrc(0),
+ rtcp_interval(kDefaultRtcpIntervalMs),
rtcp_mode(kRtcpReducedSize),
- rtp_history_ms(kDefaultRtpHistoryMs),
- rtp_max_delay_ms(kDefaultRtpMaxDelayMs),
+ use_external_encoder(false),
+ width(0),
+ height(0),
congestion_control_back_off(kDefaultCongestionControlBackOff),
+ max_bitrate(5000000),
+ min_bitrate(1000000),
+ start_bitrate(5000000),
max_qp(kDefaultMaxQp),
min_qp(kDefaultMinQp),
max_frame_rate(kDefaultMaxFrameRate),
- max_number_of_video_buffers_used(kDefaultNumberOfVideoBuffers) {}
+ max_number_of_video_buffers_used(kDefaultNumberOfVideoBuffers),
+ codec(transport::kVp8),
+ number_of_encode_threads(1) {}
AudioSenderConfig::AudioSenderConfig()
- : rtcp_interval(kDefaultRtcpIntervalMs),
+ : incoming_feedback_ssrc(0),
+ rtcp_interval(kDefaultRtcpIntervalMs),
rtcp_mode(kRtcpReducedSize),
- rtp_history_ms(kDefaultRtpHistoryMs),
- rtp_max_delay_ms(kDefaultRtpMaxDelayMs) {}
+ use_external_encoder(false),
+ frequency(0),
+ channels(0),
+ bitrate(0) {}
-AudioReceiverConfig::AudioReceiverConfig()
- : rtcp_interval(kDefaultRtcpIntervalMs),
- rtcp_mode(kRtcpReducedSize),
- rtp_max_delay_ms(kDefaultRtpMaxDelayMs) {}
-
-VideoReceiverConfig::VideoReceiverConfig()
- : rtcp_interval(kDefaultRtcpIntervalMs),
+FrameReceiverConfig::FrameReceiverConfig()
+ : feedback_ssrc(0),
+ incoming_ssrc(0),
+ rtcp_interval(kDefaultRtcpIntervalMs),
rtcp_mode(kRtcpReducedSize),
rtp_max_delay_ms(kDefaultRtpMaxDelayMs),
- max_frame_rate(kDefaultMaxFrameRate),
- decoder_faster_than_max_frame_rate(true) {}
-
-EncodedVideoFrame::EncodedVideoFrame() {}
-EncodedVideoFrame::~EncodedVideoFrame() {}
-
-EncodedAudioFrame::EncodedAudioFrame() {}
-EncodedAudioFrame::~EncodedAudioFrame() {}
-
-PcmAudioFrame::PcmAudioFrame() {}
-PcmAudioFrame::~PcmAudioFrame() {}
+ rtp_payload_type(0),
+ frequency(0),
+ channels(0),
+ max_frame_rate(0) {}
-// static
-void PacketReceiver::DeletePacket(const uint8* packet) {
- delete [] packet;
-}
+FrameReceiverConfig::~FrameReceiverConfig() {}
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/cast_config.h b/chromium/media/cast/cast_config.h
index 27cc67e5dae..ea25d6b6cf7 100644
--- a/chromium/media/cast/cast_config.h
+++ b/chromium/media/cast/cast_config.h
@@ -12,65 +12,53 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
+#include "base/single_thread_task_runner.h"
#include "media/cast/cast_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
namespace media {
+class VideoEncodeAccelerator;
+
namespace cast {
enum RtcpMode {
- kRtcpCompound, // Compound RTCP mode is described by RFC 4585.
+ kRtcpCompound, // Compound RTCP mode is described by RFC 4585.
kRtcpReducedSize, // Reduced-size RTCP mode is described by RFC 5506.
};
-enum VideoCodec {
- kVp8,
- kH264,
- kExternalVideo,
-};
-
-enum AudioCodec {
- kOpus,
- kPcm16,
- kExternalAudio,
-};
-
+// TODO(miu): Merge AudioSenderConfig and VideoSenderConfig and make their
+// naming/documentation consistent with FrameReceiverConfig.
struct AudioSenderConfig {
AudioSenderConfig();
- uint32 sender_ssrc;
+ // The sender ssrc is in rtp_config.ssrc.
uint32 incoming_feedback_ssrc;
int rtcp_interval;
std::string rtcp_c_name;
RtcpMode rtcp_mode;
- int rtp_history_ms; // The time RTP packets are stored for retransmissions.
- int rtp_max_delay_ms;
- int rtp_payload_type;
+ transport::RtpConfig rtp_config;
bool use_external_encoder;
int frequency;
int channels;
int bitrate; // Set to <= 0 for "auto variable bitrate" (libopus knows best).
- AudioCodec codec;
-
- std::string aes_key; // Binary string of size kAesKeySize.
- std::string aes_iv_mask; // Binary string of size kAesKeySize.
+ transport::AudioCodec codec;
};
struct VideoSenderConfig {
VideoSenderConfig();
- uint32 sender_ssrc;
+ // The sender ssrc is in rtp_config.ssrc.
uint32 incoming_feedback_ssrc;
int rtcp_interval;
std::string rtcp_c_name;
RtcpMode rtcp_mode;
- int rtp_history_ms; // The time RTP packets are stored for retransmissions.
- int rtp_max_delay_ms;
- int rtp_payload_type;
+ transport::RtpConfig rtp_config;
bool use_external_encoder;
int width; // Incoming frames will be scaled to this size.
@@ -84,148 +72,92 @@ struct VideoSenderConfig {
int min_qp;
int max_frame_rate;
int max_number_of_video_buffers_used; // Max value depend on codec.
- VideoCodec codec;
- int number_of_cores;
-
- std::string aes_key; // Binary string of size kAesKeySize.
- std::string aes_iv_mask; // Binary string of size kAesKeySize.
+ transport::VideoCodec codec;
+ int number_of_encode_threads;
};
-struct AudioReceiverConfig {
- AudioReceiverConfig();
-
- uint32 feedback_ssrc;
- uint32 incoming_ssrc;
-
- int rtcp_interval;
- std::string rtcp_c_name;
- RtcpMode rtcp_mode;
-
- // The time the receiver is prepared to wait for retransmissions.
- int rtp_max_delay_ms;
- int rtp_payload_type;
-
- bool use_external_decoder;
- int frequency;
- int channels;
- AudioCodec codec;
-
- std::string aes_key; // Binary string of size kAesKeySize.
- std::string aes_iv_mask; // Binary string of size kAesKeySize.
-};
+// TODO(miu): Naming and minor type changes are badly needed in a later CL.
+struct FrameReceiverConfig {
+ FrameReceiverConfig();
+ ~FrameReceiverConfig();
-struct VideoReceiverConfig {
- VideoReceiverConfig();
+ // The receiver's SSRC identifier.
+ uint32 feedback_ssrc; // TODO(miu): Rename to receiver_ssrc for clarity.
- uint32 feedback_ssrc;
- uint32 incoming_ssrc;
+ // The sender's SSRC identifier.
+ uint32 incoming_ssrc; // TODO(miu): Rename to sender_ssrc for clarity.
+ // Mean interval (in milliseconds) between RTCP reports.
+ // TODO(miu): Remove this since it's never not kDefaultRtcpIntervalMs.
int rtcp_interval;
- std::string rtcp_c_name;
- RtcpMode rtcp_mode;
-
- // The time the receiver is prepared to wait for retransmissions.
- int rtp_max_delay_ms;
- int rtp_payload_type;
-
- bool use_external_decoder;
- int max_frame_rate;
-
- // Some HW decoders can not run faster than the frame rate, preventing it
- // from catching up after a glitch.
- bool decoder_faster_than_max_frame_rate;
- VideoCodec codec;
-
- std::string aes_key; // Binary string of size kAesKeySize.
- std::string aes_iv_mask; // Binary string of size kAesKeySize.
-};
-
-struct EncodedVideoFrame {
- EncodedVideoFrame();
- ~EncodedVideoFrame();
-
- VideoCodec codec;
- bool key_frame;
- uint32 frame_id;
- uint32 last_referenced_frame_id;
- std::string data;
-};
-
-// DEPRECATED: Do not use in new code. Please migrate existing code to use
-// media::AudioBus.
-struct PcmAudioFrame {
- PcmAudioFrame();
- ~PcmAudioFrame();
-
- int channels; // Samples in interleaved stereo format. L0, R0, L1 ,R1 ,...
- int frequency;
- std::vector<int16> samples;
-};
-
-struct EncodedAudioFrame {
- EncodedAudioFrame();
- ~EncodedAudioFrame();
-
- AudioCodec codec;
- uint32 frame_id; // Needed to release the frame.
- int samples; // Needed send side to advance the RTP timestamp.
- // Not used receive side.
- // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration.
- static const int kMaxNumberOfSamples = 48 * 2 * 100;
- std::string data;
-};
-typedef std::vector<uint8> Packet;
-typedef std::vector<Packet> PacketList;
-
-class PacketSender {
- public:
- // All packets to be sent to the network will be delivered via these
- // functions.
- virtual bool SendPackets(const PacketList& packets) = 0;
+ // CNAME representing this receiver.
+ // TODO(miu): Remove this since it should be derived elsewhere (probably in
+ // the transport layer).
+ std::string rtcp_c_name;
- virtual bool SendPacket(const Packet& packet) = 0;
+ // Determines amount of detail in RTCP reports.
+ // TODO(miu): Remove this since it's never anything but kRtcpReducedSize.
+ RtcpMode rtcp_mode;
- virtual ~PacketSender() {}
-};
+ // 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.).
+ int rtp_max_delay_ms; // TODO(miu): Change to TimeDelta target_playout_delay.
-class PacketReceiver : public base::RefCountedThreadSafe<PacketReceiver> {
- public:
- // All packets received from the network should be delivered via this
- // function.
- virtual void ReceivedPacket(const uint8* packet, size_t length,
- const base::Closure callback) = 0;
+ // RTP payload type enum: Specifies the type/encoding of frame data.
+ int rtp_payload_type;
- static void DeletePacket(const uint8* packet);
+ // RTP timebase: The number of RTP units advanced per one second. For audio,
+ // this is the sampling rate. For video, by convention, this is 90 kHz.
+ int frequency; // TODO(miu): Rename to rtp_timebase for clarity.
- protected:
- virtual ~PacketReceiver() {}
+ // Number of channels. For audio, this is normally 2. For video, this must
+ // be 1 as Cast does not have support for stereoscopic video.
+ int channels;
- private:
- friend class base::RefCountedThreadSafe<PacketReceiver>;
+ // The target frame rate. For audio, this is normally 100 (i.e., frames have
+ // a duration of 10ms each). For video, this is normally 30, but any frame
+ // rate is supported.
+ int max_frame_rate; // TODO(miu): Rename to target_frame_rate.
+
+ // Codec used for the compression of signal data.
+ // TODO(miu): Merge the AudioCodec and VideoCodec enums into one so this union
+ // is not necessary.
+ union MergedCodecPlaceholder {
+ transport::AudioCodec audio;
+ transport::VideoCodec video;
+ MergedCodecPlaceholder() : audio(transport::kUnknownAudioCodec) {}
+ } codec;
+
+ // The AES crypto key and initialization vector. Each of these strings
+ // contains the data in binary form, of size kAesKeySize. If they are empty
+ // strings, crypto is not being used.
+ std::string aes_key;
+ std::string aes_iv_mask;
};
-class VideoEncoderController {
- public:
- // Inform the encoder about the new target bit rate.
- virtual void SetBitRate(int new_bit_rate) = 0;
+// import from media::cast::transport
+typedef transport::Packet Packet;
+typedef transport::PacketList PacketList;
- // Inform the encoder to not encode the next frame.
- // Note: this setting is sticky and should last until called with false.
- virtual void SkipNextFrame(bool skip_next_frame) = 0;
+typedef base::Callback<void(CastInitializationStatus)>
+ CastInitializationCallback;
- // Inform the encoder to encode the next frame as a key frame.
- virtual void GenerateKeyFrame() = 0;
+typedef base::Callback<void(scoped_refptr<base::SingleThreadTaskRunner>,
+ scoped_ptr<media::VideoEncodeAccelerator>)>
+ ReceiveVideoEncodeAcceleratorCallback;
+typedef base::Callback<void(const ReceiveVideoEncodeAcceleratorCallback&)>
+ CreateVideoEncodeAcceleratorCallback;
- // Inform the encoder to only reference frames older or equal to frame_id;
- virtual void LatestFrameIdToReference(uint32 frame_id) = 0;
-
- // Query the codec about how many frames it has skipped due to slow ACK.
- virtual int NumberOfSkippedFrames() const = 0;
-
- protected:
- virtual ~VideoEncoderController() {}
-};
+typedef base::Callback<void(scoped_ptr<base::SharedMemory>)>
+ ReceiveVideoEncodeMemoryCallback;
+typedef base::Callback<void(size_t size,
+ const ReceiveVideoEncodeMemoryCallback&)>
+ CreateVideoEncodeMemoryCallback;
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/cast_defines.h b/chromium/media/cast/cast_defines.h
index aad7ae2b1d8..64b20c96da6 100644
--- a/chromium/media/cast/cast_defines.h
+++ b/chromium/media/cast/cast_defines.h
@@ -5,6 +5,8 @@
#ifndef MEDIA_CAST_CAST_DEFINES_H_
#define MEDIA_CAST_CAST_DEFINES_H_
+#include <stdint.h>
+
#include <map>
#include <set>
@@ -12,6 +14,7 @@
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/time/time.h"
+#include "media/cast/transport/cast_transport_config.h"
namespace media {
namespace cast {
@@ -19,16 +22,33 @@ namespace cast {
const int64 kDontShowTimeoutMs = 33;
const float kDefaultCongestionControlBackOff = 0.875f;
const uint32 kVideoFrequency = 90000;
-const int64 kSkippedFramesCheckPeriodkMs = 10000;
-const uint32 kStartFrameId = GG_UINT32_C(0xffffffff);
+const uint32 kStartFrameId = UINT32_C(0xffffffff);
+
+// This is an important system-wide constant. This limits how much history the
+// implementation must retain in order to process the acknowledgements of past
+// frames.
+const int kMaxUnackedFrames = 255;
-// Number of skipped frames threshold in fps (as configured) per period above.
-const int kSkippedFramesThreshold = 3;
-const size_t kIpPacketSize = 1500;
+const size_t kMaxIpPacketSize = 1500;
const int kStartRttMs = 20;
const int64 kCastMessageUpdateIntervalMs = 33;
const int64 kNackRepeatIntervalMs = 30;
+enum CastInitializationStatus {
+ STATUS_AUDIO_UNINITIALIZED,
+ STATUS_VIDEO_UNINITIALIZED,
+ STATUS_AUDIO_INITIALIZED,
+ STATUS_VIDEO_INITIALIZED,
+ STATUS_INVALID_CAST_ENVIRONMENT,
+ STATUS_INVALID_CRYPTO_CONFIGURATION,
+ STATUS_UNSUPPORTED_AUDIO_CODEC,
+ STATUS_UNSUPPORTED_VIDEO_CODEC,
+ STATUS_INVALID_AUDIO_CONFIGURATION,
+ STATUS_INVALID_VIDEO_CONFIGURATION,
+ STATUS_GPU_ACCELERATION_NOT_SUPPORTED,
+ STATUS_GPU_ACCELERATION_ERROR,
+};
+
enum DefaultSettings {
kDefaultAudioEncoderBitrate = 0, // This means "auto," and may mean VBR.
kDefaultAudioSamplingRate = 48000,
@@ -41,17 +61,29 @@ enum DefaultSettings {
kDefaultRtpMaxDelayMs = 100,
};
+enum PacketType {
+ kNewPacket,
+ kNewPacketCompletingFrame,
+ kDuplicatePacket,
+ kTooOldPacket,
+};
+
+// kRtcpCastAllPacketsLost is used in PacketIDSet and
+// on the wire to mean that ALL packets for a particular
+// frame are lost.
const uint16 kRtcpCastAllPacketsLost = 0xffff;
+// kRtcpCastLastPacket is used in PacketIDSet to ask for
+// the last packet of a frame to be retransmitted.
+const uint16 kRtcpCastLastPacket = 0xfffe;
+
const size_t kMinLengthOfRtcp = 8;
// Basic RTP header + cast header.
const size_t kMinLengthOfRtp = 12 + 6;
-const size_t kAesBlockSize = 16;
-const size_t kAesKeySize = 16;
-
// Each uint16 represents one packet id within a cast frame.
+// Can also contain kRtcpCastAllPacketsLost and kRtcpCastLastPacket.
typedef std::set<uint16> PacketIdSet;
// Each uint8 represents one cast frame.
typedef std::map<uint8, PacketIdSet> MissingFramesAndPacketsMap;
@@ -62,20 +94,26 @@ typedef std::map<uint8, PacketIdSet> MissingFramesAndPacketsMap;
// January 1970, in NTP seconds.
// Network Time Protocol (NTP), which is in seconds relative to 0h UTC on
// 1 January 1900.
-static const int64 kUnixEpochInNtpSeconds = GG_INT64_C(2208988800);
+static const int64 kUnixEpochInNtpSeconds = INT64_C(2208988800);
// Magic fractional unit. Used to convert time (in microseconds) to/from
// fractional NTP seconds.
static const double kMagicFractionalUnit = 4.294967296E3;
+// The maximum number of Cast receiver events to keep in history for the
+// purpose of sending the events through RTCP.
+// The number chosen should be more than the number of events that can be
+// stored in a RTCP packet.
+static const size_t kReceiverRtcpEventHistorySize = 512;
+
inline bool IsNewerFrameId(uint32 frame_id, uint32 prev_frame_id) {
return (frame_id != prev_frame_id) &&
- static_cast<uint32>(frame_id - prev_frame_id) < 0x80000000;
+ static_cast<uint32>(frame_id - prev_frame_id) < 0x80000000;
}
inline bool IsNewerRtpTimestamp(uint32 timestamp, uint32 prev_timestamp) {
return (timestamp != prev_timestamp) &&
- static_cast<uint32>(timestamp - prev_timestamp) < 0x80000000;
+ static_cast<uint32>(timestamp - prev_timestamp) < 0x80000000;
}
inline bool IsOlderFrameId(uint32 frame_id, uint32 prev_frame_id) {
@@ -84,7 +122,7 @@ inline bool IsOlderFrameId(uint32 frame_id, uint32 prev_frame_id) {
inline bool IsNewerPacketId(uint16 packet_id, uint16 prev_packet_id) {
return (packet_id != prev_packet_id) &&
- static_cast<uint16>(packet_id - prev_packet_id) < 0x8000;
+ static_cast<uint16>(packet_id - prev_packet_id) < 0x8000;
}
inline bool IsNewerSequenceNumber(uint16 sequence_number,
@@ -107,13 +145,22 @@ inline base::TimeDelta ConvertFromNtpDiff(uint32 ntp_delay) {
return base::TimeDelta::FromMilliseconds(delay_ms);
}
-inline void ConvertTimeToFractions(int64 time_us,
+inline void ConvertTimeToFractions(int64 ntp_time_us,
uint32* seconds,
uint32* fractions) {
- DCHECK_GE(time_us, 0) << "Time must NOT be negative";
- *seconds = static_cast<uint32>(time_us / base::Time::kMicrosecondsPerSecond);
+ DCHECK_GE(ntp_time_us, 0) << "Time must NOT be negative";
+ const int64 seconds_component =
+ ntp_time_us / base::Time::kMicrosecondsPerSecond;
+ // NTP time will overflow in the year 2036. Also, make sure unit tests don't
+ // regress and use an origin past the year 2036. If this overflows here, the
+ // inverse calculation fails to compute the correct TimeTicks value, throwing
+ // off the entire system.
+ DCHECK_LT(seconds_component, INT64_C(4263431296))
+ << "One year left to fix the NTP year 2036 wrap-around issue!";
+ *seconds = static_cast<uint32>(seconds_component);
*fractions = static_cast<uint32>(
- (time_us % base::Time::kMicrosecondsPerSecond) * kMagicFractionalUnit);
+ (ntp_time_us % base::Time::kMicrosecondsPerSecond) *
+ kMagicFractionalUnit);
}
inline void ConvertTimeTicksToNtp(const base::TimeTicks& time,
@@ -122,7 +169,8 @@ inline void ConvertTimeTicksToNtp(const base::TimeTicks& time,
base::TimeDelta elapsed_since_unix_epoch =
time - base::TimeTicks::UnixEpoch();
- int64 ntp_time_us = elapsed_since_unix_epoch.InMicroseconds() +
+ int64 ntp_time_us =
+ elapsed_since_unix_epoch.InMicroseconds() +
(kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond);
ConvertTimeToFractions(ntp_time_us, ntp_seconds, ntp_fractions);
@@ -130,30 +178,19 @@ inline void ConvertTimeTicksToNtp(const base::TimeTicks& time,
inline base::TimeTicks ConvertNtpToTimeTicks(uint32 ntp_seconds,
uint32 ntp_fractions) {
- int64 ntp_time_us = static_cast<int64>(ntp_seconds) *
- base::Time::kMicrosecondsPerSecond +
- static_cast<int64>(ntp_fractions) / kMagicFractionalUnit;
+ int64 ntp_time_us =
+ static_cast<int64>(ntp_seconds) * base::Time::kMicrosecondsPerSecond +
+ static_cast<int64>(ntp_fractions) / kMagicFractionalUnit;
- base::TimeDelta elapsed_since_unix_epoch =
- base::TimeDelta::FromMicroseconds(ntp_time_us -
- (kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond));
+ base::TimeDelta elapsed_since_unix_epoch = base::TimeDelta::FromMicroseconds(
+ ntp_time_us -
+ (kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond));
return base::TimeTicks::UnixEpoch() + elapsed_since_unix_epoch;
}
-inline std::string GetAesNonce(uint32 frame_id, const std::string& iv_mask) {
- std::string aes_nonce(kAesBlockSize, 0);
-
- // Serializing frame_id in big-endian order (aes_nonce[8] is the most
- // significant byte of frame_id).
- aes_nonce[11] = frame_id & 0xff;
- aes_nonce[10] = (frame_id >> 8) & 0xff;
- aes_nonce[9] = (frame_id >> 16) & 0xff;
- aes_nonce[8] = (frame_id >> 24) & 0xff;
-
- for (size_t i = 0; i < kAesBlockSize; ++i) {
- aes_nonce[i] ^= iv_mask[i];
- }
- return aes_nonce;
+inline base::TimeDelta RtpDeltaToTimeDelta(int64 rtp_delta, int rtp_timebase) {
+ DCHECK_GT(rtp_timebase, 0);
+ return rtp_delta * base::TimeDelta::FromSeconds(1) / rtp_timebase;
}
inline uint32 GetVideoRtpTimestamp(const base::TimeTicks& time_ticks) {
diff --git a/chromium/media/cast/cast_environment.cc b/chromium/media/cast/cast_environment.cc
index be636bb253d..93eb8c72522 100644
--- a/chromium/media/cast/cast_environment.cc
+++ b/chromium/media/cast/cast_environment.cc
@@ -4,65 +4,66 @@
#include "media/cast/cast_environment.h"
+#include "base/bind.h"
+#include "base/location.h"
#include "base/logging.h"
-using base::TaskRunner;
+using base::SingleThreadTaskRunner;
+
+namespace {
+
+void DeleteLoggingOnMainThread(scoped_ptr<media::cast::LoggingImpl> logging) {
+ logging.reset();
+}
+
+} // namespace
namespace media {
namespace cast {
CastEnvironment::CastEnvironment(
- base::TickClock* clock,
- scoped_refptr<TaskRunner> main_thread_proxy,
- scoped_refptr<TaskRunner> audio_encode_thread_proxy,
- scoped_refptr<TaskRunner> audio_decode_thread_proxy,
- scoped_refptr<TaskRunner> video_encode_thread_proxy,
- scoped_refptr<TaskRunner> video_decode_thread_proxy,
- const CastLoggingConfig& config)
- : clock_(clock),
- main_thread_proxy_(main_thread_proxy),
- audio_encode_thread_proxy_(audio_encode_thread_proxy),
- audio_decode_thread_proxy_(audio_decode_thread_proxy),
- video_encode_thread_proxy_(video_encode_thread_proxy),
- video_decode_thread_proxy_(video_decode_thread_proxy),
- logging_(new LoggingImpl(clock, main_thread_proxy, config)) {
- DCHECK(main_thread_proxy) << "Main thread required";
-}
+ scoped_ptr<base::TickClock> clock,
+ scoped_refptr<SingleThreadTaskRunner> main_thread_proxy,
+ scoped_refptr<SingleThreadTaskRunner> audio_thread_proxy,
+ scoped_refptr<SingleThreadTaskRunner> video_thread_proxy)
+ : main_thread_proxy_(main_thread_proxy),
+ audio_thread_proxy_(audio_thread_proxy),
+ video_thread_proxy_(video_thread_proxy),
+ clock_(clock.Pass()),
+ logging_(new LoggingImpl) {}
-CastEnvironment::~CastEnvironment() {}
+CastEnvironment::~CastEnvironment() {
+ // Logging must be deleted on the main thread.
+ if (main_thread_proxy_ && !main_thread_proxy_->RunsTasksOnCurrentThread()) {
+ main_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&DeleteLoggingOnMainThread, base::Passed(&logging_)));
+ }
+}
bool CastEnvironment::PostTask(ThreadId identifier,
- const tracked_objects::Location& from_here,
- const base::Closure& task) {
- scoped_refptr<TaskRunner> task_runner =
- GetMessageTaskRunnerForThread(identifier);
-
- return task_runner->PostTask(from_here, task);
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return GetTaskRunner(identifier)->PostTask(from_here, task);
}
-bool CastEnvironment::PostDelayedTask(ThreadId identifier,
- const tracked_objects::Location& from_here,
- const base::Closure& task,
- base::TimeDelta delay) {
- scoped_refptr<TaskRunner> task_runner =
- GetMessageTaskRunnerForThread(identifier);
-
- return task_runner->PostDelayedTask(from_here, task, delay);
+bool CastEnvironment::PostDelayedTask(
+ ThreadId identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return GetTaskRunner(identifier)->PostDelayedTask(from_here, task, delay);
}
-scoped_refptr<TaskRunner> CastEnvironment::GetMessageTaskRunnerForThread(
- ThreadId identifier) {
+scoped_refptr<SingleThreadTaskRunner> CastEnvironment::GetTaskRunner(
+ ThreadId identifier) const {
switch (identifier) {
case CastEnvironment::MAIN:
return main_thread_proxy_;
- case CastEnvironment::AUDIO_ENCODER:
- return audio_encode_thread_proxy_;
- case CastEnvironment::AUDIO_DECODER:
- return audio_decode_thread_proxy_;
- case CastEnvironment::VIDEO_ENCODER:
- return video_encode_thread_proxy_;
- case CastEnvironment::VIDEO_DECODER:
- return video_decode_thread_proxy_;
+ case CastEnvironment::AUDIO:
+ return audio_thread_proxy_;
+ case CastEnvironment::VIDEO:
+ return video_thread_proxy_;
default:
NOTREACHED() << "Invalid Thread identifier";
return NULL;
@@ -72,30 +73,19 @@ scoped_refptr<TaskRunner> CastEnvironment::GetMessageTaskRunnerForThread(
bool CastEnvironment::CurrentlyOn(ThreadId identifier) {
switch (identifier) {
case CastEnvironment::MAIN:
- return main_thread_proxy_->RunsTasksOnCurrentThread();
- case CastEnvironment::AUDIO_ENCODER:
- return audio_encode_thread_proxy_->RunsTasksOnCurrentThread();
- case CastEnvironment::AUDIO_DECODER:
- return audio_decode_thread_proxy_->RunsTasksOnCurrentThread();
- case CastEnvironment::VIDEO_ENCODER:
- return video_encode_thread_proxy_->RunsTasksOnCurrentThread();
- case CastEnvironment::VIDEO_DECODER:
- return video_decode_thread_proxy_->RunsTasksOnCurrentThread();
+ return main_thread_proxy_ &&
+ main_thread_proxy_->RunsTasksOnCurrentThread();
+ case CastEnvironment::AUDIO:
+ return audio_thread_proxy_ &&
+ audio_thread_proxy_->RunsTasksOnCurrentThread();
+ case CastEnvironment::VIDEO:
+ return video_thread_proxy_ &&
+ video_thread_proxy_->RunsTasksOnCurrentThread();
default:
NOTREACHED() << "Invalid thread identifier";
return false;
}
}
-base::TickClock* CastEnvironment::Clock() const {
- return clock_;
-}
-
-LoggingImpl* CastEnvironment::Logging() {
- DCHECK(CurrentlyOn(CastEnvironment::MAIN)) <<
- "Must be called from main thread";
- return logging_.get();
-}
-
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/cast_environment.h b/chromium/media/cast/cast_environment.h
index 8a135733c04..1549747ee22 100644
--- a/chromium/media/cast/cast_environment.h
+++ b/chromium/media/cast/cast_environment.h
@@ -8,7 +8,7 @@
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/task_runner.h"
+#include "base/single_thread_task_runner.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "media/cast/logging/logging_defines.h"
@@ -24,25 +24,18 @@ class CastEnvironment : public base::RefCountedThreadSafe<CastEnvironment> {
// The main thread is where the cast system is configured and where timers
// and network IO is performed.
MAIN,
- // The audio encoder thread is where all send side audio processing is done,
- // primarily encoding but also re-sampling.
- AUDIO_ENCODER,
- // The audio decoder thread is where all receive side audio processing is
- // done, primarily decoding but also error concealment and re-sampling.
- AUDIO_DECODER,
- // The video encoder thread is where the video encode processing is done.
- VIDEO_ENCODER,
- // The video decoder thread is where the video decode processing is done.
- VIDEO_DECODER,
+ // The audio thread is where all send side audio processing is done,
+ // primarily encoding / decoding but also re-sampling.
+ AUDIO,
+ // The video encoder thread is where the video processing is done.
+ VIDEO,
};
- CastEnvironment(base::TickClock* clock,
- scoped_refptr<base::TaskRunner> main_thread_proxy,
- scoped_refptr<base::TaskRunner> audio_encode_thread_proxy,
- scoped_refptr<base::TaskRunner> audio_decode_thread_proxy,
- scoped_refptr<base::TaskRunner> video_encode_thread_proxy,
- scoped_refptr<base::TaskRunner> video_decode_thread_proxy,
- const CastLoggingConfig& config);
+ CastEnvironment(
+ scoped_ptr<base::TickClock> clock,
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_proxy,
+ scoped_refptr<base::SingleThreadTaskRunner> audio_thread_proxy,
+ scoped_refptr<base::SingleThreadTaskRunner> video_thread_proxy);
// These are the same methods in message_loop.h, but are guaranteed to either
// get posted to the MessageLoop if it's still alive, or be deleted otherwise.
@@ -60,29 +53,38 @@ class CastEnvironment : public base::RefCountedThreadSafe<CastEnvironment> {
bool CurrentlyOn(ThreadId identifier);
- base::TickClock* Clock() const;
+ // All of the media::cast implementation must use this TickClock.
+ base::TickClock* Clock() const { return clock_.get(); }
- // Logging is not thread safe. Should always be called from the main thread.
- LoggingImpl* Logging();
+ // Logging is not thread safe. Its methods should always be called from the
+ // main thread.
+ // TODO(hubbe): Logging should be a thread-safe interface.
+ LoggingImpl* Logging() const { return logging_.get(); }
- protected:
- virtual ~CastEnvironment();
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+ ThreadId identifier) const;
- private:
- friend class base::RefCountedThreadSafe<CastEnvironment>;
+ bool HasAudioThread() {
+ return audio_thread_proxy_ ? true : false;
+ }
- scoped_refptr<base::TaskRunner> GetMessageTaskRunnerForThread(
- ThreadId identifier);
+ bool HasVideoThread() {
+ return video_thread_proxy_ ? true : false;
+ }
- base::TickClock* const clock_; // Not owned by this class.
- scoped_refptr<base::TaskRunner> main_thread_proxy_;
- scoped_refptr<base::TaskRunner> audio_encode_thread_proxy_;
- scoped_refptr<base::TaskRunner> audio_decode_thread_proxy_;
- scoped_refptr<base::TaskRunner> video_encode_thread_proxy_;
- scoped_refptr<base::TaskRunner> video_decode_thread_proxy_;
+ protected:
+ virtual ~CastEnvironment();
+ // Subclasses may override these.
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_proxy_;
+ scoped_refptr<base::SingleThreadTaskRunner> audio_thread_proxy_;
+ scoped_refptr<base::SingleThreadTaskRunner> video_thread_proxy_;
+ scoped_ptr<base::TickClock> clock_;
scoped_ptr<LoggingImpl> logging_;
+ private:
+ friend class base::RefCountedThreadSafe<CastEnvironment>;
+
DISALLOW_COPY_AND_ASSIGN(CastEnvironment);
};
diff --git a/chromium/media/cast/cast_receiver.gyp b/chromium/media/cast/cast_receiver.gyp
deleted file mode 100644
index 031aec7e16a..00000000000
--- a/chromium/media/cast/cast_receiver.gyp
+++ /dev/null
@@ -1,33 +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.
-
-{
- 'includes': [
- 'audio_receiver/audio_receiver.gypi',
- 'video_receiver/video_receiver.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'cast_receiver',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc/',
- ],
- 'sources': [
- 'cast_receiver.h',
- 'cast_receiver_impl.cc',
- 'cast_receiver_impl.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- 'cast_audio_receiver',
- 'cast_video_receiver',
- 'net/pacing/paced_sender.gyp:cast_paced_sender',
- 'rtp_receiver/rtp_receiver.gyp:cast_rtp_receiver',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/cast_receiver.h b/chromium/media/cast/cast_receiver.h
index 75e6f68d3bb..a9d3edeb78e 100644
--- a/chromium/media/cast/cast_receiver.h
+++ b/chromium/media/cast/cast_receiver.h
@@ -13,71 +13,71 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
+#include "media/base/audio_bus.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
namespace media {
class VideoFrame;
-}
-namespace media {
namespace cast {
-// Callback in which the raw audio frame and play-out time will be returned
-// once decoding is complete.
-typedef base::Callback<void(scoped_ptr<PcmAudioFrame>, const base::TimeTicks&)>
- AudioFrameDecodedCallback;
-// Callback in which the encoded audio frame and play-out time will be returned.
-typedef base::Callback<void(scoped_ptr<EncodedAudioFrame>,
- const base::TimeTicks&)> AudioFrameEncodedCallback;
+namespace transport {
+class PacketSender;
+}
-// Callback in which the raw frame and render time will be returned once
-// decoding is complete.
+// The following callbacks are used to deliver decoded audio/video frame data,
+// the frame's corresponding play-out time, and a continuity flag.
+// |is_continuous| will be false to indicate the loss of data due to a loss of
+// frames (or decoding errors). This allows the client to take steps to smooth
+// discontinuities for playback. Note: A NULL pointer can be returned when data
+// is not available (e.g., bad/missing packet).
+typedef base::Callback<void(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& playout_time,
+ bool is_continuous)> AudioFrameDecodedCallback;
+// TODO(miu): |video_frame| includes a timestamp, so use that instead.
typedef base::Callback<void(const scoped_refptr<media::VideoFrame>& video_frame,
- const base::TimeTicks&)>
- VideoFrameDecodedCallback;
-
-// Callback in which the encoded video frame and render time will be returned.
-typedef base::Callback<void(scoped_ptr<EncodedVideoFrame>,
- const base::TimeTicks&)> VideoFrameEncodedCallback;
-
-// This Class is thread safe.
-class FrameReceiver : public base::RefCountedThreadSafe<FrameReceiver> {
- public:
- virtual void GetRawAudioFrame(int number_of_10ms_blocks,
- int desired_frequency,
- const AudioFrameDecodedCallback& callback) = 0;
-
- virtual void GetCodedAudioFrame(
- const AudioFrameEncodedCallback& callback) = 0;
+ const base::TimeTicks& playout_time,
+ bool is_continuous)> VideoFrameDecodedCallback;
- virtual void GetRawVideoFrame(const VideoFrameDecodedCallback& callback) = 0;
+// The following callback delivers encoded frame data and metadata. The client
+// should examine the |frame_id| field to determine whether any frames have been
+// 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>)>
+ ReceiveEncodedFrameCallback;
- virtual void GetEncodedVideoFrame(
- const VideoFrameEncodedCallback& callback) = 0;
-
- protected:
- virtual ~FrameReceiver() {}
-
- private:
- friend class base::RefCountedThreadSafe<FrameReceiver>;
-};
-
-// This Class is thread safe.
class CastReceiver {
public:
- static CastReceiver* CreateCastReceiver(
+ static scoped_ptr<CastReceiver> Create(
scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- const VideoReceiverConfig& video_config,
- PacketSender* const packet_sender);
-
- // All received RTP and RTCP packets for the call should be inserted to this
- // PacketReceiver.
- virtual scoped_refptr<PacketReceiver> packet_receiver() = 0;
-
- // Polling interface to get audio and video frames from the CastReceiver.
- virtual scoped_refptr<FrameReceiver> frame_receiver() = 0;
+ const FrameReceiverConfig& audio_config,
+ const FrameReceiverConfig& video_config,
+ 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 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. 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/chromium/media/cast/cast_receiver_impl.cc b/chromium/media/cast/cast_receiver_impl.cc
deleted file mode 100644
index e2c004fe963..00000000000
--- a/chromium/media/cast/cast_receiver_impl.cc
+++ /dev/null
@@ -1,175 +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 VideoFrameEncodedCallback& callback) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoReceiver::GetEncodedVideoFrame,
- video_receiver_->AsWeakPtr(), callback));
- }
-
- virtual void GetRawAudioFrame(
- int number_of_10ms_blocks,
- int desired_frequency,
- const AudioFrameDecodedCallback& callback) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, base::Bind(
- &AudioReceiver::GetRawAudioFrame, audio_receiver_->AsWeakPtr(),
- number_of_10ms_blocks, desired_frequency, callback));
- }
-
- virtual void GetCodedAudioFrame(
- const AudioFrameEncodedCallback& callback) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioReceiver::GetEncodedAudioFrame,
- audio_receiver_->AsWeakPtr(), callback));
- }
-
- protected:
- virtual ~LocalFrameReceiver() {}
-
- private:
- friend class base::RefCountedThreadSafe<LocalFrameReceiver>;
-
- scoped_refptr<CastEnvironment> cast_environment_;
- AudioReceiver* audio_receiver_;
- VideoReceiver* video_receiver_;
-};
-
-// The video and audio receivers should only be called from the main thread.
-class LocalPacketReceiver : public PacketReceiver {
- public:
- LocalPacketReceiver(scoped_refptr<CastEnvironment> cast_environment,
- AudioReceiver* audio_receiver,
- VideoReceiver* video_receiver,
- uint32 ssrc_of_audio_sender,
- uint32 ssrc_of_video_sender)
- : cast_environment_(cast_environment),
- audio_receiver_(audio_receiver),
- video_receiver_(video_receiver),
- ssrc_of_audio_sender_(ssrc_of_audio_sender),
- ssrc_of_video_sender_(ssrc_of_video_sender) {}
-
- virtual void ReceivedPacket(const uint8* packet,
- size_t length,
- const base::Closure callback) OVERRIDE {
- if (length < kMinLengthOfRtcp) {
- // No action; just log and call the callback informing that we are done
- // with the packet.
- VLOG(1) << "Received a packet which is too short " << length;
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
- return;
- }
- uint32 ssrc_of_sender;
- if (!Rtcp::IsRtcpPacket(packet, length)) {
- if (length < kMinLengthOfRtp) {
- // No action; just log and call the callback informing that we are done
- // with the packet.
- VLOG(1) << "Received a RTP packet which is too short " << length;
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
- return;
- }
- ssrc_of_sender = RtpReceiver::GetSsrcOfSender(packet, length);
- } else {
- ssrc_of_sender = Rtcp::GetSsrcOfSender(packet, length);
- }
- if (ssrc_of_sender == ssrc_of_audio_sender_) {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioReceiver::IncomingPacket,
- audio_receiver_->AsWeakPtr(), packet, length, callback));
- } else if (ssrc_of_sender == ssrc_of_video_sender_) {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoReceiver::IncomingPacket,
- video_receiver_->AsWeakPtr(), packet, length, callback));
- } else {
- // No action; just log and call the callback informing that we are done
- // with the packet.
- VLOG(1) << "Received a packet with a non matching sender SSRC "
- << ssrc_of_sender;
-
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
- }
- }
-
- protected:
- virtual ~LocalPacketReceiver() {}
-
- private:
- friend class base::RefCountedThreadSafe<LocalPacketReceiver>;
-
- scoped_refptr<CastEnvironment> cast_environment_;
- AudioReceiver* audio_receiver_;
- VideoReceiver* video_receiver_;
- const uint32 ssrc_of_audio_sender_;
- const uint32 ssrc_of_video_sender_;
-};
-
-CastReceiver* CastReceiver::CreateCastReceiver(
- scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- const VideoReceiverConfig& video_config,
- PacketSender* const packet_sender) {
- return new CastReceiverImpl(cast_environment,
- audio_config,
- video_config,
- packet_sender);
-}
-
-CastReceiverImpl::CastReceiverImpl(
- scoped_refptr<CastEnvironment> cast_environment,
- const AudioReceiverConfig& audio_config,
- const VideoReceiverConfig& video_config,
- PacketSender* const packet_sender)
- : pacer_(cast_environment, packet_sender),
- audio_receiver_(cast_environment, audio_config, &pacer_),
- video_receiver_(cast_environment, video_config, &pacer_),
- frame_receiver_(new LocalFrameReceiver(cast_environment,
- &audio_receiver_,
- &video_receiver_)),
- packet_receiver_(new LocalPacketReceiver(cast_environment,
- &audio_receiver_,
- &video_receiver_,
- audio_config.incoming_ssrc,
- video_config.incoming_ssrc)) {}
-
-CastReceiverImpl::~CastReceiverImpl() {}
-
-scoped_refptr<PacketReceiver> CastReceiverImpl::packet_receiver() {
- return packet_receiver_;
-}
-
-scoped_refptr<FrameReceiver> CastReceiverImpl::frame_receiver() {
- return frame_receiver_;
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/cast_receiver_impl.h b/chromium/media/cast/cast_receiver_impl.h
deleted file mode 100644
index d34a3de6514..00000000000
--- a/chromium/media/cast/cast_receiver_impl.h
+++ /dev/null
@@ -1,50 +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/net/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 AudioReceiverConfig& audio_config,
- const VideoReceiverConfig& video_config,
- PacketSender* const packet_sender);
-
- virtual ~CastReceiverImpl();
-
- // All received RTP and RTCP packets for the call should be inserted to this
- // PacketReceiver.
- virtual scoped_refptr<PacketReceiver> packet_receiver() OVERRIDE;
-
- // Interface to get audio and video frames from the CastReceiver.
- virtual scoped_refptr<FrameReceiver> frame_receiver() OVERRIDE;
-
- private:
- PacedSender pacer_;
- AudioReceiver audio_receiver_;
- VideoReceiver video_receiver_;
- scoped_refptr<FrameReceiver> frame_receiver_;
- scoped_refptr<PacketReceiver> packet_receiver_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_CAST_RECEIVER_IMPL_
diff --git a/chromium/media/cast/cast_sender.gyp b/chromium/media/cast/cast_sender.gyp
deleted file mode 100644
index 1f9b07e4a42..00000000000
--- a/chromium/media/cast/cast_sender.gyp
+++ /dev/null
@@ -1,36 +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.
-
-{
- 'includes': [
- 'audio_sender/audio_sender.gypi',
- 'congestion_control/congestion_control.gypi',
- 'video_sender/video_sender.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'cast_sender',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc/',
- ],
- 'sources': [
- 'cast_sender.h',
- 'cast_sender_impl.cc',
- 'cast_sender_impl.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- 'audio_sender',
- 'congestion_control',
- 'net/pacing/paced_sender.gyp:cast_paced_sender',
- 'net/rtp_sender/rtp_sender.gyp:cast_rtp_sender',
- 'rtcp/rtcp.gyp:cast_rtcp',
- 'video_sender',
- ], # dependencies
- },
- ],
-}
diff --git a/chromium/media/cast/cast_sender.h b/chromium/media/cast/cast_sender.h
index abe22f56345..eb3327ff3df 100644
--- a/chromium/media/cast/cast_sender.h
+++ b/chromium/media/cast/cast_sender.h
@@ -2,11 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// This is the main interface for the cast sender. All configuration are done
-// at creation.
+// This is the main interface for the cast sender.
//
-// The FrameInput and PacketReciever interfaces should normally be accessed from
-// the IO thread. However they are allowed to be called from any thread.
+// The AudioFrameInput, VideoFrameInput and PacketReciever interfaces should
+// be accessed from the main thread.
#ifndef MEDIA_CAST_CAST_SENDER_H_
#define MEDIA_CAST_CAST_SENDER_H_
@@ -14,83 +13,83 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
+#include "media/base/audio_bus.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
+#include "media/cast/transport/cast_transport_sender.h"
namespace media {
-class AudioBus;
class VideoFrame;
-}
-namespace media {
namespace cast {
+class AudioSender;
+class VideoSender;
-// This Class is thread safe.
-class FrameInput : public base::RefCountedThreadSafe<FrameInput> {
+class VideoFrameInput : public base::RefCountedThreadSafe<VideoFrameInput> {
public:
- // The video_frame must be valid until the callback is called.
- // The callback is called from the main cast thread as soon as
- // the encoder is done with the frame; it does not mean that the encoded frame
- // has been sent out.
+ // Insert video frames into Cast sender. Frames will be encoded, packetized
+ // and sent to the network.
virtual void InsertRawVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time) = 0;
- // The video_frame must be valid until the callback is called.
- // The callback is called from the main cast thread as soon as
- // the cast sender is done with the frame; it does not mean that the encoded
- // frame has been sent out.
- virtual void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time,
- const base::Closure callback) = 0;
-
- // The |audio_bus| must be valid until the |done_callback| is called.
- // The callback is called from the main cast thread as soon as the encoder is
- // done with |audio_bus|; it does not mean that the encoded data has been
- // sent out.
- virtual void InsertAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback) = 0;
-
- // The audio_frame must be valid until the callback is called.
- // The callback is called from the main cast thread as soon as
- // the cast sender is done with the frame; it does not mean that the encoded
- // frame has been sent out.
- virtual void InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time,
- const base::Closure callback) = 0;
+ protected:
+ virtual ~VideoFrameInput() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<VideoFrameInput>;
+};
+
+class AudioFrameInput : public base::RefCountedThreadSafe<AudioFrameInput> {
+ public:
+ // Insert audio frames into Cast sender. Frames will be encoded, packetized
+ // and sent to the network.
+ virtual void InsertAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time) = 0;
protected:
- virtual ~FrameInput() {}
+ virtual ~AudioFrameInput() {}
private:
- friend class base::RefCountedThreadSafe<FrameInput>;
+ friend class base::RefCountedThreadSafe<AudioFrameInput>;
};
-// This Class is thread safe.
-// The provided PacketSender object will always be called form the main cast
-// thread.
+// All methods of CastSender must be called on the main thread.
+// Provided CastTransportSender will also be called on the main thread.
class CastSender {
public:
- static CastSender* CreateCastSender(
+ static scoped_ptr<CastSender> Create(
scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig& audio_config,
- const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacketSender* const packet_sender);
+ transport::CastTransportSender* const transport_sender);
virtual ~CastSender() {}
- // All audio and video frames for the session should be inserted to this
- // object.
- // Can be called from any thread.
- virtual scoped_refptr<FrameInput> frame_input() = 0;
+ // All video frames for the session should be inserted to this object.
+ virtual scoped_refptr<VideoFrameInput> video_frame_input() = 0;
+
+ // All audio frames for the session should be inserted to this object.
+ virtual scoped_refptr<AudioFrameInput> audio_frame_input() = 0;
// All RTCP packets for the session should be inserted to this object.
- // Can be called from any thread.
- virtual scoped_refptr<PacketReceiver> packet_receiver() = 0;
+ // This function and the callback must be called on the main thread.
+ virtual transport::PacketReceiverCallback packet_receiver() = 0;
+
+ // Initialize the audio stack. Must be called in order to send audio frames.
+ // Status of the initialization will be returned on cast_initialization_cb.
+ virtual void InitializeAudio(
+ const AudioSenderConfig& audio_config,
+ const CastInitializationCallback& cast_initialization_cb) = 0;
+
+ // Initialize the video stack. Must be called in order to send video frames.
+ // Status of the initialization will be returned on cast_initialization_cb.
+ virtual void InitializeVideo(
+ const VideoSenderConfig& video_config,
+ const CastInitializationCallback& cast_initialization_cb,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb) = 0;
};
} // namespace cast
diff --git a/chromium/media/cast/cast_sender_impl.cc b/chromium/media/cast/cast_sender_impl.cc
index 69ebd53c6bd..361e4d8dc12 100644
--- a/chromium/media/cast/cast_sender_impl.cc
+++ b/chromium/media/cast/cast_sender_impl.cc
@@ -12,62 +12,136 @@
namespace media {
namespace cast {
-// The LocalFrameInput class posts all incoming frames; audio and video to the
-// main cast thread for processing.
-// This make the cast sender interface thread safe.
-class LocalFrameInput : public FrameInput {
+// The LocalVideoFrameInput class posts all incoming video frames to the main
+// cast thread for processing.
+class LocalVideoFrameInput : public VideoFrameInput {
public:
- LocalFrameInput(scoped_refptr<CastEnvironment> cast_environment,
- base::WeakPtr<AudioSender> audio_sender,
- base::WeakPtr<VideoSender> video_sender)
- : cast_environment_(cast_environment),
- audio_sender_(audio_sender),
- video_sender_(video_sender) {}
+ LocalVideoFrameInput(scoped_refptr<CastEnvironment> cast_environment,
+ base::WeakPtr<VideoSender> video_sender)
+ : cast_environment_(cast_environment), video_sender_(video_sender) {}
virtual void InsertRawVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoSender::InsertRawVideoFrame, video_sender_,
- video_frame, capture_time));
+ cast_environment_->PostTask(CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&VideoSender::InsertRawVideoFrame,
+ video_sender_,
+ video_frame,
+ capture_time));
}
- virtual void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time,
- const base::Closure callback) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoSender::InsertCodedVideoFrame, video_sender_,
- video_frame, capture_time, callback));
- }
+ protected:
+ virtual ~LocalVideoFrameInput() {}
- virtual void InsertAudio(const AudioBus* audio_bus,
- const base::TimeTicks& recorded_time,
- const base::Closure& done_callback) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioSender::InsertAudio, audio_sender_,
- audio_bus, recorded_time, done_callback));
- }
+ private:
+ friend class base::RefCountedThreadSafe<LocalVideoFrameInput>;
+
+ scoped_refptr<CastEnvironment> cast_environment_;
+ base::WeakPtr<VideoSender> video_sender_;
- virtual void InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time,
- const base::Closure callback) OVERRIDE {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioSender::InsertCodedAudioFrame, audio_sender_,
- audio_frame, recorded_time, callback));
+ DISALLOW_COPY_AND_ASSIGN(LocalVideoFrameInput);
+};
+
+// The LocalAudioFrameInput class posts all incoming audio frames to the main
+// cast thread for processing. Therefore frames can be inserted from any thread.
+class LocalAudioFrameInput : public AudioFrameInput {
+ public:
+ LocalAudioFrameInput(scoped_refptr<CastEnvironment> cast_environment,
+ base::WeakPtr<AudioSender> audio_sender)
+ : cast_environment_(cast_environment), audio_sender_(audio_sender) {}
+
+ virtual void InsertAudio(scoped_ptr<AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time) OVERRIDE {
+ cast_environment_->PostTask(CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&AudioSender::InsertAudio,
+ audio_sender_,
+ base::Passed(&audio_bus),
+ recorded_time));
}
protected:
- virtual ~LocalFrameInput() {}
+ virtual ~LocalAudioFrameInput() {}
private:
- friend class base::RefCountedThreadSafe<LocalFrameInput>;
+ friend class base::RefCountedThreadSafe<LocalAudioFrameInput>;
scoped_refptr<CastEnvironment> cast_environment_;
base::WeakPtr<AudioSender> audio_sender_;
- base::WeakPtr<VideoSender> video_sender_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalAudioFrameInput);
};
-// LocalCastSenderPacketReceiver handle the incoming packets to the cast sender
+scoped_ptr<CastSender> CastSender::Create(
+ scoped_refptr<CastEnvironment> cast_environment,
+ transport::CastTransportSender* const transport_sender) {
+ CHECK(cast_environment);
+ return scoped_ptr<CastSender>(
+ new CastSenderImpl(cast_environment, transport_sender));
+}
+
+CastSenderImpl::CastSenderImpl(
+ scoped_refptr<CastEnvironment> cast_environment,
+ transport::CastTransportSender* const transport_sender)
+ : cast_environment_(cast_environment),
+ transport_sender_(transport_sender),
+ weak_factory_(this) {
+ CHECK(cast_environment);
+}
+
+void CastSenderImpl::InitializeAudio(
+ const AudioSenderConfig& audio_config,
+ const CastInitializationCallback& cast_initialization_cb) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ CHECK(audio_config.use_external_encoder ||
+ cast_environment_->HasAudioThread());
+
+ VLOG(1) << "CastSenderImpl@" << this << "::InitializeAudio()";
+
+ audio_sender_.reset(
+ new AudioSender(cast_environment_, audio_config, transport_sender_));
+
+ const CastInitializationStatus status = audio_sender_->InitializationResult();
+ if (status == STATUS_AUDIO_INITIALIZED) {
+ ssrc_of_audio_sender_ = audio_config.incoming_feedback_ssrc;
+ audio_frame_input_ =
+ new LocalAudioFrameInput(cast_environment_, audio_sender_->AsWeakPtr());
+ }
+ cast_initialization_cb.Run(status);
+}
+
+void CastSenderImpl::InitializeVideo(
+ const VideoSenderConfig& video_config,
+ const CastInitializationCallback& cast_initialization_cb,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ CHECK(video_config.use_external_encoder ||
+ cast_environment_->HasVideoThread());
+
+ VLOG(1) << "CastSenderImpl@" << this << "::InitializeVideo()";
+
+ video_sender_.reset(new VideoSender(cast_environment_,
+ video_config,
+ create_vea_cb,
+ create_video_encode_mem_cb,
+ transport_sender_));
+
+ const CastInitializationStatus status = video_sender_->InitializationResult();
+ if (status == STATUS_VIDEO_INITIALIZED) {
+ ssrc_of_video_sender_ = video_config.incoming_feedback_ssrc;
+ video_frame_input_ =
+ new LocalVideoFrameInput(cast_environment_, video_sender_->AsWeakPtr());
+ }
+ cast_initialization_cb.Run(status);
+}
+
+CastSenderImpl::~CastSenderImpl() {
+ VLOG(1) << "CastSenderImpl@" << this << "::~CastSenderImpl()";
+}
+
+// ReceivedPacket handle the incoming packets to the cast sender
// it's only expected to receive RTCP feedback packets from the remote cast
// receiver. The class verifies that that it is a RTCP packet and based on the
// SSRC of the incoming packet route the packet to the correct sender; audio or
@@ -92,102 +166,54 @@ class LocalFrameInput : public FrameInput {
// generates multiple streams in one RTP session, for example from
// separate video cameras, each MUST be identified as a different
// SSRC.
-
-class LocalCastSenderPacketReceiver : public PacketReceiver {
- public:
- LocalCastSenderPacketReceiver(scoped_refptr<CastEnvironment> cast_environment,
- base::WeakPtr<AudioSender> audio_sender,
- base::WeakPtr<VideoSender> video_sender,
- uint32 ssrc_of_audio_sender,
- uint32 ssrc_of_video_sender)
- : cast_environment_(cast_environment),
- audio_sender_(audio_sender),
- video_sender_(video_sender),
- ssrc_of_audio_sender_(ssrc_of_audio_sender),
- ssrc_of_video_sender_(ssrc_of_video_sender) {}
-
- virtual void ReceivedPacket(const uint8* packet,
- size_t length,
- const base::Closure callback) OVERRIDE {
- if (!Rtcp::IsRtcpPacket(packet, length)) {
- // We should have no incoming RTP packets.
- // No action; just log and call the callback informing that we are done
- // with the packet.
- VLOG(1) << "Unexpectedly received a RTP packet in the cast sender";
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
+void CastSenderImpl::ReceivedPacket(scoped_ptr<Packet> packet) {
+ DCHECK(cast_environment_);
+ size_t length = packet->size();
+ const uint8_t* data = &packet->front();
+ if (!Rtcp::IsRtcpPacket(data, length)) {
+ VLOG(1) << "CastSenderImpl@" << this << "::ReceivedPacket() -- "
+ << "Received an invalid (non-RTCP?) packet in the cast sender.";
+ return;
+ }
+ uint32 ssrc_of_sender = Rtcp::GetSsrcOfSender(data, length);
+ if (ssrc_of_sender == ssrc_of_audio_sender_) {
+ if (!audio_sender_) {
+ NOTREACHED();
return;
}
- uint32 ssrc_of_sender = Rtcp::GetSsrcOfSender(packet, length);
- if (ssrc_of_sender == ssrc_of_audio_sender_) {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&AudioSender::IncomingRtcpPacket, audio_sender_,
- packet, length, callback));
- } else if (ssrc_of_sender == ssrc_of_video_sender_) {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoSender::IncomingRtcpPacket, video_sender_,
- packet, length, callback));
- } else {
- // No action; just log and call the callback informing that we are done
- // with the packet.
- VLOG(1) << "Received a RTCP packet with a non matching sender SSRC "
- << ssrc_of_sender;
-
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
+ cast_environment_->PostTask(CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&AudioSender::IncomingRtcpPacket,
+ audio_sender_->AsWeakPtr(),
+ base::Passed(&packet)));
+ } else if (ssrc_of_sender == ssrc_of_video_sender_) {
+ if (!video_sender_) {
+ NOTREACHED();
+ return;
}
+ cast_environment_->PostTask(CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&VideoSender::IncomingRtcpPacket,
+ video_sender_->AsWeakPtr(),
+ base::Passed(&packet)));
+ } else {
+ VLOG(1) << "CastSenderImpl@" << this << "::ReceivedPacket() -- "
+ << "Received a RTCP packet with a non matching sender SSRC "
+ << ssrc_of_sender;
}
+}
- protected:
- virtual ~LocalCastSenderPacketReceiver() {}
-
- private:
- friend class base::RefCountedThreadSafe<LocalCastSenderPacketReceiver>;
-
- scoped_refptr<CastEnvironment> cast_environment_;
- base::WeakPtr<AudioSender> audio_sender_;
- base::WeakPtr<VideoSender> video_sender_;
- const uint32 ssrc_of_audio_sender_;
- const uint32 ssrc_of_video_sender_;
-};
-
-CastSender* CastSender::CreateCastSender(
- scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig& audio_config,
- const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacketSender* const packet_sender) {
- return new CastSenderImpl(cast_environment,
- audio_config,
- video_config,
- video_encoder_controller,
- packet_sender);
+scoped_refptr<AudioFrameInput> CastSenderImpl::audio_frame_input() {
+ return audio_frame_input_;
}
-CastSenderImpl::CastSenderImpl(
- scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig& audio_config,
- const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacketSender* const packet_sender)
- : pacer_(cast_environment, packet_sender),
- audio_sender_(cast_environment, audio_config, &pacer_),
- video_sender_(cast_environment, video_config, video_encoder_controller,
- &pacer_),
- frame_input_(new LocalFrameInput(cast_environment,
- audio_sender_.AsWeakPtr(),
- video_sender_.AsWeakPtr())),
- packet_receiver_(new LocalCastSenderPacketReceiver(cast_environment,
- audio_sender_.AsWeakPtr(), video_sender_.AsWeakPtr(),
- audio_config.incoming_feedback_ssrc,
- video_config.incoming_feedback_ssrc)) {}
-
-CastSenderImpl::~CastSenderImpl() {}
-
-scoped_refptr<FrameInput> CastSenderImpl::frame_input() {
- return frame_input_;
+scoped_refptr<VideoFrameInput> CastSenderImpl::video_frame_input() {
+ return video_frame_input_;
}
-scoped_refptr<PacketReceiver> CastSenderImpl::packet_receiver() {
- return packet_receiver_;
+transport::PacketReceiverCallback CastSenderImpl::packet_receiver() {
+ return base::Bind(&CastSenderImpl::ReceivedPacket,
+ weak_factory_.GetWeakPtr());
}
} // namespace cast
diff --git a/chromium/media/cast/cast_sender_impl.h b/chromium/media/cast/cast_sender_impl.h
index 2c5dd222e1a..d09a869712c 100644
--- a/chromium/media/cast/cast_sender_impl.h
+++ b/chromium/media/cast/cast_sender_impl.h
@@ -8,47 +8,64 @@
#include "base/memory/scoped_ptr.h"
#include "media/cast/audio_sender/audio_sender.h"
#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
#include "media/cast/cast_environment.h"
#include "media/cast/cast_sender.h"
-#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/video_sender/video_sender.h"
namespace media {
- class VideoFrame;
-}
+class VideoFrame;
-namespace media {
namespace cast {
-
class AudioSender;
-class PacedSender;
class VideoSender;
-// This calls is a pure owner class that group all required sending objects
-// together such as pacer, packet receiver, frame input, audio and video sender.
+// This class combines all required sending objects such as the audio and video
+// senders, pacer, packet receiver and frame input.
class CastSenderImpl : public CastSender {
public:
CastSenderImpl(scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig& audio_config,
- const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacketSender* const packet_sender);
+ transport::CastTransportSender* const transport_sender);
+
+ virtual void InitializeAudio(
+ const AudioSenderConfig& audio_config,
+ const CastInitializationCallback& cast_initialization_cb) OVERRIDE;
+ virtual void InitializeVideo(
+ const VideoSenderConfig& video_config,
+ const CastInitializationCallback& cast_initialization_cb,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb)
+ OVERRIDE;
virtual ~CastSenderImpl();
- virtual scoped_refptr<FrameInput> frame_input() OVERRIDE;
- virtual scoped_refptr<PacketReceiver> packet_receiver() OVERRIDE;
+ virtual scoped_refptr<AudioFrameInput> audio_frame_input() OVERRIDE;
+ virtual scoped_refptr<VideoFrameInput> video_frame_input() OVERRIDE;
+
+ virtual transport::PacketReceiverCallback packet_receiver() OVERRIDE;
private:
- PacedSender pacer_;
- AudioSender audio_sender_;
- VideoSender video_sender_;
- scoped_refptr<FrameInput> frame_input_;
- scoped_refptr<PacketReceiver> packet_receiver_;
+ void ReceivedPacket(scoped_ptr<Packet> packet);
+
+ CastInitializationCallback initialization_callback_;
+ scoped_ptr<AudioSender> audio_sender_;
+ scoped_ptr<VideoSender> video_sender_;
+ scoped_refptr<AudioFrameInput> audio_frame_input_;
+ scoped_refptr<VideoFrameInput> video_frame_input_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ // The transport sender is owned by the owner of the CastSender, and should be
+ // valid throughout the lifetime of the CastSender.
+ transport::CastTransportSender* const transport_sender_;
+ uint32 ssrc_of_audio_sender_;
+ uint32 ssrc_of_video_sender_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<CastSenderImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CastSenderImpl);
};
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_CAST_SENDER_IMPL_H_
-
diff --git a/chromium/media/cast/cast_testing.gypi b/chromium/media/cast/cast_testing.gypi
new file mode 100644
index 00000000000..aef0fbd8c3e
--- /dev/null
+++ b/chromium/media/cast/cast_testing.gypi
@@ -0,0 +1,276 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'cast_test_utility',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_receiver',
+ 'cast_transport',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
+ '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry',
+ ],
+ 'sources': [
+ 'test/fake_single_thread_task_runner.cc',
+ 'test/fake_single_thread_task_runner.h',
+ 'test/skewed_single_thread_task_runner.cc',
+ 'test/skewed_single_thread_task_runner.h',
+ 'test/skewed_tick_clock.cc',
+ 'test/skewed_tick_clock.h',
+ 'test/utility/audio_utility.cc',
+ 'test/utility/audio_utility.h',
+ 'test/utility/barcode.cc',
+ 'test/utility/barcode.h',
+ 'test/utility/default_config.cc',
+ 'test/utility/default_config.h',
+ 'test/utility/in_process_receiver.cc',
+ 'test/utility/in_process_receiver.h',
+ 'test/utility/input_builder.cc',
+ 'test/utility/input_builder.h',
+ 'test/utility/net_utility.cc',
+ 'test/utility/net_utility.h',
+ 'test/utility/standalone_cast_environment.cc',
+ 'test/utility/standalone_cast_environment.h',
+ 'test/utility/video_utility.cc',
+ 'test/utility/video_utility.h',
+ 'test/utility/udp_proxy.cc',
+ 'test/utility/udp_proxy.h',
+ ], # source
+ },
+ {
+ 'target_name': 'cast_unittests',
+ 'type': '<(gtest_target_type)',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_receiver',
+ 'cast_rtcp',
+ 'cast_sender',
+ 'cast_test_utility',
+ # Not a true dependency. This is here to make sure the CQ can verify
+ # the tools compile correctly.
+ 'cast_tools',
+ 'cast_transport',
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ '<(DEPTH)/net/net.gyp:net',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ '<(DEPTH)/media/base/run_all_unittests.cc',
+ 'audio_sender/audio_encoder_unittest.cc',
+ 'audio_sender/audio_sender_unittest.cc',
+ 'congestion_control/congestion_control_unittest.cc',
+ 'framer/cast_message_builder_unittest.cc',
+ 'framer/frame_buffer_unittest.cc',
+ 'framer/framer_unittest.cc',
+ 'logging/encoding_event_subscriber_unittest.cc',
+ 'logging/serialize_deserialize_test.cc',
+ 'logging/logging_impl_unittest.cc',
+ 'logging/logging_raw_unittest.cc',
+ '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',
+ 'rtcp/mock_rtcp_sender_feedback.h',
+ 'rtcp/rtcp_receiver_unittest.cc',
+ 'rtcp/rtcp_sender_unittest.cc',
+ 'rtcp/rtcp_unittest.cc',
+ 'rtcp/receiver_rtcp_event_subscriber_unittest.cc',
+# TODO(miu): The following two are test utility modules. Rename/move the files.
+ 'rtcp/test_rtcp_packet_builder.cc',
+ 'rtcp/test_rtcp_packet_builder.h',
+ 'rtp_receiver/rtp_receiver_defines.h',
+ 'rtp_receiver/mock_rtp_payload_feedback.cc',
+ 'rtp_receiver/mock_rtp_payload_feedback.h',
+ 'rtp_receiver/receiver_stats_unittest.cc',
+ 'rtp_receiver/rtp_parser/test/rtp_packet_builder.cc',
+ 'rtp_receiver/rtp_parser/rtp_parser_unittest.cc',
+ 'test/end2end_unittest.cc',
+ 'test/fake_receiver_time_offset_estimator.cc',
+ 'test/fake_receiver_time_offset_estimator.h',
+ 'test/fake_single_thread_task_runner.cc',
+ 'test/fake_single_thread_task_runner.h',
+ 'test/fake_video_encode_accelerator.cc',
+ 'test/fake_video_encode_accelerator.h',
+ 'test/utility/audio_utility_unittest.cc',
+ 'test/utility/barcode_unittest.cc',
+ 'transport/cast_transport_sender_impl_unittest.cc',
+ 'transport/pacing/mock_paced_packet_sender.cc',
+ 'transport/pacing/mock_paced_packet_sender.h',
+ 'transport/pacing/paced_sender_unittest.cc',
+ 'transport/rtp_sender/packet_storage/packet_storage_unittest.cc',
+ 'transport/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc',
+ '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_sender/external_video_encoder_unittest.cc',
+ 'video_sender/video_encoder_impl_unittest.cc',
+ 'video_sender/video_sender_unittest.cc',
+ ], # source
+ },
+ {
+ 'target_name': 'cast_benchmarks',
+ 'type': '<(gtest_target_type)',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_receiver',
+ 'cast_rtcp',
+ 'cast_sender',
+ 'cast_test_utility',
+ 'cast_transport',
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ '<(DEPTH)/net/net.gyp:net',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'test/cast_benchmarks.cc',
+ 'test/fake_single_thread_task_runner.cc',
+ 'test/fake_single_thread_task_runner.h',
+ 'test/fake_video_encode_accelerator.cc',
+ 'test/fake_video_encode_accelerator.h',
+ 'test/utility/test_util.cc',
+ 'test/utility/test_util.h',
+ ], # source
+ 'conditions': [
+ ['os_posix==1 and OS!="mac" and OS!="ios" and use_allocator!="none"',
+ {
+ 'dependencies': [
+ '<(DEPTH)/base/allocator/allocator.gyp:allocator',
+ ],
+ }
+ ],
+ ],
+ },
+ {
+ # This is a target for the collection of cast development tools.
+ # They are built on bots but not shipped.
+ 'target_name': 'cast_tools',
+ 'type': 'none',
+ 'dependencies': [
+ 'cast_receiver_app',
+ 'cast_sender_app',
+ 'udp_proxy',
+ ],
+ },
+ {
+ 'target_name': 'cast_receiver_app',
+ 'type': 'executable',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_receiver',
+ 'cast_test_utility',
+ 'cast_transport',
+ '<(DEPTH)/net/net.gyp:net_test_support',
+ '<(DEPTH)/media/media.gyp:media',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
+ ],
+ 'sources': [
+ '<(DEPTH)/media/cast/test/receiver.cc',
+ ],
+ 'conditions': [
+ ['OS == "linux" and use_x11==1', {
+ 'dependencies': [
+ '<(DEPTH)/build/linux/system.gyp:x11',
+ '<(DEPTH)/build/linux/system.gyp:xext',
+ ],
+ 'sources': [
+ '<(DEPTH)/media/cast/test/linux_output_window.cc',
+ '<(DEPTH)/media/cast/test/linux_output_window.h',
+ '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'cast_sender_app',
+ 'type': 'executable',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_sender',
+ 'cast_test_utility',
+ 'cast_transport',
+ '<(DEPTH)/net/net.gyp:net_test_support',
+ '<(DEPTH)/media/media.gyp:media',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/ffmpeg/ffmpeg.gyp:ffmpeg',
+ '<(DEPTH)/third_party/opus/opus.gyp:opus',
+ '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry',
+ ],
+ 'sources': [
+ '<(DEPTH)/media/cast/test/sender.cc',
+ ],
+ },
+ {
+ 'target_name': 'generate_barcode_video',
+ 'type': 'executable',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_test_utility',
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/media/media.gyp:media',
+ ],
+ 'sources': [
+ 'test/utility/generate_barcode_video.cc',
+ ],
+ },
+ {
+ 'target_name': 'generate_timecode_audio',
+ 'type': 'executable',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_base',
+ 'cast_test_utility',
+ 'cast_transport',
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/media/media.gyp:media',
+ ],
+ 'sources': [
+ 'test/utility/generate_timecode_audio.cc',
+ ],
+ },
+ {
+ 'target_name': 'udp_proxy',
+ 'type': 'executable',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'dependencies': [
+ 'cast_test_utility',
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/media/media.gyp:media',
+ ],
+ 'sources': [
+ 'test/utility/udp_proxy_main.cc',
+ ],
+ }
+ ], # targets
+}
diff --git a/chromium/media/cast/congestion_control/congestion_control.cc b/chromium/media/cast/congestion_control/congestion_control.cc
index 35687e7477a..d24e0ac3d0f 100644
--- a/chromium/media/cast/congestion_control/congestion_control.cc
+++ b/chromium/media/cast/congestion_control/congestion_control.cc
@@ -2,6 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// The purpose of this file is determine what bitrate to use for mirroring.
+// Ideally this should be as much as possible, without causing any frames to
+// arrive late.
+
+// The current algorithm is to measure how much bandwidth we've been using
+// recently. We also keep track of how much data has been queued up for sending
+// in a virtual "buffer" (this virtual buffer represents all the buffers between
+// the sender and the receiver, including retransmissions and so forth.)
+// If we estimate that our virtual buffer is mostly empty, we try to use
+// more bandwidth than our recent usage, otherwise we use less.
+
#include "media/cast/congestion_control/congestion_control.h"
#include "base/logging.h"
@@ -11,104 +22,176 @@
namespace media {
namespace cast {
-static const int64 kCongestionControlMinChangeIntervalMs = 10;
-static const int64 kCongestionControlMaxChangeIntervalMs = 100;
+// This means that we *try* to keep our buffer 90% empty.
+// If it is less full, we increase the bandwidth, if it is more
+// we decrease the bandwidth. Making this smaller makes the
+// congestion control more aggressive.
+static const double kTargetEmptyBufferFraction = 0.9;
-// At 10 ms RTT TCP Reno would ramp 1500 * 8 * 100 = 1200 Kbit/s.
-// NACK is sent after a maximum of 10 ms.
-static const int kCongestionControlMaxBitrateIncreasePerMillisecond = 1200;
+// This is the size of our history in frames. Larger values makes the
+// congestion control adapt slower.
+static const size_t kHistorySize = 100;
-static const int64 kMaxElapsedTimeMs = kCongestionControlMaxChangeIntervalMs;
+CongestionControl::FrameStats::FrameStats() : frame_size(0) {
+}
CongestionControl::CongestionControl(base::TickClock* clock,
- float congestion_control_back_off,
uint32 max_bitrate_configured,
uint32 min_bitrate_configured,
- uint32 start_bitrate)
+ size_t max_unacked_frames)
: clock_(clock),
- congestion_control_back_off_(congestion_control_back_off),
max_bitrate_configured_(max_bitrate_configured),
min_bitrate_configured_(min_bitrate_configured),
- bitrate_(start_bitrate) {
- DCHECK_GT(congestion_control_back_off, 0.0f) << "Invalid config";
- DCHECK_LT(congestion_control_back_off, 1.0f) << "Invalid config";
+ last_frame_stats_(static_cast<uint32>(-1)),
+ last_acked_frame_(static_cast<uint32>(-1)),
+ last_encoded_frame_(static_cast<uint32>(-1)),
+ history_size_(max_unacked_frames + kHistorySize),
+ acked_bits_in_history_(0) {
DCHECK_GE(max_bitrate_configured, min_bitrate_configured) << "Invalid config";
- DCHECK_GE(max_bitrate_configured, start_bitrate) << "Invalid config";
- DCHECK_GE(start_bitrate, min_bitrate_configured) << "Invalid config";
+ frame_stats_.resize(2);
+ base::TimeTicks now = clock->NowTicks();
+ frame_stats_[0].ack_time = now;
+ frame_stats_[0].sent_time = now;
+ frame_stats_[1].ack_time = now;
+ DCHECK(!frame_stats_[0].ack_time.is_null());
}
-CongestionControl::~CongestionControl() {
+CongestionControl::~CongestionControl() {}
+
+void CongestionControl::UpdateRtt(base::TimeDelta rtt) {
+ rtt_ = base::TimeDelta::FromSecondsD(
+ (rtt_.InSecondsF() * 7 + rtt.InSecondsF()) / 8);
}
-bool CongestionControl::OnAck(base::TimeDelta rtt, uint32* new_bitrate) {
- base::TimeTicks now = clock_->NowTicks();
+// Calculate how much "dead air" there is between two frames.
+base::TimeDelta CongestionControl::DeadTime(const FrameStats& a,
+ const FrameStats& b) {
+ if (b.sent_time > a.ack_time) {
+ return b.sent_time - a.ack_time;
+ } else {
+ return base::TimeDelta();
+ }
+}
+
+double CongestionControl::CalculateSafeBitrate() {
+ double transmit_time =
+ (GetFrameStats(last_acked_frame_)->ack_time -
+ frame_stats_.front().sent_time - dead_time_in_history_).InSecondsF();
- // First feedback?
- if (time_last_increase_.is_null()) {
- time_last_increase_ = now;
- time_last_decrease_ = now;
- return false;
+ if (acked_bits_in_history_ == 0 || transmit_time <= 0.0) {
+ return min_bitrate_configured_;
}
- // Are we at the max bitrate?
- if (max_bitrate_configured_ == bitrate_) return false;
-
- // Make sure RTT is never less than 1 ms.
- rtt = std::max(rtt, base::TimeDelta::FromMilliseconds(1));
-
- base::TimeDelta elapsed_time = std::min(now - time_last_increase_,
- base::TimeDelta::FromMilliseconds(kMaxElapsedTimeMs));
- base::TimeDelta change_interval = std::max(rtt,
- base::TimeDelta::FromMilliseconds(kCongestionControlMinChangeIntervalMs));
- change_interval = std::min(change_interval,
- base::TimeDelta::FromMilliseconds(kCongestionControlMaxChangeIntervalMs));
-
- // Have enough time have passed?
- if (elapsed_time < change_interval) return false;
-
- time_last_increase_ = now;
-
- // One packet per RTT multiplied by the elapsed time fraction.
- // 1500 * 8 * (1000 / rtt_ms) * (elapsed_time_ms / 1000) =>
- // 1500 * 8 * elapsed_time_ms / rtt_ms.
- uint32 bitrate_increase = (1500 * 8 * elapsed_time.InMilliseconds()) /
- rtt.InMilliseconds();
- uint32 max_bitrate_increase =
- kCongestionControlMaxBitrateIncreasePerMillisecond *
- elapsed_time.InMilliseconds();
- bitrate_increase = std::min(max_bitrate_increase, bitrate_increase);
- *new_bitrate = std::min(bitrate_increase + bitrate_, max_bitrate_configured_);
- bitrate_ = *new_bitrate;
- return true;
+ return acked_bits_in_history_ / std::max(transmit_time, 1E-3);
}
-bool CongestionControl::OnNack(base::TimeDelta rtt, uint32* new_bitrate) {
- base::TimeTicks now = clock_->NowTicks();
+CongestionControl::FrameStats* CongestionControl::GetFrameStats(
+ uint32 frame_id) {
+ int32 offset = static_cast<int32>(frame_id - last_frame_stats_);
+ DCHECK_LT(offset, static_cast<int32>(kHistorySize));
+ if (offset > 0) {
+ frame_stats_.resize(frame_stats_.size() + offset);
+ last_frame_stats_ += offset;
+ offset = 0;
+ }
+ while (frame_stats_.size() > history_size_) {
+ DCHECK_GT(frame_stats_.size(), 1UL);
+ DCHECK(!frame_stats_[0].ack_time.is_null());
+ acked_bits_in_history_ -= frame_stats_[0].frame_size;
+ dead_time_in_history_ -= DeadTime(frame_stats_[0], frame_stats_[1]);
+ DCHECK_GE(acked_bits_in_history_, 0UL);
+ VLOG(2) << "DT: " << dead_time_in_history_.InSecondsF();
+ DCHECK_GE(dead_time_in_history_.InSecondsF(), 0.0);
+ frame_stats_.pop_front();
+ }
+ offset += frame_stats_.size() - 1;
+ if (offset < 0 || offset >= static_cast<int32>(frame_stats_.size())) {
+ return NULL;
+ }
+ return &frame_stats_[offset];
+}
- // First feedback?
- if (time_last_decrease_.is_null()) {
- time_last_increase_ = now;
- time_last_decrease_ = now;
- return false;
+void CongestionControl::AckFrame(uint32 frame_id, base::TimeTicks when) {
+ FrameStats* frame_stats = GetFrameStats(last_acked_frame_);
+ while (IsNewerFrameId(frame_id, last_acked_frame_)) {
+ FrameStats* last_frame_stats = frame_stats;
+ last_acked_frame_++;
+ frame_stats = GetFrameStats(last_acked_frame_);
+ DCHECK(frame_stats);
+ frame_stats->ack_time = when;
+ acked_bits_in_history_ += frame_stats->frame_size;
+ dead_time_in_history_ += DeadTime(*last_frame_stats, *frame_stats);
}
- base::TimeDelta elapsed_time = std::min(now - time_last_decrease_,
- base::TimeDelta::FromMilliseconds(kMaxElapsedTimeMs));
- base::TimeDelta change_interval = std::max(rtt,
- base::TimeDelta::FromMilliseconds(kCongestionControlMinChangeIntervalMs));
- change_interval = std::min(change_interval,
- base::TimeDelta::FromMilliseconds(kCongestionControlMaxChangeIntervalMs));
+}
- // Have enough time have passed?
- if (elapsed_time < change_interval) return false;
+void CongestionControl::SendFrameToTransport(uint32 frame_id,
+ size_t frame_size,
+ base::TimeTicks when) {
+ last_encoded_frame_ = frame_id;
+ FrameStats* frame_stats = GetFrameStats(frame_id);
+ DCHECK(frame_stats);
+ frame_stats->frame_size = frame_size;
+ frame_stats->sent_time = when;
+}
- time_last_decrease_ = now;
- time_last_increase_ = now;
+base::TimeTicks CongestionControl::EstimatedAckTime(uint32 frame_id,
+ double bitrate) {
+ FrameStats* frame_stats = GetFrameStats(frame_id);
+ DCHECK(frame_stats);
+ if (frame_stats->ack_time.is_null()) {
+ DCHECK(frame_stats->frame_size) << "frame_id: " << frame_id;
+ base::TimeTicks ret = EstimatedSendingTime(frame_id, bitrate);
+ ret += base::TimeDelta::FromSecondsD(frame_stats->frame_size / bitrate);
+ ret += rtt_;
+ base::TimeTicks now = clock_->NowTicks();
+ if (ret < now) {
+ // This is a little counter-intuitive, but it seems to work.
+ // Basically, when we estimate that the ACK should have already happened,
+ // we figure out how long ago it should have happened and guess that the
+ // ACK will happen half of that time in the future. This will cause some
+ // over-estimation when acks are late, which is actually what we want.
+ return now + (now - ret) / 2;
+ } else {
+ return ret;
+ }
+ } else {
+ return frame_stats->ack_time;
+ }
+}
- *new_bitrate = std::max(
- static_cast<uint32>(bitrate_ * congestion_control_back_off_),
- min_bitrate_configured_);
+base::TimeTicks CongestionControl::EstimatedSendingTime(uint32 frame_id,
+ double bitrate) {
+ FrameStats* frame_stats = GetFrameStats(frame_id);
+ DCHECK(frame_stats);
+ base::TimeTicks ret = EstimatedAckTime(frame_id - 1, bitrate) - rtt_;
+ if (frame_stats->sent_time.is_null()) {
+ // Not sent yet, but we can't start sending it in the past.
+ return std::max(ret, clock_->NowTicks());
+ } else {
+ return std::max(ret, frame_stats->sent_time);
+ }
+}
- bitrate_ = *new_bitrate;
- return true;
+uint32 CongestionControl::GetBitrate(base::TimeTicks playout_time,
+ base::TimeDelta playout_delay) {
+ double safe_bitrate = CalculateSafeBitrate();
+ // Estimate when we might start sending the next frame.
+ base::TimeDelta time_to_catch_up =
+ playout_time -
+ EstimatedSendingTime(last_encoded_frame_ + 1, safe_bitrate);
+
+ double empty_buffer_fraction =
+ time_to_catch_up.InSecondsF() / playout_delay.InSecondsF();
+ empty_buffer_fraction = std::min(empty_buffer_fraction, 1.0);
+ empty_buffer_fraction = std::max(empty_buffer_fraction, 0.0);
+
+ uint32 bits_per_second = static_cast<uint32>(
+ safe_bitrate * empty_buffer_fraction / kTargetEmptyBufferFraction);
+ VLOG(3) << " FBR:" << (bits_per_second / 1E6)
+ << " EBF:" << empty_buffer_fraction
+ << " SBR:" << (safe_bitrate / 1E6);
+ bits_per_second = std::max(bits_per_second, min_bitrate_configured_);
+ bits_per_second = std::min(bits_per_second, max_bitrate_configured_);
+ return bits_per_second;
}
} // namespace cast
diff --git a/chromium/media/cast/congestion_control/congestion_control.gypi b/chromium/media/cast/congestion_control/congestion_control.gypi
deleted file mode 100644
index 20a57ca2a30..00000000000
--- a/chromium/media/cast/congestion_control/congestion_control.gypi
+++ /dev/null
@@ -1,23 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'congestion_control',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'sources': [
- 'congestion_control.h',
- 'congestion_control.cc',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- ],
- },
- ],
-}
-
diff --git a/chromium/media/cast/congestion_control/congestion_control.h b/chromium/media/cast/congestion_control/congestion_control.h
index df88151eb8f..54622ab114d 100644
--- a/chromium/media/cast/congestion_control/congestion_control.h
+++ b/chromium/media/cast/congestion_control/congestion_control.h
@@ -5,6 +5,8 @@
#ifndef MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_
#define MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_
+#include <deque>
+
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/tick_clock.h"
@@ -16,29 +18,65 @@ namespace cast {
class CongestionControl {
public:
CongestionControl(base::TickClock* clock,
- float congestion_control_back_off,
uint32 max_bitrate_configured,
uint32 min_bitrate_configured,
- uint32 start_bitrate);
+ size_t max_unacked_frames);
virtual ~CongestionControl();
- // Don't call OnAck if the same message contain a NACK.
- // Returns true if the bitrate have changed.
- bool OnAck(base::TimeDelta rtt_ms, uint32* new_bitrate);
+ void UpdateRtt(base::TimeDelta rtt);
+
+ // Called when an encoded frame is sent to the transport.
+ void SendFrameToTransport(uint32 frame_id,
+ size_t frame_size,
+ base::TimeTicks when);
- // Returns true if the bitrate have changed.
- bool OnNack(base::TimeDelta rtt_ms, uint32* new_bitrate);
+ // Called when we receive an ACK for a frame.
+ void AckFrame(uint32 frame_id, base::TimeTicks when);
+ // Returns the bitrate we should use for the next frame.
+ uint32 GetBitrate(base::TimeTicks playout_time,
+ base::TimeDelta playout_delay);
private:
+ struct FrameStats {
+ FrameStats();
+ // Time this frame was sent to the transport.
+ base::TimeTicks sent_time;
+ // Time this frame was acked.
+ base::TimeTicks ack_time;
+ // Size of encoded frame in bits.
+ size_t frame_size;
+ };
+
+ // Calculate how much "dead air" (idle time) there is between two frames.
+ static base::TimeDelta DeadTime(const FrameStats& a, const FrameStats& b);
+ // Get the FrameStats for a given |frame_id|.
+ // Note: Older FrameStats will be removed automatically.
+ FrameStats* GetFrameStats(uint32 frame_id);
+ // Calculata safe bitrate. This is based on how much we've been
+ // sending in the past.
+ double CalculateSafeBitrate();
+
+ // For a given frame, calculate when it might be acked.
+ // (Or return the time it was acked, if it was.)
+ base::TimeTicks EstimatedAckTime(uint32 frame_id, double bitrate);
+ // Calculate when we start sending the data for a given frame.
+ // This is done by calculating when we were done sending the previous
+ // frame, but obvoiusly can't be less than |sent_time| (if known).
+ base::TimeTicks EstimatedSendingTime(uint32 frame_id, double bitrate);
+
base::TickClock* const clock_; // Not owned by this class.
- const float congestion_control_back_off_;
const uint32 max_bitrate_configured_;
const uint32 min_bitrate_configured_;
- uint32 bitrate_;
- base::TimeTicks time_last_increase_;
- base::TimeTicks time_last_decrease_;
+ std::deque<FrameStats> frame_stats_;
+ uint32 last_frame_stats_;
+ uint32 last_acked_frame_;
+ uint32 last_encoded_frame_;
+ base::TimeDelta rtt_;
+ size_t history_size_;
+ size_t acked_bits_in_history_;
+ base::TimeDelta dead_time_in_history_;
DISALLOW_COPY_AND_ASSIGN(CongestionControl);
};
diff --git a/chromium/media/cast/congestion_control/congestion_control_unittest.cc b/chromium/media/cast/congestion_control/congestion_control_unittest.cc
index 108d2b340b7..5745eab21df 100644
--- a/chromium/media/cast/congestion_control/congestion_control_unittest.cc
+++ b/chromium/media/cast/congestion_control/congestion_control_unittest.cc
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include "base/test/simple_test_tick_clock.h"
#include "media/cast/cast_defines.h"
#include "media/cast/congestion_control/congestion_control.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
@@ -12,169 +15,106 @@ namespace cast {
static const uint32 kMaxBitrateConfigured = 5000000;
static const uint32 kMinBitrateConfigured = 500000;
-static const uint32 kStartBitrate = 2000000;
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
-static const int64 kRttMs = 20;
-static const int64 kAckRateMs = 33;
+static const int64 kStartMillisecond = INT64_C(12345678900000);
+static const double kTargetEmptyBufferFraction = 0.9;
class CongestionControlTest : public ::testing::Test {
protected:
CongestionControlTest()
- : congestion_control_(&testing_clock_,
- kDefaultCongestionControlBackOff,
- kMaxBitrateConfigured,
- kMinBitrateConfigured,
- kStartBitrate) {
+ : task_runner_(new test::FakeSingleThreadTaskRunner(&testing_clock_)) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ congestion_control_.reset(new CongestionControl(
+ &testing_clock_, kMaxBitrateConfigured, kMinBitrateConfigured, 10));
}
- // Returns the last bitrate of the run.
- uint32 RunWithOneLossEventPerSecond(int fps, int rtt_ms,
- int runtime_in_seconds) {
- const base::TimeDelta rtt = base::TimeDelta::FromMilliseconds(rtt_ms);
- const base::TimeDelta ack_rate =
- base::TimeDelta::FromMilliseconds(GG_INT64_C(1000) / fps);
- uint32 new_bitrate = 0;
- EXPECT_FALSE(congestion_control_.OnAck(rtt, &new_bitrate));
-
- for (int seconds = 0; seconds < runtime_in_seconds; ++seconds) {
- for (int i = 1; i < fps; ++i) {
- testing_clock_.Advance(ack_rate);
- congestion_control_.OnAck(rtt, &new_bitrate);
- }
- EXPECT_TRUE(congestion_control_.OnNack(rtt, &new_bitrate));
- }
- return new_bitrate;
- }
-
- base::SimpleTestTickClock testing_clock_;
- CongestionControl congestion_control_;
-};
-
-TEST_F(CongestionControlTest, Max) {
- uint32 new_bitrate = 0;
- const base::TimeDelta rtt = base::TimeDelta::FromMilliseconds(kRttMs);
- const base::TimeDelta ack_rate =
- base::TimeDelta::FromMilliseconds(kAckRateMs);
- EXPECT_FALSE(congestion_control_.OnAck(rtt, &new_bitrate));
-
- uint32 expected_increase_bitrate = 0;
-
- // Expected time is 5 seconds. 500000 - 2000000 = 5 * 1500 * 8 * (1000 / 20).
- for (int i = 0; i < 151; ++i) {
- testing_clock_.Advance(ack_rate);
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- expected_increase_bitrate += 1500 * 8 * kAckRateMs / kRttMs;
- EXPECT_EQ(kStartBitrate + expected_increase_bitrate, new_bitrate);
- }
- testing_clock_.Advance(ack_rate);
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- EXPECT_EQ(kMaxBitrateConfigured, new_bitrate);
-}
-
-TEST_F(CongestionControlTest, Min) {
- uint32 new_bitrate = 0;
- const base::TimeDelta rtt = base::TimeDelta::FromMilliseconds(kRttMs);
- const base::TimeDelta ack_rate =
- base::TimeDelta::FromMilliseconds(kAckRateMs);
- EXPECT_FALSE(congestion_control_.OnNack(rtt, &new_bitrate));
-
- uint32 expected_decrease_bitrate = kStartBitrate;
-
- // Expected number is 10. 2000 * 0.875^10 <= 500.
- for (int i = 0; i < 10; ++i) {
- testing_clock_.Advance(ack_rate);
- EXPECT_TRUE(congestion_control_.OnNack(rtt, &new_bitrate));
- expected_decrease_bitrate = static_cast<uint32>(
- expected_decrease_bitrate * kDefaultCongestionControlBackOff);
- EXPECT_EQ(expected_decrease_bitrate, new_bitrate);
+ void AckFrame(uint32 frame_id) {
+ congestion_control_->AckFrame(frame_id, testing_clock_.NowTicks());
}
- testing_clock_.Advance(ack_rate);
- EXPECT_TRUE(congestion_control_.OnNack(rtt, &new_bitrate));
- EXPECT_EQ(kMinBitrateConfigured, new_bitrate);
-}
-TEST_F(CongestionControlTest, Timing) {
- const base::TimeDelta rtt = base::TimeDelta::FromMilliseconds(kRttMs);
- const base::TimeDelta ack_rate =
- base::TimeDelta::FromMilliseconds(kAckRateMs);
- uint32 new_bitrate = 0;
- uint32 expected_bitrate = kStartBitrate;
-
- EXPECT_FALSE(congestion_control_.OnAck(rtt, &new_bitrate));
-
- testing_clock_.Advance(ack_rate);
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- expected_bitrate += 1500 * 8 * kAckRateMs / kRttMs;
- EXPECT_EQ(expected_bitrate, new_bitrate);
-
- // We should back immediately.
- EXPECT_TRUE(congestion_control_.OnNack(rtt, &new_bitrate));
- expected_bitrate = static_cast<uint32>(
- expected_bitrate * kDefaultCongestionControlBackOff);
- EXPECT_EQ(expected_bitrate, new_bitrate);
-
- // Less than one RTT have passed don't back again.
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
- EXPECT_FALSE(congestion_control_.OnNack(rtt, &new_bitrate));
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
- EXPECT_TRUE(congestion_control_.OnNack(rtt, &new_bitrate));
- expected_bitrate = static_cast<uint32>(
- expected_bitrate * kDefaultCongestionControlBackOff);
- EXPECT_EQ(expected_bitrate, new_bitrate);
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
- EXPECT_FALSE(congestion_control_.OnAck(rtt, &new_bitrate));
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- expected_bitrate += 1500 * 8 * 20 / kRttMs;
- EXPECT_EQ(expected_bitrate, new_bitrate);
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
- EXPECT_FALSE(congestion_control_.OnAck(rtt, &new_bitrate));
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- expected_bitrate += 1500 * 8 * 20 / kRttMs;
- EXPECT_EQ(expected_bitrate, new_bitrate);
-
- // Test long elapsed time (300 ms).
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(300));
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- expected_bitrate += 1500 * 8 * 100 / kRttMs;
- EXPECT_EQ(expected_bitrate, new_bitrate);
-
- // Test many short elapsed time (1 ms).
- for (int i = 0; i < 19; ++i) {
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(1));
- EXPECT_FALSE(congestion_control_.OnAck(rtt, &new_bitrate));
+ void Run(uint32 frames,
+ size_t frame_size,
+ base::TimeDelta rtt,
+ base::TimeDelta frame_delay,
+ base::TimeDelta ack_time) {
+ for (frame_id_ = 0; frame_id_ < frames; frame_id_++) {
+ congestion_control_->UpdateRtt(rtt);
+ congestion_control_->SendFrameToTransport(
+ frame_id_, frame_size, testing_clock_.NowTicks());
+ task_runner_->PostDelayedTask(FROM_HERE,
+ base::Bind(&CongestionControlTest::AckFrame,
+ base::Unretained(this),
+ frame_id_),
+ ack_time);
+ task_runner_->Sleep(frame_delay);
+ }
}
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(1));
- EXPECT_TRUE(congestion_control_.OnAck(rtt, &new_bitrate));
- expected_bitrate += 1500 * 8 * 20 / kRttMs;
- EXPECT_EQ(expected_bitrate, new_bitrate);
-}
-TEST_F(CongestionControlTest, Convergence24fps) {
- EXPECT_GE(RunWithOneLossEventPerSecond(24, kRttMs, 100),
- GG_UINT32_C(3000000));
-}
+ base::SimpleTestTickClock testing_clock_;
+ scoped_ptr<CongestionControl> congestion_control_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ uint32 frame_id_;
-TEST_F(CongestionControlTest, Convergence24fpsLongRtt) {
- EXPECT_GE(RunWithOneLossEventPerSecond(24, 100, 100),
- GG_UINT32_C(500000));
-}
+ DISALLOW_COPY_AND_ASSIGN(CongestionControlTest);
+};
-TEST_F(CongestionControlTest, Convergence60fps) {
- EXPECT_GE(RunWithOneLossEventPerSecond(60, kRttMs, 100),
- GG_UINT32_C(3500000));
+TEST_F(CongestionControlTest, SimpleRun) {
+ uint32 frame_delay = 33;
+ uint32 frame_size = 10000 * 8;
+ Run(500,
+ frame_size,
+ base::TimeDelta::FromMilliseconds(10),
+ base::TimeDelta::FromMilliseconds(frame_delay),
+ base::TimeDelta::FromMilliseconds(45));
+ // Empty the buffer.
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(100));
+
+ uint32 safe_bitrate = frame_size * 1000 / frame_delay;
+ uint32 bitrate = congestion_control_->GetBitrate(
+ testing_clock_.NowTicks() + base::TimeDelta::FromMilliseconds(300),
+ base::TimeDelta::FromMilliseconds(300));
+ EXPECT_NEAR(
+ safe_bitrate / kTargetEmptyBufferFraction, bitrate, safe_bitrate * 0.05);
+
+ bitrate = congestion_control_->GetBitrate(
+ testing_clock_.NowTicks() + base::TimeDelta::FromMilliseconds(200),
+ base::TimeDelta::FromMilliseconds(300));
+ EXPECT_NEAR(safe_bitrate / kTargetEmptyBufferFraction * 2 / 3,
+ bitrate,
+ safe_bitrate * 0.05);
+
+ bitrate = congestion_control_->GetBitrate(
+ testing_clock_.NowTicks() + base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(300));
+ EXPECT_NEAR(safe_bitrate / kTargetEmptyBufferFraction * 1 / 3,
+ bitrate,
+ safe_bitrate * 0.05);
+
+ // Add a large (100ms) frame.
+ congestion_control_->SendFrameToTransport(
+ frame_id_++, safe_bitrate * 100 / 1000, testing_clock_.NowTicks());
+
+ // Results should show that we have ~200ms to send
+ bitrate = congestion_control_->GetBitrate(
+ testing_clock_.NowTicks() + base::TimeDelta::FromMilliseconds(300),
+ base::TimeDelta::FromMilliseconds(300));
+ EXPECT_NEAR(safe_bitrate / kTargetEmptyBufferFraction * 2 / 3,
+ bitrate,
+ safe_bitrate * 0.05);
+
+ // Add another large (100ms) frame.
+ congestion_control_->SendFrameToTransport(
+ frame_id_++, safe_bitrate * 100 / 1000, testing_clock_.NowTicks());
+
+ // Resulst should show that we have ~100ms to send
+ bitrate = congestion_control_->GetBitrate(
+ testing_clock_.NowTicks() + base::TimeDelta::FromMilliseconds(300),
+ base::TimeDelta::FromMilliseconds(300));
+ EXPECT_NEAR(safe_bitrate / kTargetEmptyBufferFraction * 1 / 3,
+ bitrate,
+ safe_bitrate * 0.05);
}
-TEST_F(CongestionControlTest, Convergence60fpsLongRtt) {
- EXPECT_GE(RunWithOneLossEventPerSecond(60, 100, 100),
- GG_UINT32_C(500000));
-}
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/framer/cast_message_builder.cc b/chromium/media/cast/framer/cast_message_builder.cc
index 7d89f744315..f3473f96902 100644
--- a/chromium/media/cast/framer/cast_message_builder.cc
+++ b/chromium/media/cast/framer/cast_message_builder.cc
@@ -23,7 +23,6 @@ CastMessageBuilder::CastMessageBuilder(
decoder_faster_than_max_frame_rate_(decoder_faster_than_max_frame_rate),
max_unacked_frames_(max_unacked_frames),
cast_msg_(media_ssrc),
- waiting_for_key_frame_(true),
slowing_down_ack_(false),
acked_last_frame_(true),
last_acked_frame_id_(kStartFrameId) {
@@ -32,65 +31,61 @@ CastMessageBuilder::CastMessageBuilder(
CastMessageBuilder::~CastMessageBuilder() {}
-void CastMessageBuilder::CompleteFrameReceived(uint32 frame_id,
- bool is_key_frame) {
+void CastMessageBuilder::CompleteFrameReceived(uint32 frame_id) {
+ DCHECK_GE(static_cast<int32>(frame_id - last_acked_frame_id_), 0);
+ VLOG(2) << "CompleteFrameReceived: " << frame_id;
if (last_update_time_.is_null()) {
// Our first update.
last_update_time_ = clock_->NowTicks();
}
- if (waiting_for_key_frame_) {
- if (!is_key_frame) {
- // Ignore that we have received this complete frame since we are
- // waiting on a key frame.
- return;
- }
- waiting_for_key_frame_ = false;
- cast_msg_.missing_frames_and_packets_.clear();
- cast_msg_.ack_frame_id_ = frame_id;
- last_update_time_ = clock_->NowTicks();
- // We might have other complete frames waiting after we receive the last
- // packet in the key-frame.
- UpdateAckMessage();
- } else {
- if (!UpdateAckMessage()) return;
- BuildPacketList();
+ if (!UpdateAckMessage(frame_id)) {
+ return;
}
+ BuildPacketList();
+
// Send cast message.
- VLOG(1) << "Send cast message Ack:" << static_cast<int>(frame_id);
+ VLOG(2) << "Send cast message Ack:" << static_cast<int>(frame_id);
cast_feedback_->CastFeedback(cast_msg_);
}
-bool CastMessageBuilder::UpdateAckMessage() {
+bool CastMessageBuilder::UpdateAckMessage(uint32 frame_id) {
if (!decoder_faster_than_max_frame_rate_) {
int complete_frame_count = frame_id_map_->NumberOfCompleteFrames();
if (complete_frame_count > max_unacked_frames_) {
// We have too many frames pending in our framer; slow down ACK.
- slowing_down_ack_ = true;
+ if (!slowing_down_ack_) {
+ slowing_down_ack_ = true;
+ ack_queue_.push_back(last_acked_frame_id_);
+ }
} else if (complete_frame_count <= 1) {
// We are down to one or less frames in our framer; ACK normally.
slowing_down_ack_ = false;
+ ack_queue_.clear();
}
}
+
if (slowing_down_ack_) {
// We are slowing down acknowledgment by acknowledging every other frame.
- if (acked_last_frame_) {
- acked_last_frame_ = false;
- } else {
- acked_last_frame_ = true;
- last_acked_frame_id_++;
- // Note: frame skipping and slowdown ACK is not supported at the same
- // time; and it's not needed since we can skip frames to catch up.
+ // Note: frame skipping and slowdown ACK is not supported at the same
+ // time; and it's not needed since we can skip frames to catch up.
+ if (!ack_queue_.empty() && ack_queue_.back() == frame_id) {
+ return false;
}
- } else {
- uint32 frame_id = frame_id_map_->LastContinuousFrame();
-
- // Is it a new frame?
- if (last_acked_frame_id_ == frame_id) return false;
+ ack_queue_.push_back(frame_id);
+ if (!acked_last_frame_) {
+ ack_queue_.pop_front();
+ }
+ frame_id = ack_queue_.front();
+ }
- last_acked_frame_id_ = frame_id;
- acked_last_frame_ = true;
+ acked_last_frame_ = false;
+ // Is it a new frame?
+ if (last_acked_frame_id_ == frame_id) {
+ return false;
}
+ acked_last_frame_ = true;
+ last_acked_frame_id_ = frame_id;
cast_msg_.ack_frame_id_ = last_acked_frame_id_;
cast_msg_.missing_frames_and_packets_.clear();
last_update_time_ = clock_->NowTicks();
@@ -100,23 +95,24 @@ bool CastMessageBuilder::UpdateAckMessage() {
bool CastMessageBuilder::TimeToSendNextCastMessage(
base::TimeTicks* time_to_send) {
// We haven't received any packets.
- if (last_update_time_.is_null() && frame_id_map_->Empty()) return false;
+ if (last_update_time_.is_null() && frame_id_map_->Empty())
+ return false;
- *time_to_send = last_update_time_ +
- base::TimeDelta::FromMilliseconds(kCastMessageUpdateIntervalMs);
+ *time_to_send = last_update_time_ + base::TimeDelta::FromMilliseconds(
+ kCastMessageUpdateIntervalMs);
return true;
}
void CastMessageBuilder::UpdateCastMessage() {
RtcpCastMessage message(media_ssrc_);
- if (!UpdateCastMessageInternal(&message)) return;
+ if (!UpdateCastMessageInternal(&message))
+ return;
// Send cast message.
cast_feedback_->CastFeedback(message);
}
void CastMessageBuilder::Reset() {
- waiting_for_key_frame_ = true;
cast_msg_.ack_frame_id_ = kStartFrameId;
cast_msg_.missing_frames_and_packets_.clear();
time_last_nacked_map_.clear();
@@ -138,9 +134,10 @@ bool CastMessageBuilder::UpdateCastMessageInternal(RtcpCastMessage* message) {
}
last_update_time_ = now;
- UpdateAckMessage(); // Needed to cover when a frame is skipped.
+ // Needed to cover when a frame is skipped.
+ UpdateAckMessage(last_acked_frame_id_);
BuildPacketList();
- *message = cast_msg_;
+ message->Copy(cast_msg_);
return true;
}
@@ -151,7 +148,8 @@ void CastMessageBuilder::BuildPacketList() {
cast_msg_.missing_frames_and_packets_.clear();
// Are we missing packets?
- if (frame_id_map_->Empty()) return;
+ if (frame_id_map_->Empty())
+ return;
uint32 newest_frame_id = frame_id_map_->NewestFrameId();
uint32 next_expected_frame_id = cast_msg_.ack_frame_id_ + 1;
@@ -173,8 +171,8 @@ void CastMessageBuilder::BuildPacketList() {
PacketIdSet missing;
if (frame_id_map_->FrameExists(next_expected_frame_id)) {
bool last_frame = (newest_frame_id == next_expected_frame_id);
- frame_id_map_->GetMissingPackets(next_expected_frame_id, last_frame,
- &missing);
+ frame_id_map_->GetMissingPackets(
+ next_expected_frame_id, last_frame, &missing);
if (!missing.empty()) {
time_last_nacked_map_[next_expected_frame_id] = now;
cast_msg_.missing_frames_and_packets_.insert(
diff --git a/chromium/media/cast/framer/cast_message_builder.h b/chromium/media/cast/framer/cast_message_builder.h
index b76a196111c..9db88d4a990 100644
--- a/chromium/media/cast/framer/cast_message_builder.h
+++ b/chromium/media/cast/framer/cast_message_builder.h
@@ -7,6 +7,7 @@
#ifndef MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
#define MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
+#include <deque>
#include <map>
#include "media/cast/framer/frame_id_map.h"
@@ -30,13 +31,13 @@ class CastMessageBuilder {
int max_unacked_frames);
~CastMessageBuilder();
- void CompleteFrameReceived(uint32 frame_id, bool is_key_frame);
+ void CompleteFrameReceived(uint32 frame_id);
bool TimeToSendNextCastMessage(base::TimeTicks* time_to_send);
void UpdateCastMessage();
void Reset();
private:
- bool UpdateAckMessage();
+ bool UpdateAckMessage(uint32 frame_id);
void BuildPacketList();
bool UpdateCastMessageInternal(RtcpCastMessage* message);
@@ -51,13 +52,13 @@ class CastMessageBuilder {
RtcpCastMessage cast_msg_;
base::TimeTicks last_update_time_;
- bool waiting_for_key_frame_;
TimeLastNackMap time_last_nacked_map_;
bool slowing_down_ack_;
bool acked_last_frame_;
uint32 last_acked_frame_id_;
+ std::deque<uint32> ack_queue_;
DISALLOW_COPY_AND_ASSIGN(CastMessageBuilder);
};
diff --git a/chromium/media/cast/framer/cast_message_builder_unittest.cc b/chromium/media/cast/framer/cast_message_builder_unittest.cc
index f4b708c90ef..ef75162a086 100644
--- a/chromium/media/cast/framer/cast_message_builder_unittest.cc
+++ b/chromium/media/cast/framer/cast_message_builder_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include "base/memory/scoped_ptr.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/cast/framer/cast_message_builder.h"
@@ -12,21 +14,18 @@
namespace media {
namespace cast {
+namespace {
static const uint32 kSsrc = 0x1234;
static const uint32 kShortTimeIncrementMs = 10;
static const uint32 kLongTimeIncrementMs = 40;
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
+static const int64 kStartMillisecond = INT64_C(12345678900000);
-namespace {
typedef std::map<uint32, size_t> MissingPacketsMap;
class NackFeedbackVerification : public RtpPayloadFeedback {
public:
NackFeedbackVerification()
- : triggered_(false),
- missing_packets_(),
- last_frame_acked_(0) {}
-
+ : triggered_(false), missing_packets_(), last_frame_acked_(0) {}
virtual void CastFeedback(const RtcpCastMessage& cast_feedback) OVERRIDE {
EXPECT_EQ(kSsrc, cast_feedback.media_ssrc_);
@@ -43,10 +42,10 @@ class NackFeedbackVerification : public RtpPayloadFeedback {
if ((frame_it->second.size() == 1) &&
(*frame_it->second.begin() == kRtcpCastAllPacketsLost)) {
missing_packets_.insert(
- std::make_pair(frame_it->first, kRtcpCastAllPacketsLost));
+ std::make_pair(frame_it->first, kRtcpCastAllPacketsLost));
} else {
- missing_packets_.insert(
- std::make_pair(frame_it->first, frame_it->second.size()));
+ missing_packets_.insert(
+ std::make_pair(frame_it->first, frame_it->second.size()));
}
++frame_it;
}
@@ -56,14 +55,15 @@ class NackFeedbackVerification : public RtpPayloadFeedback {
size_t num_missing_packets(uint32 frame_id) {
MissingPacketsMap::iterator it;
it = missing_packets_.find(frame_id);
- if (it == missing_packets_.end()) return 0;
+ if (it == missing_packets_.end())
+ return 0;
return it->second;
}
// Holds value for one call.
bool triggered() {
- bool ret_val = triggered_;
+ bool ret_val = triggered_;
triggered_ = false;
return ret_val;
}
@@ -74,6 +74,8 @@ class NackFeedbackVerification : public RtpPayloadFeedback {
bool triggered_;
MissingPacketsMap missing_packets_; // Missing packets per frame.
uint32 last_frame_acked_;
+
+ DISALLOW_COPY_AND_ASSIGN(NackFeedbackVerification);
};
} // namespace
@@ -86,7 +88,7 @@ class CastMessageBuilderTest : public ::testing::Test {
kSsrc,
true,
0)) {
- rtp_header_.webrtc.header.ssrc = kSsrc;
+ rtp_header_.sender_ssrc = kSsrc;
rtp_header_.is_key_frame = false;
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
@@ -94,33 +96,23 @@ class CastMessageBuilderTest : public ::testing::Test {
virtual ~CastMessageBuilderTest() {}
- void SetFrameId(uint32 frame_id) {
+ void SetFrameIds(uint32 frame_id, uint32 reference_frame_id) {
rtp_header_.frame_id = frame_id;
+ rtp_header_.reference_frame_id = reference_frame_id;
}
- void SetPacketId(uint16 packet_id) {
- rtp_header_.packet_id = packet_id;
- }
+ void SetPacketId(uint16 packet_id) { rtp_header_.packet_id = packet_id; }
void SetMaxPacketId(uint16 max_packet_id) {
rtp_header_.max_packet_id = max_packet_id;
}
- void SetKeyFrame(bool is_key) {
- rtp_header_.is_key_frame = is_key;
- }
-
- void SetReferenceFrameId(uint32 reference_frame_id) {
- rtp_header_.is_reference = true;
- rtp_header_.reference_frame_id = reference_frame_id;
- }
+ void SetKeyFrame(bool is_key) { rtp_header_.is_key_frame = is_key; }
void InsertPacket() {
- bool complete = false;
- frame_id_map_.InsertPacket(rtp_header_, &complete);
- if (complete) {
- cast_msg_builder_->CompleteFrameReceived(rtp_header_.frame_id,
- rtp_header_.is_key_frame);
+ PacketType packet_type = frame_id_map_.InsertPacket(rtp_header_);
+ if (packet_type == kNewPacketCompletingFrame) {
+ cast_msg_builder_->CompleteFrameReceived(rtp_header_.frame_id);
}
cast_msg_builder_->UpdateCastMessage();
}
@@ -139,30 +131,12 @@ class CastMessageBuilderTest : public ::testing::Test {
RtpCastHeader rtp_header_;
FrameIdMap frame_id_map_;
base::SimpleTestTickClock testing_clock_;
-};
-TEST_F(CastMessageBuilderTest, StartWithAKeyFrame) {
- SetFrameId(3);
- SetPacketId(0);
- SetMaxPacketId(0);
- InsertPacket();
- // Should not trigger ack.
- EXPECT_FALSE(feedback_.triggered());
- SetFrameId(5);
- SetPacketId(0);
- SetMaxPacketId(0);
- SetKeyFrame(true);
- InsertPacket();
- frame_id_map_.RemoveOldFrames(5); // Simulate 5 being pulled for rendering.
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- cast_msg_builder_->UpdateCastMessage();
- EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(5u, feedback_.last_frame_acked());
-}
+ DISALLOW_COPY_AND_ASSIGN(CastMessageBuilderTest);
+};
TEST_F(CastMessageBuilderTest, OneFrameNackList) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(4);
SetMaxPacketId(10);
InsertPacket();
@@ -178,13 +152,13 @@ TEST_F(CastMessageBuilderTest, OneFrameNackList) {
}
TEST_F(CastMessageBuilderTest, CompleteFrameMissing) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(2);
SetMaxPacketId(5);
InsertPacket();
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(2);
+ SetFrameIds(2, 1);
SetPacketId(2);
SetMaxPacketId(5);
InsertPacket();
@@ -192,55 +166,30 @@ TEST_F(CastMessageBuilderTest, CompleteFrameMissing) {
EXPECT_EQ(kRtcpCastAllPacketsLost, feedback_.num_missing_packets(1));
}
-TEST_F(CastMessageBuilderTest, FastForwardAck) {
- SetFrameId(1);
- SetPacketId(0);
- SetMaxPacketId(0);
- InsertPacket();
- EXPECT_FALSE(feedback_.triggered());
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(2);
- SetPacketId(0);
- SetMaxPacketId(0);
- InsertPacket();
- EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(kStartFrameId, feedback_.last_frame_acked());
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(0);
- SetPacketId(0);
- SetMaxPacketId(0);
- SetKeyFrame(true);
- InsertPacket();
- EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(2u, feedback_.last_frame_acked());
-}
-
TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
- SetFrameId(1);
+ SetFrameIds(1, 0);
SetPacketId(0);
SetMaxPacketId(1);
InsertPacket();
EXPECT_FALSE(feedback_.triggered());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(2);
+ SetFrameIds(2, 1);
SetPacketId(0);
SetMaxPacketId(0);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(3);
+ SetFrameIds(3, 2);
SetPacketId(0);
SetMaxPacketId(5);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(kStartFrameId, feedback_.last_frame_acked());
+ EXPECT_EQ(2u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(5);
+ SetFrameIds(5, 5);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
@@ -253,7 +202,7 @@ TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
EXPECT_EQ(5u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
- SetFrameId(1);
+ SetFrameIds(1, 0);
SetPacketId(1);
SetMaxPacketId(1);
InsertPacket();
@@ -265,44 +214,8 @@ TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
EXPECT_EQ(5u, feedback_.last_frame_acked());
}
-TEST_F(CastMessageBuilderTest, WrapFastForward) {
- SetFrameId(254);
- SetPacketId(0);
- SetMaxPacketId(1);
- SetKeyFrame(true);
- InsertPacket();
- EXPECT_FALSE(feedback_.triggered());
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(255);
- SetPacketId(0);
- SetMaxPacketId(0);
- SetKeyFrame(false);
- InsertPacket();
- EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(253u, feedback_.last_frame_acked());
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(256);
- SetPacketId(0);
- SetMaxPacketId(0);
- SetKeyFrame(false);
- InsertPacket();
- EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(253u, feedback_.last_frame_acked());
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
- SetFrameId(254);
- SetPacketId(1);
- SetMaxPacketId(1);
- SetKeyFrame(true);
- InsertPacket();
- EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(256u, feedback_.last_frame_acked());
-}
-
TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacket) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(20);
SetKeyFrame(true);
@@ -316,7 +229,7 @@ TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacket) {
}
TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextFrame) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(20);
SetKeyFrame(true);
@@ -329,7 +242,7 @@ TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextFrame) {
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(4u, feedback_.num_missing_packets(0));
- SetFrameId(1);
+ SetFrameIds(1, 0);
SetMaxPacketId(2);
SetPacketId(0);
SetKeyFrame(false);
@@ -341,7 +254,7 @@ TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextFrame) {
}
TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextKey) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(20);
SetKeyFrame(true);
@@ -354,7 +267,7 @@ TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextKey) {
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(4u, feedback_.num_missing_packets(0));
- SetFrameId(1);
+ SetFrameIds(1, 1);
SetMaxPacketId(0);
SetPacketId(0);
SetKeyFrame(true);
@@ -378,7 +291,7 @@ TEST_F(CastMessageBuilderTest, Reset) {
}
TEST_F(CastMessageBuilderTest, DeltaAfterReset) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
@@ -388,7 +301,7 @@ TEST_F(CastMessageBuilderTest, DeltaAfterReset) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
cast_msg_builder_->Reset();
- SetFrameId(1);
+ SetFrameIds(1, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
@@ -396,7 +309,7 @@ TEST_F(CastMessageBuilderTest, DeltaAfterReset) {
}
TEST_F(CastMessageBuilderTest, BasicRps) {
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
@@ -405,12 +318,11 @@ TEST_F(CastMessageBuilderTest, BasicRps) {
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(0u, feedback_.last_frame_acked());
- SetFrameId(3);
+ SetFrameIds(3, 0);
SetKeyFrame(false);
- SetReferenceFrameId(0);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
- EXPECT_EQ(0u, feedback_.last_frame_acked());
+ EXPECT_EQ(3u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
frame_id_map_.RemoveOldFrames(3); // Simulate 3 being pulled for rendering.
@@ -421,7 +333,7 @@ TEST_F(CastMessageBuilderTest, BasicRps) {
TEST_F(CastMessageBuilderTest, InOrderRps) {
// Create a pattern - skip to rps, and don't look back.
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
@@ -430,7 +342,7 @@ TEST_F(CastMessageBuilderTest, InOrderRps) {
base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(0u, feedback_.last_frame_acked());
- SetFrameId(1);
+ SetFrameIds(1, 0);
SetPacketId(0);
SetMaxPacketId(1);
SetKeyFrame(false);
@@ -438,11 +350,10 @@ TEST_F(CastMessageBuilderTest, InOrderRps) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
EXPECT_FALSE(feedback_.triggered());
- SetFrameId(3);
+ SetFrameIds(3, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(false);
- SetReferenceFrameId(0);
InsertPacket();
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
@@ -453,7 +364,7 @@ TEST_F(CastMessageBuilderTest, InOrderRps) {
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(3u, feedback_.last_frame_acked());
// Make an old frame complete - should not trigger an ack.
- SetFrameId(1);
+ SetFrameIds(1, 0);
SetPacketId(1);
SetMaxPacketId(1);
SetKeyFrame(false);
@@ -466,7 +377,7 @@ TEST_F(CastMessageBuilderTest, InOrderRps) {
TEST_F(CastMessageBuilderTest, SlowDownAck) {
SetDecoderSlowerThanMaxFrameRate(3);
- SetFrameId(0);
+ SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
@@ -479,30 +390,34 @@ TEST_F(CastMessageBuilderTest, SlowDownAck) {
for (frame_id = 1; frame_id < 3; ++frame_id) {
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(frame_id - 1, feedback_.last_frame_acked());
- SetFrameId(frame_id);
+ SetFrameIds(frame_id, frame_id - 1);
InsertPacket();
testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
}
// We should now have entered the slowdown ACK state.
uint32 expected_frame_id = 1;
for (; frame_id < 10; ++frame_id) {
- if (frame_id % 2) ++expected_frame_id;
- EXPECT_TRUE(feedback_.triggered());
+ if (frame_id % 2) {
+ ++expected_frame_id;
+ EXPECT_TRUE(feedback_.triggered());
+ } else {
+ EXPECT_FALSE(feedback_.triggered());
+ }
EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked());
- SetFrameId(frame_id);
+ SetFrameIds(frame_id, frame_id - 1);
InsertPacket();
testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
}
- EXPECT_TRUE(feedback_.triggered());
+ EXPECT_FALSE(feedback_.triggered());
EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked());
// Simulate frame_id being pulled for rendering.
frame_id_map_.RemoveOldFrames(frame_id);
// We should now leave the slowdown ACK state.
++frame_id;
- SetFrameId(frame_id);
+ SetFrameIds(frame_id, frame_id - 1);
InsertPacket();
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
diff --git a/chromium/media/cast/framer/frame_buffer.cc b/chromium/media/cast/framer/frame_buffer.cc
index ca9f1dedd28..0b6fa8332cd 100644
--- a/chromium/media/cast/framer/frame_buffer.cc
+++ b/chromium/media/cast/framer/frame_buffer.cc
@@ -28,21 +28,17 @@ void FrameBuffer::InsertPacket(const uint8* payload_data,
frame_id_ = rtp_header.frame_id;
max_packet_id_ = rtp_header.max_packet_id;
is_key_frame_ = rtp_header.is_key_frame;
- if (rtp_header.is_reference) {
- last_referenced_frame_id_ = rtp_header.reference_frame_id;
- } else {
- last_referenced_frame_id_ = rtp_header.frame_id - 1;
- }
-
- rtp_timestamp_ = rtp_header.webrtc.header.timestamp;
+ if (is_key_frame_)
+ DCHECK_EQ(rtp_header.frame_id, rtp_header.reference_frame_id);
+ last_referenced_frame_id_ = rtp_header.reference_frame_id;
+ rtp_timestamp_ = rtp_header.rtp_timestamp;
}
// Is this the correct frame?
- if (rtp_header.frame_id != frame_id_) return;
+ if (rtp_header.frame_id != frame_id_)
+ return;
// Insert every packet only once.
if (packets_.find(rtp_header.packet_id) != packets_.end()) {
- VLOG(3) << "Packet already received, ignored: frame "
- << frame_id_ << ", packet " << rtp_header.packet_id;
return;
}
@@ -52,8 +48,8 @@ void FrameBuffer::InsertPacket(const uint8* payload_data,
// Insert the packet.
retval.first->second.resize(payload_size);
- std::copy(payload_data, payload_data + payload_size,
- retval.first->second.begin());
+ std::copy(
+ payload_data, payload_data + payload_size, retval.first->second.begin());
++num_packets_received_;
total_data_size_ += payload_size;
@@ -63,45 +59,27 @@ bool FrameBuffer::Complete() const {
return num_packets_received_ - 1 == max_packet_id_;
}
-bool FrameBuffer::GetEncodedAudioFrame(EncodedAudioFrame* audio_frame,
- uint32* rtp_timestamp) const {
- if (!Complete()) return false;
-
- *rtp_timestamp = rtp_timestamp_;
-
- // Frame is complete -> construct.
- audio_frame->frame_id = frame_id_;
-
- // Build the data vector.
- audio_frame->data.clear();
- audio_frame->data.reserve(total_data_size_);
- PacketMap::const_iterator it;
- for (it = packets_.begin(); it != packets_.end(); ++it) {
- audio_frame->data.insert(audio_frame->data.end(),
- it->second.begin(), it->second.end());
- }
- return true;
-}
-
-bool FrameBuffer::GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
- uint32* rtp_timestamp) const {
- if (!Complete()) return false;
-
- *rtp_timestamp = rtp_timestamp_;
+bool FrameBuffer::AssembleEncodedFrame(transport::EncodedFrame* frame) const {
+ if (!Complete())
+ return false;
// Frame is complete -> construct.
- video_frame->key_frame = is_key_frame_;
- video_frame->frame_id = frame_id_;
- video_frame->last_referenced_frame_id = last_referenced_frame_id_;
+ if (is_key_frame_)
+ frame->dependency = transport::EncodedFrame::KEY;
+ else if (frame_id_ == last_referenced_frame_id_)
+ frame->dependency = transport::EncodedFrame::INDEPENDENT;
+ else
+ frame->dependency = transport::EncodedFrame::DEPENDENT;
+ frame->frame_id = frame_id_;
+ frame->referenced_frame_id = last_referenced_frame_id_;
+ frame->rtp_timestamp = rtp_timestamp_;
// Build the data vector.
- video_frame->data.clear();
- video_frame->data.reserve(total_data_size_);
+ frame->data.clear();
+ frame->data.reserve(total_data_size_);
PacketMap::const_iterator it;
- for (it = packets_.begin(); it != packets_.end(); ++it) {
- video_frame->data.insert(video_frame->data.end(),
- it->second.begin(), it->second.end());
- }
+ for (it = packets_.begin(); it != packets_.end(); ++it)
+ frame->data.insert(frame->data.end(), it->second.begin(), it->second.end());
return true;
}
diff --git a/chromium/media/cast/framer/frame_buffer.h b/chromium/media/cast/framer/frame_buffer.h
index b99f2b2582d..d4d5dedbbde 100644
--- a/chromium/media/cast/framer/frame_buffer.h
+++ b/chromium/media/cast/framer/frame_buffer.h
@@ -25,11 +25,11 @@ class FrameBuffer {
const RtpCastHeader& rtp_header);
bool Complete() const;
- bool GetEncodedAudioFrame(EncodedAudioFrame* audio_frame,
- uint32* rtp_timestamp) const;
-
- bool GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
- uint32* rtp_timestamp) const;
+ // If a frame is complete, sets the frame IDs and RTP timestamp in |frame|,
+ // and also copies the data from all packets into the data field in |frame|.
+ // Returns true if the frame was complete; false if incomplete and |frame|
+ // remains unchanged.
+ bool AssembleEncodedFrame(transport::EncodedFrame* frame) const;
bool is_key_frame() const { return is_key_frame_; }
diff --git a/chromium/media/cast/framer/frame_buffer_unittest.cc b/chromium/media/cast/framer/frame_buffer_unittest.cc
index fb14da39f7f..d6844f3e952 100644
--- a/chromium/media/cast/framer/frame_buffer_unittest.cc
+++ b/chromium/media/cast/framer/frame_buffer_unittest.cc
@@ -10,42 +10,45 @@ namespace cast {
class FrameBufferTest : public ::testing::Test {
protected:
- FrameBufferTest() {}
+ FrameBufferTest() {
+ payload_.assign(kMaxIpPacketSize, 0);
+ }
virtual ~FrameBufferTest() {}
- virtual void SetUp() {
- payload_.assign(kIpPacketSize, 0);
-
- // Build a default one packet frame - populate webrtc header.
- rtp_header_.is_key_frame = false;
- rtp_header_.frame_id = 0;
- rtp_header_.packet_id = 0;
- rtp_header_.max_packet_id = 0;
- rtp_header_.is_reference = false;
- rtp_header_.reference_frame_id = 0;
- }
-
FrameBuffer buffer_;
std::vector<uint8> payload_;
RtpCastHeader rtp_header_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameBufferTest);
};
+TEST_F(FrameBufferTest, OnePacketInsertSanity) {
+ rtp_header_.rtp_timestamp = 3000;
+ rtp_header_.is_key_frame = true;
+ rtp_header_.frame_id = 5;
+ rtp_header_.reference_frame_id = 5;
+ buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ transport::EncodedFrame frame;
+ EXPECT_TRUE(buffer_.AssembleEncodedFrame(&frame));
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
+ EXPECT_EQ(5u, frame.frame_id);
+ EXPECT_EQ(5u, frame.referenced_frame_id);
+ EXPECT_EQ(3000u, frame.rtp_timestamp);
+}
+
TEST_F(FrameBufferTest, EmptyBuffer) {
EXPECT_FALSE(buffer_.Complete());
- EXPECT_FALSE(buffer_.is_key_frame());
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
- EXPECT_FALSE(buffer_.GetEncodedVideoFrame(&frame, &rtp_timestamp));
+ transport::EncodedFrame frame;
+ EXPECT_FALSE(buffer_.AssembleEncodedFrame(&frame));
}
TEST_F(FrameBufferTest, DefaultOnePacketFrame) {
buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
EXPECT_TRUE(buffer_.Complete());
EXPECT_FALSE(buffer_.is_key_frame());
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
- EXPECT_TRUE(buffer_.GetEncodedVideoFrame(&frame, &rtp_timestamp));
+ transport::EncodedFrame frame;
+ EXPECT_TRUE(buffer_.AssembleEncodedFrame(&frame));
EXPECT_EQ(payload_.size(), frame.data.size());
}
@@ -60,13 +63,12 @@ TEST_F(FrameBufferTest, MultiplePacketFrame) {
++rtp_header_.packet_id;
EXPECT_TRUE(buffer_.Complete());
EXPECT_TRUE(buffer_.is_key_frame());
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
- EXPECT_TRUE(buffer_.GetEncodedVideoFrame(&frame, &rtp_timestamp));
+ transport::EncodedFrame frame;
+ EXPECT_TRUE(buffer_.AssembleEncodedFrame(&frame));
EXPECT_EQ(3 * payload_.size(), frame.data.size());
}
-TEST_F(FrameBufferTest, InCompleteFrame) {
+TEST_F(FrameBufferTest, IncompleteFrame) {
rtp_header_.max_packet_id = 4;
buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
++rtp_header_.packet_id;
diff --git a/chromium/media/cast/framer/frame_id_map.cc b/chromium/media/cast/framer/frame_id_map.cc
index bd9b943371c..b4389fd5323 100644
--- a/chromium/media/cast/framer/frame_id_map.cc
+++ b/chromium/media/cast/framer/frame_id_map.cc
@@ -18,28 +18,27 @@ FrameInfo::FrameInfo(uint32 frame_id,
frame_id_(frame_id),
referenced_frame_id_(referenced_frame_id),
max_received_packet_id_(0) {
- if (max_packet_id > 0) {
- // Create the set with all packets missing.
- for (uint16 i = 0; i <= max_packet_id; i++) {
- missing_packets_.insert(i);
- }
+ // Create the set with all packets missing.
+ for (uint16 i = 0; i <= max_packet_id; i++) {
+ missing_packets_.insert(i);
}
}
FrameInfo::~FrameInfo() {}
-bool FrameInfo::InsertPacket(uint16 packet_id) {
+PacketType FrameInfo::InsertPacket(uint16 packet_id) {
+ if (missing_packets_.find(packet_id) == missing_packets_.end()) {
+ return kDuplicatePacket;
+ }
// Update the last received packet id.
if (IsNewerPacketId(packet_id, max_received_packet_id_)) {
max_received_packet_id_ = packet_id;
}
missing_packets_.erase(packet_id);
- return missing_packets_.empty();
+ return missing_packets_.empty() ? kNewPacketCompletingFrame : kNewPacket;
}
-bool FrameInfo::Complete() const {
- return missing_packets_.empty();
-}
+bool FrameInfo::Complete() const { return missing_packets_.empty(); }
void FrameInfo::GetMissingPackets(bool newest_frame,
PacketIdSet* missing_packets) const {
@@ -53,35 +52,29 @@ void FrameInfo::GetMissingPackets(bool newest_frame,
}
}
-
FrameIdMap::FrameIdMap()
: waiting_for_key_(true),
last_released_frame_(kStartFrameId),
- newest_frame_id_(kStartFrameId) {
-}
+ newest_frame_id_(kStartFrameId) {}
FrameIdMap::~FrameIdMap() {}
-bool FrameIdMap::InsertPacket(const RtpCastHeader& rtp_header, bool* complete) {
+PacketType FrameIdMap::InsertPacket(const RtpCastHeader& rtp_header) {
uint32 frame_id = rtp_header.frame_id;
uint32 reference_frame_id;
- if (rtp_header.is_reference) {
- reference_frame_id = rtp_header.reference_frame_id;
- } else {
- reference_frame_id = static_cast<uint32>(frame_id - 1);
- }
+ reference_frame_id = rtp_header.reference_frame_id;
if (rtp_header.is_key_frame && waiting_for_key_) {
last_released_frame_ = static_cast<uint32>(frame_id - 1);
waiting_for_key_ = false;
}
- VLOG(1) << "InsertPacket frame:" << frame_id
+ VLOG(3) << "InsertPacket frame:" << frame_id
<< " packet:" << static_cast<int>(rtp_header.packet_id)
<< " max packet:" << static_cast<int>(rtp_header.max_packet_id);
if (IsOlderFrameId(frame_id, last_released_frame_) && !waiting_for_key_) {
- return false;
+ return kTooOldPacket;
}
// Update the last received frame id.
@@ -91,6 +84,7 @@ bool FrameIdMap::InsertPacket(const RtpCastHeader& rtp_header, bool* complete) {
// Does this packet belong to a new frame?
FrameMap::iterator it = frame_map_.find(frame_id);
+ PacketType packet_type;
if (it == frame_map_.end()) {
// New frame.
linked_ptr<FrameInfo> frame_info(new FrameInfo(frame_id,
@@ -100,12 +94,12 @@ bool FrameIdMap::InsertPacket(const RtpCastHeader& rtp_header, bool* complete) {
std::pair<FrameMap::iterator, bool> retval =
frame_map_.insert(std::make_pair(frame_id, frame_info));
- *complete = retval.first->second->InsertPacket(rtp_header.packet_id);
+ packet_type = retval.first->second->InsertPacket(rtp_header.packet_id);
} else {
// Insert packet to existing frame.
- *complete = it->second->InsertPacket(rtp_header.packet_id);
+ packet_type = it->second->InsertPacket(rtp_header.packet_id);
}
- return true;
+ return packet_type;
}
void FrameIdMap::RemoveOldFrames(uint32 frame_id) {
@@ -129,9 +123,7 @@ void FrameIdMap::Clear() {
newest_frame_id_ = kStartFrameId;
}
-uint32 FrameIdMap::NewestFrameId() const {
- return newest_frame_id_;
-}
+uint32 FrameIdMap::NewestFrameId() const { return newest_frame_id_; }
bool FrameIdMap::NextContinuousFrame(uint32* frame_id) const {
FrameMap::const_iterator it;
@@ -145,6 +137,22 @@ bool FrameIdMap::NextContinuousFrame(uint32* frame_id) const {
return false;
}
+bool FrameIdMap::HaveMultipleDecodableFrames() const {
+ // Find the oldest decodable frame.
+ FrameMap::const_iterator it;
+ bool found_one = false;
+ for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
+ if (it->second->Complete() && DecodableFrame(it->second.get())) {
+ if (found_one) {
+ return true;
+ } else {
+ found_one = true;
+ }
+ }
+ }
+ return false;
+}
+
uint32 FrameIdMap::LastContinuousFrame() const {
uint32 last_continuous_frame_id = last_released_frame_;
uint32 next_expected_frame = last_released_frame_;
@@ -154,8 +162,10 @@ uint32 FrameIdMap::LastContinuousFrame() const {
do {
next_expected_frame++;
it = frame_map_.find(next_expected_frame);
- if (it == frame_map_.end()) break;
- if (!it->second->Complete()) break;
+ if (it == frame_map_.end())
+ break;
+ if (!it->second->Complete())
+ break;
// We found the next continuous frame.
last_continuous_frame_id = it->first;
@@ -163,52 +173,26 @@ uint32 FrameIdMap::LastContinuousFrame() const {
return last_continuous_frame_id;
}
-bool FrameIdMap::NextAudioFrameAllowingMissingFrames(uint32* frame_id) const {
- // First check if we have continuous frames.
- if (NextContinuousFrame(frame_id)) return true;
-
- // Find the oldest frame.
- FrameMap::const_iterator it_best_match = frame_map_.end();
- FrameMap::const_iterator it;
-
- // Find first complete frame.
- for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
- if (it->second->Complete()) {
- it_best_match = it;
- break;
- }
- }
- if (it_best_match == frame_map_.end()) return false; // No complete frame.
-
- ++it;
- for (; it != frame_map_.end(); ++it) {
- if (it->second->Complete() &&
- IsOlderFrameId(it->first, it_best_match->first)) {
- it_best_match = it;
- }
- }
- *frame_id = it_best_match->first;
- return true;
-}
-
-bool FrameIdMap::NextVideoFrameAllowingSkippingFrames(uint32* frame_id) const {
+bool FrameIdMap::NextFrameAllowingSkippingFrames(uint32* frame_id) const {
// Find the oldest decodable frame.
FrameMap::const_iterator it_best_match = frame_map_.end();
FrameMap::const_iterator it;
for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
- if (it->second->Complete() && DecodableVideoFrame(it->second.get())) {
- it_best_match = it;
+ if (it->second->Complete() && DecodableFrame(it->second.get())) {
+ if (it_best_match == frame_map_.end() ||
+ IsOlderFrameId(it->first, it_best_match->first)) {
+ it_best_match = it;
+ }
}
}
- if (it_best_match == frame_map_.end()) return false;
+ if (it_best_match == frame_map_.end())
+ return false;
*frame_id = it_best_match->first;
return true;
}
-bool FrameIdMap::Empty() const {
- return frame_map_.empty();
-}
+bool FrameIdMap::Empty() const { return frame_map_.empty(); }
int FrameIdMap::NumberOfCompleteFrames() const {
int count = 0;
@@ -229,20 +213,27 @@ void FrameIdMap::GetMissingPackets(uint32 frame_id,
bool last_frame,
PacketIdSet* missing_packets) const {
FrameMap::const_iterator it = frame_map_.find(frame_id);
- if (it == frame_map_.end()) return;
+ if (it == frame_map_.end())
+ return;
it->second->GetMissingPackets(last_frame, missing_packets);
}
bool FrameIdMap::ContinuousFrame(FrameInfo* frame) const {
DCHECK(frame);
- if (waiting_for_key_ && !frame->is_key_frame()) return false;
+ if (waiting_for_key_ && !frame->is_key_frame())
+ return false;
return static_cast<uint32>(last_released_frame_ + 1) == frame->frame_id();
}
-bool FrameIdMap::DecodableVideoFrame(FrameInfo* frame) const {
- if (frame->is_key_frame()) return true;
- if (waiting_for_key_ && !frame->is_key_frame()) return false;
+bool FrameIdMap::DecodableFrame(FrameInfo* frame) const {
+ if (frame->is_key_frame())
+ return true;
+ if (waiting_for_key_ && !frame->is_key_frame())
+ return false;
+ // Self-reference?
+ if (frame->referenced_frame_id() == frame->frame_id())
+ return true;
// Current frame is not necessarily referencing the last frame.
// Do we have the reference frame?
diff --git a/chromium/media/cast/framer/frame_id_map.h b/chromium/media/cast/framer/frame_id_map.h
index 40b0a7f3399..66e306f6718 100644
--- a/chromium/media/cast/framer/frame_id_map.h
+++ b/chromium/media/cast/framer/frame_id_map.h
@@ -25,11 +25,9 @@ class FrameInfo {
bool key_frame);
~FrameInfo();
- // Returns true if packet is inserted.
- bool InsertPacket(uint16 packet_id);
+ PacketType InsertPacket(uint16 packet_id);
bool Complete() const;
- void GetMissingPackets(bool newest_frame,
- PacketIdSet* missing_packets) const;
+ void GetMissingPackets(bool newest_frame, PacketIdSet* missing_packets) const;
bool is_key_frame() const { return is_key_frame_; }
uint32 frame_id() const { return frame_id_; }
@@ -53,8 +51,7 @@ class FrameIdMap {
FrameIdMap();
~FrameIdMap();
- // Returns false if not a valid (old) packet, otherwise returns true.
- bool InsertPacket(const RtpCastHeader& rtp_header, bool* complete);
+ PacketType InsertPacket(const RtpCastHeader& rtp_header);
bool Empty() const;
bool FrameExists(uint32 frame_id) const;
@@ -67,8 +64,8 @@ class FrameIdMap {
bool NextContinuousFrame(uint32* frame_id) const;
uint32 LastContinuousFrame() const;
- bool NextAudioFrameAllowingMissingFrames(uint32* frame_id) const;
- bool NextVideoFrameAllowingSkippingFrames(uint32* frame_id) const;
+ bool NextFrameAllowingSkippingFrames(uint32* frame_id) const;
+ bool HaveMultipleDecodableFrames() const;
int NumberOfCompleteFrames() const;
void GetMissingPackets(uint32 frame_id,
@@ -77,7 +74,7 @@ class FrameIdMap {
private:
bool ContinuousFrame(FrameInfo* frame) const;
- bool DecodableVideoFrame(FrameInfo* frame) const;
+ bool DecodableFrame(FrameInfo* frame) const;
FrameMap frame_map_;
bool waiting_for_key_;
diff --git a/chromium/media/cast/framer/framer.cc b/chromium/media/cast/framer/framer.cc
index b06e60fd035..de4451a3b4a 100644
--- a/chromium/media/cast/framer/framer.cc
+++ b/chromium/media/cast/framer/framer.cc
@@ -17,9 +17,13 @@ Framer::Framer(base::TickClock* clock,
bool decoder_faster_than_max_frame_rate,
int max_unacked_frames)
: decoder_faster_than_max_frame_rate_(decoder_faster_than_max_frame_rate),
- cast_msg_builder_(new CastMessageBuilder(clock, incoming_payload_feedback,
- &frame_id_map_, ssrc, decoder_faster_than_max_frame_rate,
- max_unacked_frames)) {
+ cast_msg_builder_(
+ new CastMessageBuilder(clock,
+ incoming_payload_feedback,
+ &frame_id_map_,
+ ssrc,
+ decoder_faster_than_max_frame_rate,
+ max_unacked_frames)) {
DCHECK(incoming_payload_feedback) << "Invalid argument";
}
@@ -27,9 +31,20 @@ Framer::~Framer() {}
bool Framer::InsertPacket(const uint8* payload_data,
size_t payload_size,
- const RtpCastHeader& rtp_header) {
- bool complete = false;
- if (!frame_id_map_.InsertPacket(rtp_header, &complete)) return false;
+ const RtpCastHeader& rtp_header,
+ bool* duplicate) {
+ *duplicate = false;
+ PacketType packet_type = frame_id_map_.InsertPacket(rtp_header);
+ if (packet_type == kTooOldPacket) {
+ return false;
+ }
+ if (packet_type == kDuplicatePacket) {
+ VLOG(3) << "Packet already received, ignored: frame "
+ << static_cast<int>(rtp_header.frame_id) << ", packet "
+ << rtp_header.packet_id;
+ *duplicate = true;
+ return false;
+ }
// Does this packet belong to a new frame?
FrameList::iterator it = frames_.find(rtp_header.frame_id);
@@ -43,42 +58,15 @@ bool Framer::InsertPacket(const uint8* payload_data,
it->second->InsertPacket(payload_data, payload_size, rtp_header);
}
- if (complete) {
- // ACK as soon as possible.
- VLOG(1) << "Complete frame " << static_cast<int>(rtp_header.frame_id);
- cast_msg_builder_->CompleteFrameReceived(rtp_header.frame_id,
- rtp_header.is_key_frame);
- }
- return complete;
+ return packet_type == kNewPacketCompletingFrame;
}
// This does not release the frame.
-bool Framer::GetEncodedAudioFrame(EncodedAudioFrame* audio_frame,
- uint32* rtp_timestamp,
- bool* next_frame) {
- uint32 frame_id;
- // Find frame id.
- if (frame_id_map_.NextContinuousFrame(&frame_id)) {
- // We have our next frame.
- *next_frame = true;
- } else {
- if (!frame_id_map_.NextAudioFrameAllowingMissingFrames(&frame_id)) {
- return false;
- }
- *next_frame = false;
- }
-
- ConstFrameIterator it = frames_.find(frame_id);
- DCHECK(it != frames_.end());
- if (it == frames_.end()) return false;
-
- return it->second->GetEncodedAudioFrame(audio_frame, rtp_timestamp);
-}
+bool Framer::GetEncodedFrame(transport::EncodedFrame* frame,
+ bool* next_frame,
+ bool* have_multiple_decodable_frames) {
+ *have_multiple_decodable_frames = frame_id_map_.HaveMultipleDecodableFrames();
-// This does not release the frame.
-bool Framer::GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
- uint32* rtp_timestamp,
- bool* next_frame) {
uint32 frame_id;
// Find frame id.
if (frame_id_map_.NextContinuousFrame(&frame_id)) {
@@ -86,19 +74,26 @@ bool Framer::GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
*next_frame = true;
} else {
// Check if we can skip frames when our decoder is too slow.
- if (!decoder_faster_than_max_frame_rate_) return false;
+ if (!decoder_faster_than_max_frame_rate_)
+ return false;
- if (!frame_id_map_.NextVideoFrameAllowingSkippingFrames(&frame_id)) {
+ if (!frame_id_map_.NextFrameAllowingSkippingFrames(&frame_id)) {
return false;
}
*next_frame = false;
}
+ if (*next_frame) {
+ VLOG(2) << "ACK frame " << frame_id;
+ cast_msg_builder_->CompleteFrameReceived(frame_id);
+ }
+
ConstFrameIterator it = frames_.find(frame_id);
DCHECK(it != frames_.end());
- if (it == frames_.end()) return false;
+ if (it == frames_.end())
+ return false;
- return it->second->GetEncodedVideoFrame(video_frame, rtp_timestamp);
+ return it->second->AssembleEncodedFrame(frame);
}
void Framer::Reset() {
@@ -114,7 +109,7 @@ void Framer::ReleaseFrame(uint32 frame_id) {
// We have a frame - remove all frames with lower frame id.
bool skipped_old_frame = false;
FrameList::iterator it;
- for (it = frames_.begin(); it != frames_.end(); ) {
+ for (it = frames_.begin(); it != frames_.end();) {
if (IsOlderFrameId(it->first, frame_id)) {
frames_.erase(it++);
skipped_old_frame = true;
@@ -131,9 +126,7 @@ bool Framer::TimeToSendNextCastMessage(base::TimeTicks* time_to_send) {
return cast_msg_builder_->TimeToSendNextCastMessage(time_to_send);
}
-void Framer::SendCastMessage() {
- cast_msg_builder_->UpdateCastMessage();
-}
+void Framer::SendCastMessage() { cast_msg_builder_->UpdateCastMessage(); }
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/framer/framer.gyp b/chromium/media/cast/framer/framer.gyp
deleted file mode 100644
index 7b124f0c5de..00000000000
--- a/chromium/media/cast/framer/framer.gyp
+++ /dev/null
@@ -1,27 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_framer',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc',
- ],
- 'sources': [
- 'cast_message_builder.cc',
- 'cast_message_builder.h',
- 'frame_buffer.cc',
- 'frame_buffer.h',
- 'frame_id_map.cc',
- 'frame_id_map.h',
- 'framer.cc',
- 'framer.h',
- ],
- },
- ], # targets
-}
diff --git a/chromium/media/cast/framer/framer.h b/chromium/media/cast/framer/framer.h
index cf72da6c35d..0b7249eff34 100644
--- a/chromium/media/cast/framer/framer.h
+++ b/chromium/media/cast/framer/framer.h
@@ -33,22 +33,21 @@ class Framer {
~Framer();
// Return true when receiving the last packet in a frame, creating a
- // complete frame.
+ // complete frame. If a duplicate packet for an already complete frame is
+ // received, the function returns false but sets |duplicate| to true.
bool InsertPacket(const uint8* payload_data,
size_t payload_size,
- const RtpCastHeader& rtp_header);
-
- // Extracts a complete encoded frame - will only return a complete continuous
- // frame.
- // Returns false if the frame does not exist or if the frame is not complete
- // within the given time frame.
- bool GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
- uint32* rtp_timestamp,
- bool* next_frame);
-
- bool GetEncodedAudioFrame(EncodedAudioFrame* audio_frame,
- uint32* rtp_timestamp,
- bool* next_frame);
+ const RtpCastHeader& rtp_header,
+ bool* duplicate);
+
+ // Extracts a complete encoded frame - will only return a complete and
+ // decodable frame. Returns false if no such frames exist.
+ // |next_frame| will be set to true if the returned frame is the very
+ // next frame. |have_multiple_complete_frames| will be set to true
+ // if there are more decodadble frames available.
+ bool GetEncodedFrame(transport::EncodedFrame* video_frame,
+ bool* next_frame,
+ bool* have_multiple_complete_frames);
void ReleaseFrame(uint32 frame_id);
diff --git a/chromium/media/cast/framer/framer_unittest.cc b/chromium/media/cast/framer/framer_unittest.cc
index 871f048af46..ad53ef06eee 100644
--- a/chromium/media/cast/framer/framer_unittest.cc
+++ b/chromium/media/cast/framer/framer_unittest.cc
@@ -15,317 +15,484 @@ class FramerTest : public ::testing::Test {
FramerTest()
: mock_rtp_payload_feedback_(),
framer_(&testing_clock_, &mock_rtp_payload_feedback_, 0, true, 0) {
+ payload_.assign(kMaxIpPacketSize, 0);
+
+ EXPECT_CALL(mock_rtp_payload_feedback_, CastFeedback(testing::_))
+ .WillRepeatedly(testing::Return());
}
virtual ~FramerTest() {}
- virtual void SetUp() OVERRIDE {
- // Build a default one packet frame - populate webrtc header.
- rtp_header_.is_key_frame = false;
- rtp_header_.frame_id = 0;
- rtp_header_.packet_id = 0;
- rtp_header_.max_packet_id = 0;
- rtp_header_.is_reference = false;
- rtp_header_.reference_frame_id = 0;
- payload_.assign(kIpPacketSize, 0);
-
- EXPECT_CALL(mock_rtp_payload_feedback_,
- CastFeedback(testing::_)).WillRepeatedly(testing::Return());
- }
-
std::vector<uint8> payload_;
RtpCastHeader rtp_header_;
MockRtpPayloadFeedback mock_rtp_payload_feedback_;
Framer framer_;
base::SimpleTestTickClock testing_clock_;
-};
+ DISALLOW_COPY_AND_ASSIGN(FramerTest);
+};
TEST_F(FramerTest, EmptyState) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ bool multiple = false;
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
}
TEST_F(FramerTest, AlwaysStartWithKey) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool complete = false;
+ bool multiple = false;
+ bool duplicate = false;
// Insert non key first frame.
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
rtp_header_.frame_id = 1;
+ rtp_header_.reference_frame_id = 1;
rtp_header_.is_key_frame = true;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_TRUE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(1u, frame.frame_id);
- EXPECT_TRUE(frame.key_frame);
+ EXPECT_EQ(1u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
}
TEST_F(FramerTest, CompleteFrame) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool complete = false;
+ bool multiple = false;
+ bool duplicate = false;
- // start with a complete key frame.
+ // Start with a complete key frame.
rtp_header_.is_key_frame = true;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(0u, frame.frame_id);
- EXPECT_TRUE(frame.key_frame);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
// Incomplete delta.
++rtp_header_.frame_id;
+ rtp_header_.reference_frame_id = rtp_header_.frame_id - 1;
rtp_header_.is_key_frame = false;
rtp_header_.max_packet_id = 2;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
// Complete delta - can't skip, as incomplete sequence.
++rtp_header_.frame_id;
+ rtp_header_.reference_frame_id = rtp_header_.frame_id - 1;
rtp_header_.max_packet_id = 0;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+}
+
+TEST_F(FramerTest, DuplicatePackets) {
+ transport::EncodedFrame frame;
+ bool next_frame = false;
+ bool complete = false;
+ bool multiple = false;
+ bool duplicate = false;
+
+ // Start with an incomplete key frame.
+ rtp_header_.is_key_frame = true;
+ rtp_header_.max_packet_id = 1;
+ duplicate = true;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_FALSE(duplicate);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+
+ // Add same packet again in incomplete key frame.
+ duplicate = false;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_TRUE(duplicate);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+
+ // Complete key frame.
+ rtp_header_.packet_id = 1;
+ duplicate = true;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_FALSE(duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
+
+ // Add same packet again in complete key frame.
+ duplicate = false;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_TRUE(duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
+ EXPECT_EQ(0u, frame.frame_id);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
+ framer_.ReleaseFrame(frame.frame_id);
+
+ // Incomplete delta frame.
+ ++rtp_header_.frame_id;
+ rtp_header_.reference_frame_id = rtp_header_.frame_id - 1;
+ rtp_header_.packet_id = 0;
+ rtp_header_.is_key_frame = false;
+ duplicate = true;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_FALSE(duplicate);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+
+ // Add same packet again in incomplete delta frame.
+ duplicate = false;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_TRUE(duplicate);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+
+ // Complete delta frame.
+ rtp_header_.packet_id = 1;
+ duplicate = true;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_FALSE(duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT, frame.dependency);
+ EXPECT_EQ(1u, frame.frame_id);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
+ EXPECT_FALSE(multiple);
+
+ // Add same packet again in complete delta frame.
+ duplicate = false;
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(complete);
+ EXPECT_TRUE(duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT, frame.dependency);
+ EXPECT_EQ(1u, frame.frame_id);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
+ EXPECT_FALSE(multiple);
}
TEST_F(FramerTest, ContinuousSequence) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool complete = false;
+ bool multiple = false;
+ bool duplicate = false;
- // start with a complete key frame.
+ // Start with a complete key frame.
rtp_header_.is_key_frame = true;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(0u, frame.frame_id);
- EXPECT_TRUE(frame.key_frame);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
// Complete - not continuous.
rtp_header_.frame_id = 2;
+ rtp_header_.reference_frame_id = rtp_header_.frame_id - 1;
rtp_header_.is_key_frame = false;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
}
TEST_F(FramerTest, Wrap) {
// Insert key frame, frame_id = 255 (will jump to that)
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool multiple = true;
+ bool duplicate = false;
// Start with a complete key frame.
rtp_header_.is_key_frame = true;
- rtp_header_.frame_id = 255u;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ rtp_header_.frame_id = 255;
+ rtp_header_.reference_frame_id = 255;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(255u, frame.frame_id);
+ EXPECT_EQ(255u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
// Insert wrapped delta frame - should be continuous.
rtp_header_.is_key_frame = false;
rtp_header_.frame_id = 256;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT, frame.dependency);
EXPECT_EQ(256u, frame.frame_id);
+ EXPECT_EQ(255u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
}
TEST_F(FramerTest, Reset) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool complete = false;
+ bool multiple = true;
+ bool duplicate = false;
// Start with a complete key frame.
rtp_header_.is_key_frame = true;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ complete = framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(complete);
framer_.Reset();
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
}
TEST_F(FramerTest, RequireKeyAfterReset) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool multiple = false;
+ bool duplicate = false;
+
framer_.Reset();
// Start with a complete key frame.
rtp_header_.is_key_frame = false;
- rtp_header_.frame_id = 0u;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ rtp_header_.frame_id = 0;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
rtp_header_.frame_id = 1;
+ rtp_header_.reference_frame_id = 1;
rtp_header_.is_key_frame = true;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_TRUE(multiple);
}
TEST_F(FramerTest, BasicNonLastReferenceId) {
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool multiple = false;
+ bool duplicate = false;
+
rtp_header_.is_key_frame = true;
rtp_header_.frame_id = 0;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+ EXPECT_FALSE(multiple);
framer_.ReleaseFrame(frame.frame_id);
rtp_header_.is_key_frame = false;
- rtp_header_.is_reference = true;
rtp_header_.reference_frame_id = 0;
- rtp_header_.frame_id = 5u;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ rtp_header_.frame_id = 5;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_FALSE(next_frame);
+ EXPECT_FALSE(multiple);
}
TEST_F(FramerTest, InOrderReferenceFrameSelection) {
// Create pattern: 0, 1, 4, 5.
- EncodedVideoFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool multiple = false;
+ bool duplicate = false;
rtp_header_.is_key_frame = true;
rtp_header_.frame_id = 0;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
rtp_header_.is_key_frame = false;
rtp_header_.frame_id = 1;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
// Insert frame #2 partially.
rtp_header_.frame_id = 2;
rtp_header_.max_packet_id = 1;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
rtp_header_.frame_id = 4;
rtp_header_.max_packet_id = 0;
- rtp_header_.is_reference = true;
rtp_header_.reference_frame_id = 0;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(0u, frame.frame_id);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
+ EXPECT_FALSE(multiple);
framer_.ReleaseFrame(frame.frame_id);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_TRUE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT, frame.dependency);
EXPECT_EQ(1u, frame.frame_id);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_FALSE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT, frame.dependency);
EXPECT_EQ(4u, frame.frame_id);
+ EXPECT_EQ(0u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
// Insert remaining packet of frame #2 - should no be continuous.
rtp_header_.frame_id = 2;
rtp_header_.packet_id = 1;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_FALSE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
- rtp_header_.is_reference = false;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_FALSE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
rtp_header_.frame_id = 5;
+ rtp_header_.reference_frame_id = rtp_header_.frame_id - 1;
rtp_header_.packet_id = 0;
rtp_header_.max_packet_id = 0;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedVideoFrame(&frame, &rtp_timestamp,
- &next_frame));
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT, frame.dependency);
EXPECT_EQ(5u, frame.frame_id);
+ EXPECT_EQ(4u, frame.referenced_frame_id);
}
TEST_F(FramerTest, AudioWrap) {
// All audio frames are marked as key frames.
- EncodedAudioFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool multiple = false;
+ bool duplicate = false;
+
rtp_header_.is_key_frame = true;
rtp_header_.frame_id = 254;
+ rtp_header_.reference_frame_id = 254;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedAudioFrame(&frame, &rtp_timestamp,
- &next_frame));
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(254u, frame.frame_id);
+ EXPECT_EQ(254u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
rtp_header_.frame_id = 255;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ rtp_header_.reference_frame_id = 255;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
// Insert wrapped frame - should be continuous.
rtp_header_.frame_id = 256;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ rtp_header_.reference_frame_id = 256;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
- EXPECT_TRUE(framer_.GetEncodedAudioFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_TRUE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(255u, frame.frame_id);
+ EXPECT_EQ(255u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
- EXPECT_TRUE(framer_.GetEncodedAudioFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(256u, frame.frame_id);
+ EXPECT_EQ(256u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
}
TEST_F(FramerTest, AudioWrapWithMissingFrame) {
// All audio frames are marked as key frames.
- EncodedAudioFrame frame;
- uint32 rtp_timestamp;
+ transport::EncodedFrame frame;
bool next_frame = false;
+ bool multiple = true;
+ bool duplicate = false;
// Insert and get first packet.
rtp_header_.is_key_frame = true;
rtp_header_.frame_id = 253;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
- EXPECT_TRUE(framer_.GetEncodedAudioFrame(&frame, &rtp_timestamp,
- &next_frame));
+ rtp_header_.reference_frame_id = 253;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(253u, frame.frame_id);
+ EXPECT_EQ(253u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
// Insert third and fourth packets.
rtp_header_.frame_id = 255;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ rtp_header_.reference_frame_id = 255;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
rtp_header_.frame_id = 256;
- framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+ rtp_header_.reference_frame_id = 256;
+ framer_.InsertPacket(
+ payload_.data(), payload_.size(), rtp_header_, &duplicate);
// Get third and fourth packets.
- EXPECT_TRUE(framer_.GetEncodedAudioFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_FALSE(next_frame);
+ EXPECT_TRUE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(255u, frame.frame_id);
+ EXPECT_EQ(255u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
- EXPECT_TRUE(framer_.GetEncodedAudioFrame(&frame, &rtp_timestamp,
- &next_frame));
+ EXPECT_TRUE(framer_.GetEncodedFrame(&frame, &next_frame, &multiple));
EXPECT_TRUE(next_frame);
+ EXPECT_FALSE(multiple);
+ EXPECT_EQ(transport::EncodedFrame::KEY, frame.dependency);
EXPECT_EQ(256u, frame.frame_id);
+ EXPECT_EQ(256u, frame.referenced_frame_id);
framer_.ReleaseFrame(frame.frame_id);
}
diff --git a/chromium/media/cast/logging/encoding_event_subscriber.cc b/chromium/media/cast/logging/encoding_event_subscriber.cc
new file mode 100644
index 00000000000..48cc911ba80
--- /dev/null
+++ b/chromium/media/cast/logging/encoding_event_subscriber.cc
@@ -0,0 +1,286 @@
+// 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/logging/encoding_event_subscriber.h"
+
+#include <cstring>
+#include <utility>
+
+#include "base/logging.h"
+#include "media/cast/logging/proto/proto_utils.h"
+
+using google::protobuf::RepeatedPtrField;
+using media::cast::proto::AggregatedFrameEvent;
+using media::cast::proto::AggregatedPacketEvent;
+using media::cast::proto::BasePacketEvent;
+using media::cast::proto::LogMetadata;
+
+namespace {
+
+// A size limit on maps to keep lookups fast.
+const size_t kMaxMapSize = 200;
+
+// The smallest (oredered by RTP timestamp) |kNumMapEntriesToTransfer| entries
+// will be moved when the map size reaches |kMaxMapSize|.
+// Must be smaller than |kMaxMapSize|.
+const size_t kNumMapEntriesToTransfer = 100;
+
+template <typename ProtoPtr>
+bool IsRtpTimestampLessThan(const ProtoPtr& lhs, const ProtoPtr& rhs) {
+ return lhs->relative_rtp_timestamp() < rhs->relative_rtp_timestamp();
+}
+
+BasePacketEvent* GetNewBasePacketEvent(AggregatedPacketEvent* event_proto,
+ int packet_id, int size) {
+ BasePacketEvent* base = event_proto->add_base_packet_event();
+ base->set_packet_id(packet_id);
+ base->set_size(size);
+ return base;
+}
+
+}
+
+namespace media {
+namespace cast {
+
+EncodingEventSubscriber::EncodingEventSubscriber(
+ EventMediaType event_media_type,
+ size_t max_frames)
+ : event_media_type_(event_media_type),
+ max_frames_(max_frames),
+ frame_event_storage_index_(0),
+ packet_event_storage_index_(0),
+ seen_first_rtp_timestamp_(false),
+ first_rtp_timestamp_(0u) {}
+
+EncodingEventSubscriber::~EncodingEventSubscriber() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void EncodingEventSubscriber::OnReceiveFrameEvent(
+ const FrameEvent& frame_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (event_media_type_ != frame_event.media_type)
+ return;
+
+ RtpTimestamp relative_rtp_timestamp =
+ GetRelativeRtpTimestamp(frame_event.rtp_timestamp);
+ FrameEventMap::iterator it = frame_event_map_.find(relative_rtp_timestamp);
+ linked_ptr<AggregatedFrameEvent> event_proto;
+
+ // Look up existing entry. If not found, create a new entry and add to map.
+ if (it == frame_event_map_.end()) {
+ event_proto.reset(new AggregatedFrameEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ frame_event_map_.insert(
+ std::make_pair(relative_rtp_timestamp, event_proto));
+ } else {
+ event_proto = it->second;
+ if (event_proto->event_type_size() >= kMaxEventsPerProto) {
+ DVLOG(2) << "Too many events in frame " << frame_event.rtp_timestamp
+ << ". Using new frame event proto.";
+ AddFrameEventToStorage(event_proto);
+ event_proto.reset(new AggregatedFrameEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ it->second = event_proto;
+ }
+ }
+
+ event_proto->add_event_type(ToProtoEventType(frame_event.type));
+ event_proto->add_event_timestamp_ms(
+ (frame_event.timestamp - base::TimeTicks()).InMilliseconds());
+
+ if (frame_event.type == FRAME_ENCODED) {
+ event_proto->set_encoded_frame_size(frame_event.size);
+ if (frame_event.media_type == VIDEO_EVENT) {
+ event_proto->set_encoded_frame_size(frame_event.size);
+ event_proto->set_key_frame(frame_event.key_frame);
+ event_proto->set_target_bitrate(frame_event.target_bitrate);
+ }
+ } else if (frame_event.type == FRAME_PLAYOUT) {
+ event_proto->set_delay_millis(frame_event.delay_delta.InMilliseconds());
+ }
+
+ if (frame_event_map_.size() > kMaxMapSize)
+ TransferFrameEvents(kNumMapEntriesToTransfer);
+
+ DCHECK(frame_event_map_.size() <= kMaxMapSize);
+ DCHECK(frame_event_storage_.size() <= max_frames_);
+}
+
+void EncodingEventSubscriber::OnReceivePacketEvent(
+ const PacketEvent& packet_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (event_media_type_ != packet_event.media_type)
+ return;
+
+ RtpTimestamp relative_rtp_timestamp =
+ GetRelativeRtpTimestamp(packet_event.rtp_timestamp);
+ PacketEventMap::iterator it =
+ packet_event_map_.find(relative_rtp_timestamp);
+ linked_ptr<AggregatedPacketEvent> event_proto;
+ BasePacketEvent* base_packet_event_proto = NULL;
+
+ // Look up existing entry. If not found, create a new entry and add to map.
+ if (it == packet_event_map_.end()) {
+ event_proto.reset(new AggregatedPacketEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ packet_event_map_.insert(
+ std::make_pair(relative_rtp_timestamp, event_proto));
+ base_packet_event_proto = GetNewBasePacketEvent(
+ event_proto.get(), packet_event.packet_id, packet_event.size);
+ } else {
+ // Found existing entry, now look up existing BasePacketEvent using packet
+ // ID. If not found, create a new entry and add to proto.
+ event_proto = it->second;
+ RepeatedPtrField<BasePacketEvent>* field =
+ event_proto->mutable_base_packet_event();
+ for (RepeatedPtrField<BasePacketEvent>::pointer_iterator base_it =
+ field->pointer_begin();
+ base_it != field->pointer_end();
+ ++base_it) {
+ if ((*base_it)->packet_id() == packet_event.packet_id) {
+ base_packet_event_proto = *base_it;
+ break;
+ }
+ }
+ if (!base_packet_event_proto) {
+ if (event_proto->base_packet_event_size() >= kMaxPacketsPerFrame) {
+ DVLOG(3) << "Too many packets in AggregatedPacketEvent "
+ << packet_event.rtp_timestamp << ". "
+ << "Using new packet event proto.";
+ AddPacketEventToStorage(event_proto);
+ event_proto.reset(new AggregatedPacketEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ it->second = event_proto;
+ }
+
+ base_packet_event_proto = GetNewBasePacketEvent(
+ event_proto.get(), packet_event.packet_id, packet_event.size);
+ } else if (base_packet_event_proto->event_type_size() >=
+ kMaxEventsPerProto) {
+ DVLOG(3) << "Too many events in packet "
+ << packet_event.rtp_timestamp << ", "
+ << packet_event.packet_id << ". Using new packet event proto.";
+ AddPacketEventToStorage(event_proto);
+ event_proto.reset(new AggregatedPacketEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ it->second = event_proto;
+ base_packet_event_proto = GetNewBasePacketEvent(
+ event_proto.get(), packet_event.packet_id, packet_event.size);
+ }
+ }
+
+ base_packet_event_proto->add_event_type(
+ ToProtoEventType(packet_event.type));
+ base_packet_event_proto->add_event_timestamp_ms(
+ (packet_event.timestamp - base::TimeTicks()).InMilliseconds());
+
+ // |base_packet_event_proto| could have been created with a receiver event
+ // which does not have the packet size and we would need to overwrite it when
+ // we see a sender event, which does have the packet size.
+ if (packet_event.size > 0) {
+ base_packet_event_proto->set_size(packet_event.size);
+ }
+
+ if (packet_event_map_.size() > kMaxMapSize)
+ TransferPacketEvents(kNumMapEntriesToTransfer);
+
+ DCHECK(packet_event_map_.size() <= kMaxMapSize);
+ DCHECK(packet_event_storage_.size() <= max_frames_);
+}
+
+void EncodingEventSubscriber::GetEventsAndReset(LogMetadata* metadata,
+ FrameEventList* frame_events, PacketEventList* packet_events) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Flush all events.
+ TransferFrameEvents(frame_event_map_.size());
+ TransferPacketEvents(packet_event_map_.size());
+ std::sort(frame_event_storage_.begin(), frame_event_storage_.end(),
+ &IsRtpTimestampLessThan<linked_ptr<AggregatedFrameEvent> >);
+ std::sort(packet_event_storage_.begin(), packet_event_storage_.end(),
+ &IsRtpTimestampLessThan<linked_ptr<AggregatedPacketEvent> >);
+
+ metadata->set_is_audio(event_media_type_ == AUDIO_EVENT);
+ metadata->set_first_rtp_timestamp(first_rtp_timestamp_);
+ metadata->set_num_frame_events(frame_event_storage_.size());
+ metadata->set_num_packet_events(packet_event_storage_.size());
+ metadata->set_reference_timestamp_ms_at_unix_epoch(
+ (base::TimeTicks::UnixEpoch() - base::TimeTicks()).InMilliseconds());
+ frame_events->swap(frame_event_storage_);
+ packet_events->swap(packet_event_storage_);
+ Reset();
+}
+
+void EncodingEventSubscriber::TransferFrameEvents(size_t max_num_entries) {
+ DCHECK(frame_event_map_.size() >= max_num_entries);
+
+ FrameEventMap::iterator it = frame_event_map_.begin();
+ for (size_t i = 0;
+ i < max_num_entries && it != frame_event_map_.end();
+ i++, ++it) {
+ AddFrameEventToStorage(it->second);
+ }
+
+ frame_event_map_.erase(frame_event_map_.begin(), it);
+}
+
+void EncodingEventSubscriber::TransferPacketEvents(size_t max_num_entries) {
+ PacketEventMap::iterator it = packet_event_map_.begin();
+ for (size_t i = 0;
+ i < max_num_entries && it != packet_event_map_.end();
+ i++, ++it) {
+ AddPacketEventToStorage(it->second);
+ }
+
+ packet_event_map_.erase(packet_event_map_.begin(), it);
+}
+
+void EncodingEventSubscriber::AddFrameEventToStorage(
+ const linked_ptr<AggregatedFrameEvent>& frame_event_proto) {
+ if (frame_event_storage_.size() >= max_frames_) {
+ frame_event_storage_[frame_event_storage_index_] = frame_event_proto;
+ } else {
+ frame_event_storage_.push_back(frame_event_proto);
+ }
+
+ frame_event_storage_index_ = (frame_event_storage_index_ + 1) % max_frames_;
+}
+
+void EncodingEventSubscriber::AddPacketEventToStorage(
+ const linked_ptr<AggregatedPacketEvent>& packet_event_proto) {
+ if (packet_event_storage_.size() >= max_frames_)
+ packet_event_storage_[packet_event_storage_index_] = packet_event_proto;
+ else
+ packet_event_storage_.push_back(packet_event_proto);
+
+ packet_event_storage_index_ = (packet_event_storage_index_ + 1) % max_frames_;
+}
+
+RtpTimestamp EncodingEventSubscriber::GetRelativeRtpTimestamp(
+ RtpTimestamp rtp_timestamp) {
+ if (!seen_first_rtp_timestamp_) {
+ seen_first_rtp_timestamp_ = true;
+ first_rtp_timestamp_ = rtp_timestamp;
+ }
+
+ return rtp_timestamp - first_rtp_timestamp_;
+}
+
+void EncodingEventSubscriber::Reset() {
+ frame_event_map_.clear();
+ frame_event_storage_.clear();
+ frame_event_storage_index_ = 0;
+ packet_event_map_.clear();
+ packet_event_storage_.clear();
+ packet_event_storage_index_ = 0;
+ seen_first_rtp_timestamp_ = false;
+ first_rtp_timestamp_ = 0u;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/encoding_event_subscriber.h b/chromium/media/cast/logging/encoding_event_subscriber.h
new file mode 100644
index 00000000000..ca2cccb5f74
--- /dev/null
+++ b/chromium/media/cast/logging/encoding_event_subscriber.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_LOGGING_ENCODING_EVENT_SUBSCRIBER_H_
+#define MEDIA_CAST_LOGGING_ENCODING_EVENT_SUBSCRIBER_H_
+
+#include <map>
+
+#include "base/memory/linked_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/proto/raw_events.pb.h"
+#include "media/cast/logging/raw_event_subscriber.h"
+
+namespace media {
+namespace cast {
+
+// Number of packets per frame recorded by the subscriber.
+// Once the max number of packets has been reached, a new aggregated proto
+// will be created.
+static const int kMaxPacketsPerFrame = 64;
+// Number of events per proto recorded by the subscriber.
+// Once the max number of events has been reached, a new aggregated proto
+// will be created.
+static const int kMaxEventsPerProto = 16;
+
+typedef std::vector<linked_ptr<media::cast::proto::AggregatedFrameEvent> >
+ FrameEventList;
+typedef std::vector<linked_ptr<media::cast::proto::AggregatedPacketEvent> >
+ PacketEventList;
+
+// A RawEventSubscriber implementation that subscribes to events,
+// encodes them in protocol buffer format, and aggregates them into a more
+// compact structure. Aggregation is per-frame, and uses a map with RTP
+// timestamp as key. Periodically, old entries in the map will be transferred
+// to a storage vector. This helps keep the size of the map small and
+// lookup times fast. The storage itself is a circular buffer that will
+// overwrite old entries once it has reached the size configured by user.
+class EncodingEventSubscriber : public RawEventSubscriber {
+ public:
+ // |event_media_type|: The subscriber will only process events that
+ // corresponds to this type.
+ // |max_frames|: How many events to keep in the frame / packet storage.
+ // This helps keep memory usage bounded.
+ // Every time one of |OnReceive[Frame,Packet]Event()| is
+ // called, it will check if the respective map size has exceeded |max_frames|.
+ // If so, it will remove the oldest aggregated entry (ordered by RTP
+ // timestamp).
+ EncodingEventSubscriber(EventMediaType event_media_type, size_t max_frames);
+
+ virtual ~EncodingEventSubscriber();
+
+ // RawReventSubscriber implementations.
+ virtual void OnReceiveFrameEvent(const FrameEvent& frame_event) OVERRIDE;
+ virtual void OnReceivePacketEvent(const PacketEvent& packet_event) OVERRIDE;
+
+ // Assigns frame events and packet events received so far to |frame_events|
+ // and |packet_events| and resets the internal state.
+ // In addition, assign metadata associated with these events to |metadata|.
+ // The protos in |frame_events| and |packets_events| are sorted in
+ // ascending RTP timestamp order.
+ void GetEventsAndReset(media::cast::proto::LogMetadata* metadata,
+ FrameEventList* frame_events,
+ PacketEventList* packet_events);
+
+ private:
+ typedef std::map<RtpTimestamp,
+ linked_ptr<media::cast::proto::AggregatedFrameEvent> >
+ FrameEventMap;
+ typedef std::map<RtpTimestamp,
+ linked_ptr<media::cast::proto::AggregatedPacketEvent> >
+ PacketEventMap;
+
+ // Transfer up to |max_num_entries| smallest entries from |frame_event_map_|
+ // to |frame_event_storage_|. This helps keep size of |frame_event_map_| small
+ // and lookup speed fast.
+ void TransferFrameEvents(size_t max_num_entries);
+ // See above.
+ void TransferPacketEvents(size_t max_num_entries);
+
+ void AddFrameEventToStorage(
+ const linked_ptr<media::cast::proto::AggregatedFrameEvent>&
+ frame_event_proto);
+ void AddPacketEventToStorage(
+ const linked_ptr<media::cast::proto::AggregatedPacketEvent>&
+ packet_event_proto);
+
+ // Returns the difference between |rtp_timestamp| and |first_rtp_timestamp_|.
+ // Sets |first_rtp_timestamp_| if it is not already set.
+ RtpTimestamp GetRelativeRtpTimestamp(RtpTimestamp rtp_timestamp);
+
+ // Clears the maps and first RTP timestamp seen.
+ void Reset();
+
+ const EventMediaType event_media_type_;
+ const size_t max_frames_;
+
+ FrameEventMap frame_event_map_;
+ FrameEventList frame_event_storage_;
+ int frame_event_storage_index_;
+
+ PacketEventMap packet_event_map_;
+ PacketEventList packet_event_storage_;
+ int packet_event_storage_index_;
+
+ // All functions must be called on the main thread.
+ base::ThreadChecker thread_checker_;
+
+ // Set to true on first event encountered after a |Reset()|.
+ bool seen_first_rtp_timestamp_;
+
+ // Set to RTP timestamp of first event encountered after a |Reset()|.
+ RtpTimestamp first_rtp_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncodingEventSubscriber);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_ENCODING_EVENT_SUBSCRIBER_H_
diff --git a/chromium/media/cast/logging/encoding_event_subscriber_unittest.cc b/chromium/media/cast/logging/encoding_event_subscriber_unittest.cc
new file mode 100644
index 00000000000..3d77a621b78
--- /dev/null
+++ b/chromium/media/cast/logging/encoding_event_subscriber_unittest.cc
@@ -0,0 +1,668 @@
+// 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/encoding_event_subscriber.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using media::cast::proto::AggregatedFrameEvent;
+using media::cast::proto::AggregatedPacketEvent;
+using media::cast::proto::BasePacketEvent;
+using media::cast::proto::LogMetadata;
+
+namespace {
+
+int64 InMilliseconds(base::TimeTicks event_time) {
+ return (event_time - base::TimeTicks()).InMilliseconds();
+}
+
+}
+
+namespace media {
+namespace cast {
+
+class EncodingEventSubscriberTest : public ::testing::Test {
+ protected:
+ EncodingEventSubscriberTest()
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)),
+ first_rtp_timestamp_(0) {}
+
+ void Init(EventMediaType event_media_type) {
+ DCHECK(!event_subscriber_);
+ event_subscriber_.reset(new EncodingEventSubscriber(event_media_type, 10));
+ cast_environment_->Logging()->AddRawEventSubscriber(
+ event_subscriber_.get());
+ }
+
+ virtual ~EncodingEventSubscriberTest() {
+ if (event_subscriber_) {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(
+ event_subscriber_.get());
+ }
+ }
+
+ void GetEventsAndReset() {
+ event_subscriber_->GetEventsAndReset(
+ &metadata_, &frame_events_, &packet_events_);
+ first_rtp_timestamp_ = metadata_.first_rtp_timestamp();
+ }
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ scoped_ptr<EncodingEventSubscriber> event_subscriber_;
+ FrameEventList frame_events_;
+ PacketEventList packet_events_;
+ LogMetadata metadata_;
+ RtpTimestamp first_rtp_timestamp_;
+};
+
+TEST_F(EncodingEventSubscriberTest, FrameEventTruncating) {
+ Init(VIDEO_EVENT);
+
+ base::TimeTicks now(testing_clock_->NowTicks());
+
+ // Entry with RTP timestamp 0 should get dropped.
+ for (int i = 0; i < 11; i++) {
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ i * 100,
+ /*frame_id*/ 0);
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_DECODED,
+ VIDEO_EVENT,
+ i * 100,
+ /*frame_id*/ 0);
+ }
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(10u, frame_events_.size());
+ EXPECT_EQ(100u, frame_events_.front()->relative_rtp_timestamp());
+ EXPECT_EQ(1000u, frame_events_.back()->relative_rtp_timestamp());
+}
+
+TEST_F(EncodingEventSubscriberTest, PacketEventTruncating) {
+ Init(AUDIO_EVENT);
+
+ base::TimeTicks now(testing_clock_->NowTicks());
+
+ // Entry with RTP timestamp 0 should get dropped.
+ for (int i = 0; i < 11; i++) {
+ cast_environment_->Logging()->InsertPacketEvent(now,
+ PACKET_RECEIVED,
+ AUDIO_EVENT,
+ /*rtp_timestamp*/ i * 100,
+ /*frame_id*/ 0,
+ /*packet_id*/ i,
+ /*max_packet_id*/ 10,
+ /*size*/ 123);
+ }
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(10u, packet_events_.size());
+ EXPECT_EQ(100u, packet_events_.front()->relative_rtp_timestamp());
+ EXPECT_EQ(1000u, packet_events_.back()->relative_rtp_timestamp());
+}
+
+TEST_F(EncodingEventSubscriberTest, EventFiltering) {
+ Init(VIDEO_EVENT);
+
+ base::TimeTicks now(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_DECODED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+
+ // This is an AUDIO_EVENT and shouldn't be processed by the subscriber.
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_DECODED,
+ AUDIO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, frame_events_.size());
+ FrameEventList::iterator it = frame_events_.begin();
+
+ linked_ptr<AggregatedFrameEvent> frame_event = *it;
+
+ ASSERT_EQ(1, frame_event->event_type_size());
+ EXPECT_EQ(media::cast::proto::FRAME_DECODED,
+ frame_event->event_type(0));
+
+ GetEventsAndReset();
+
+ EXPECT_TRUE(packet_events_.empty());
+}
+
+TEST_F(EncodingEventSubscriberTest, FrameEvent) {
+ Init(VIDEO_EVENT);
+ base::TimeTicks now(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ cast_environment_->Logging()->InsertFrameEvent(now, FRAME_DECODED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, frame_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp - first_rtp_timestamp_;
+ FrameEventList::iterator it = frame_events_.begin();
+
+ linked_ptr<AggregatedFrameEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->event_type_size());
+ EXPECT_EQ(media::cast::proto::FRAME_DECODED, event->event_type(0));
+ ASSERT_EQ(1, event->event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now), event->event_timestamp_ms(0));
+
+ EXPECT_EQ(0, event->encoded_frame_size());
+ EXPECT_EQ(0, event->delay_millis());
+
+ GetEventsAndReset();
+ EXPECT_TRUE(frame_events_.empty());
+}
+
+TEST_F(EncodingEventSubscriberTest, FrameEventDelay) {
+ Init(AUDIO_EVENT);
+ base::TimeTicks now(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ int delay_ms = 100;
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ now, FRAME_PLAYOUT, AUDIO_EVENT, rtp_timestamp,
+ /*frame_id*/ 0, base::TimeDelta::FromMilliseconds(delay_ms));
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, frame_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp - first_rtp_timestamp_;
+ FrameEventList::iterator it = frame_events_.begin();
+
+ linked_ptr<AggregatedFrameEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->event_type_size());
+ EXPECT_EQ(media::cast::proto::FRAME_PLAYOUT, event->event_type(0));
+ ASSERT_EQ(1, event->event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now), event->event_timestamp_ms(0));
+
+ EXPECT_EQ(0, event->encoded_frame_size());
+ EXPECT_EQ(100, event->delay_millis());
+ EXPECT_FALSE(event->has_key_frame());
+}
+
+TEST_F(EncodingEventSubscriberTest, FrameEventSize) {
+ Init(VIDEO_EVENT);
+ base::TimeTicks now(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ int size = 123;
+ bool key_frame = true;
+ int target_bitrate = 1024;
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ now, FRAME_ENCODED, VIDEO_EVENT, rtp_timestamp,
+ /*frame_id*/ 0, size, key_frame, target_bitrate);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, frame_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp - first_rtp_timestamp_;
+ FrameEventList::iterator it = frame_events_.begin();
+
+ linked_ptr<AggregatedFrameEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->event_type_size());
+ EXPECT_EQ(media::cast::proto::FRAME_ENCODED, event->event_type(0));
+ ASSERT_EQ(1, event->event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now), event->event_timestamp_ms(0));
+
+ EXPECT_EQ(size, event->encoded_frame_size());
+ EXPECT_EQ(0, event->delay_millis());
+ EXPECT_TRUE(event->has_key_frame());
+ EXPECT_EQ(key_frame, event->key_frame());
+ EXPECT_EQ(target_bitrate, event->target_bitrate());
+}
+
+TEST_F(EncodingEventSubscriberTest, MultipleFrameEvents) {
+ Init(AUDIO_EVENT);
+ RtpTimestamp rtp_timestamp1 = 100;
+ RtpTimestamp rtp_timestamp2 = 200;
+ base::TimeTicks now1(testing_clock_->NowTicks());
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ now1, FRAME_PLAYOUT, AUDIO_EVENT, rtp_timestamp1,
+ /*frame_id*/ 0, /*delay*/ base::TimeDelta::FromMilliseconds(100));
+
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(20));
+ base::TimeTicks now2(testing_clock_->NowTicks());
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ now2, FRAME_ENCODED, AUDIO_EVENT, rtp_timestamp2,
+ /*frame_id*/ 0, /*size*/ 123, /* key_frame - unused */ false,
+ /*target_bitrate - unused*/ 0);
+
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(20));
+ base::TimeTicks now3(testing_clock_->NowTicks());
+ cast_environment_->Logging()->InsertFrameEvent(
+ now3, FRAME_DECODED, AUDIO_EVENT, rtp_timestamp1, /*frame_id*/ 0);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(2u, frame_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp1 - first_rtp_timestamp_;
+ FrameEventList::iterator it = frame_events_.begin();
+
+ linked_ptr<AggregatedFrameEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(2, event->event_type_size());
+ EXPECT_EQ(media::cast::proto::FRAME_PLAYOUT, event->event_type(0));
+ EXPECT_EQ(media::cast::proto::FRAME_DECODED, event->event_type(1));
+
+ ASSERT_EQ(2, event->event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now1), event->event_timestamp_ms(0));
+ EXPECT_EQ(InMilliseconds(now3), event->event_timestamp_ms(1));
+
+ EXPECT_FALSE(event->has_key_frame());
+
+ relative_rtp_timestamp = rtp_timestamp2 - first_rtp_timestamp_;
+ ++it;
+
+ event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->event_type_size());
+ EXPECT_EQ(media::cast::proto::FRAME_ENCODED, event->event_type(0));
+
+ ASSERT_EQ(1, event->event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now2), event->event_timestamp_ms(0));
+
+ EXPECT_FALSE(event->has_key_frame());
+}
+
+TEST_F(EncodingEventSubscriberTest, PacketEvent) {
+ Init(AUDIO_EVENT);
+ base::TimeTicks now(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ int packet_id = 2;
+ int size = 100;
+ cast_environment_->Logging()->InsertPacketEvent(
+ now, PACKET_RECEIVED, AUDIO_EVENT,
+ rtp_timestamp, /*frame_id*/ 0, packet_id,
+ /*max_packet_id*/ 10, size);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, packet_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp - first_rtp_timestamp_;
+ PacketEventList::iterator it = packet_events_.begin();
+
+ linked_ptr<AggregatedPacketEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->base_packet_event_size());
+ const BasePacketEvent& base_event = event->base_packet_event(0);
+ EXPECT_EQ(packet_id, base_event.packet_id());
+ ASSERT_EQ(1, base_event.event_type_size());
+ EXPECT_EQ(media::cast::proto::PACKET_RECEIVED,
+ base_event.event_type(0));
+ ASSERT_EQ(1, base_event.event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now), base_event.event_timestamp_ms(0));
+ EXPECT_EQ(size, base_event.size());
+
+ GetEventsAndReset();
+ EXPECT_TRUE(packet_events_.empty());
+}
+
+TEST_F(EncodingEventSubscriberTest, MultiplePacketEventsForPacket) {
+ Init(VIDEO_EVENT);
+ base::TimeTicks now1(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ int packet_id = 2;
+ int size = 100;
+ cast_environment_->Logging()->InsertPacketEvent(now1,
+ PACKET_SENT_TO_NETWORK,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0,
+ packet_id,
+ /*max_packet_id*/ 10,
+ size);
+
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(20));
+ base::TimeTicks now2(testing_clock_->NowTicks());
+ cast_environment_->Logging()->InsertPacketEvent(now2,
+ PACKET_RETRANSMITTED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0,
+ packet_id,
+ /*max_packet_id*/ 10,
+ size);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, packet_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp - first_rtp_timestamp_;
+ PacketEventList::iterator it = packet_events_.begin();
+
+ linked_ptr<AggregatedPacketEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->base_packet_event_size());
+ const BasePacketEvent& base_event = event->base_packet_event(0);
+ EXPECT_EQ(packet_id, base_event.packet_id());
+ ASSERT_EQ(2, base_event.event_type_size());
+ EXPECT_EQ(media::cast::proto::PACKET_SENT_TO_NETWORK,
+ base_event.event_type(0));
+ EXPECT_EQ(media::cast::proto::PACKET_RETRANSMITTED,
+ base_event.event_type(1));
+ ASSERT_EQ(2, base_event.event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now1), base_event.event_timestamp_ms(0));
+ EXPECT_EQ(InMilliseconds(now2), base_event.event_timestamp_ms(1));
+}
+
+TEST_F(EncodingEventSubscriberTest, MultiplePacketEventsForFrame) {
+ Init(VIDEO_EVENT);
+ base::TimeTicks now1(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp = 100;
+ int packet_id_1 = 2;
+ int packet_id_2 = 3;
+ int size = 100;
+ cast_environment_->Logging()->InsertPacketEvent(now1,
+ PACKET_SENT_TO_NETWORK,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0,
+ packet_id_1,
+ /*max_packet_id*/ 10,
+ size);
+
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(20));
+ base::TimeTicks now2(testing_clock_->NowTicks());
+ cast_environment_->Logging()->InsertPacketEvent(now2,
+ PACKET_RETRANSMITTED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0,
+ packet_id_2,
+ /*max_packet_id*/ 10,
+ size);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(1u, packet_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp - first_rtp_timestamp_;
+ PacketEventList::iterator it = packet_events_.begin();
+
+ linked_ptr<AggregatedPacketEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(2, event->base_packet_event_size());
+ const BasePacketEvent& base_event = event->base_packet_event(0);
+ EXPECT_EQ(packet_id_1, base_event.packet_id());
+ ASSERT_EQ(1, base_event.event_type_size());
+ EXPECT_EQ(media::cast::proto::PACKET_SENT_TO_NETWORK,
+ base_event.event_type(0));
+ ASSERT_EQ(1, base_event.event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now1), base_event.event_timestamp_ms(0));
+
+ const BasePacketEvent& base_event_2 = event->base_packet_event(1);
+ EXPECT_EQ(packet_id_2, base_event_2.packet_id());
+ ASSERT_EQ(1, base_event_2.event_type_size());
+ EXPECT_EQ(media::cast::proto::PACKET_RETRANSMITTED,
+ base_event_2.event_type(0));
+ ASSERT_EQ(1, base_event_2.event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now2), base_event_2.event_timestamp_ms(0));
+}
+
+TEST_F(EncodingEventSubscriberTest, MultiplePacketEvents) {
+ Init(VIDEO_EVENT);
+ base::TimeTicks now1(testing_clock_->NowTicks());
+ RtpTimestamp rtp_timestamp_1 = 100;
+ RtpTimestamp rtp_timestamp_2 = 200;
+ int packet_id_1 = 2;
+ int packet_id_2 = 3;
+ int size = 100;
+ cast_environment_->Logging()->InsertPacketEvent(now1,
+ PACKET_SENT_TO_NETWORK,
+ VIDEO_EVENT,
+ rtp_timestamp_1,
+ /*frame_id*/ 0,
+ packet_id_1,
+ /*max_packet_id*/ 10,
+ size);
+
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(20));
+ base::TimeTicks now2(testing_clock_->NowTicks());
+ cast_environment_->Logging()->InsertPacketEvent(now2,
+ PACKET_RETRANSMITTED,
+ VIDEO_EVENT,
+ rtp_timestamp_2,
+ /*frame_id*/ 0,
+ packet_id_2,
+ /*max_packet_id*/ 10,
+ size);
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(2u, packet_events_.size());
+
+ RtpTimestamp relative_rtp_timestamp = rtp_timestamp_1 - first_rtp_timestamp_;
+ PacketEventList::iterator it = packet_events_.begin();
+
+ linked_ptr<AggregatedPacketEvent> event = *it;
+
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->base_packet_event_size());
+ const BasePacketEvent& base_event = event->base_packet_event(0);
+ EXPECT_EQ(packet_id_1, base_event.packet_id());
+ ASSERT_EQ(1, base_event.event_type_size());
+ EXPECT_EQ(media::cast::proto::PACKET_SENT_TO_NETWORK,
+ base_event.event_type(0));
+ ASSERT_EQ(1, base_event.event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now1), base_event.event_timestamp_ms(0));
+
+ relative_rtp_timestamp = rtp_timestamp_2 - first_rtp_timestamp_;
+ ++it;
+ ASSERT_TRUE(it != packet_events_.end());
+
+ event = *it;
+ EXPECT_EQ(relative_rtp_timestamp, event->relative_rtp_timestamp());
+
+ ASSERT_EQ(1, event->base_packet_event_size());
+ const BasePacketEvent& base_event_2 = event->base_packet_event(0);
+ EXPECT_EQ(packet_id_2, base_event_2.packet_id());
+ ASSERT_EQ(1, base_event_2.event_type_size());
+ EXPECT_EQ(media::cast::proto::PACKET_RETRANSMITTED,
+ base_event_2.event_type(0));
+ ASSERT_EQ(1, base_event_2.event_timestamp_ms_size());
+ EXPECT_EQ(InMilliseconds(now2), base_event_2.event_timestamp_ms(0));
+}
+
+TEST_F(EncodingEventSubscriberTest, FirstRtpTimestamp) {
+ Init(VIDEO_EVENT);
+ RtpTimestamp rtp_timestamp = 12345;
+ base::TimeTicks now(testing_clock_->NowTicks());
+
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_CAPTURE_END,
+ VIDEO_EVENT,
+ rtp_timestamp + 30,
+ /*frame_id*/ 1);
+
+ GetEventsAndReset();
+
+ EXPECT_EQ(rtp_timestamp, first_rtp_timestamp_);
+ FrameEventList::iterator it = frame_events_.begin();
+ ASSERT_NE(frame_events_.end(), it);
+ EXPECT_EQ(0u, (*it)->relative_rtp_timestamp());
+
+ ++it;
+ ASSERT_NE(frame_events_.end(), it);
+ EXPECT_EQ(30u, (*it)->relative_rtp_timestamp());
+
+ rtp_timestamp = 67890;
+
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+ GetEventsAndReset();
+
+ EXPECT_EQ(rtp_timestamp, first_rtp_timestamp_);
+}
+
+TEST_F(EncodingEventSubscriberTest, RelativeRtpTimestampWrapAround) {
+ Init(VIDEO_EVENT);
+ RtpTimestamp rtp_timestamp = 0xffffffff - 20;
+ base::TimeTicks now(testing_clock_->NowTicks());
+
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+
+ // RtpTimestamp has now wrapped around.
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_CAPTURE_END,
+ VIDEO_EVENT,
+ rtp_timestamp + 30,
+ /*frame_id*/ 1);
+
+ GetEventsAndReset();
+
+ FrameEventList::iterator it = frame_events_.begin();
+ ASSERT_NE(frame_events_.end(), it);
+ EXPECT_EQ(0u, (*it)->relative_rtp_timestamp());
+
+ ++it;
+ ASSERT_NE(frame_events_.end(), it);
+ EXPECT_EQ(30u, (*it)->relative_rtp_timestamp());
+}
+
+TEST_F(EncodingEventSubscriberTest, MaxEventsPerProto) {
+ Init(VIDEO_EVENT);
+ RtpTimestamp rtp_timestamp = 100;
+ for (int i = 0; i < kMaxEventsPerProto + 1; i++) {
+ cast_environment_->Logging()->InsertFrameEvent(testing_clock_->NowTicks(),
+ FRAME_ACK_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0);
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
+ }
+
+ GetEventsAndReset();
+
+ ASSERT_EQ(2u, frame_events_.size());
+ FrameEventList::iterator frame_it = frame_events_.begin();
+ ASSERT_TRUE(frame_it != frame_events_.end());
+
+ linked_ptr<AggregatedFrameEvent> frame_event = *frame_it;
+
+ EXPECT_EQ(kMaxEventsPerProto, frame_event->event_type_size());
+
+ for (int i = 0; i < kMaxPacketsPerFrame + 1; i++) {
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(),
+ PACKET_SENT_TO_NETWORK,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0,
+ i,
+ kMaxPacketsPerFrame,
+ 123);
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
+ }
+
+ GetEventsAndReset();
+
+ EXPECT_EQ(2u, packet_events_.size());
+
+ PacketEventList::iterator packet_it = packet_events_.begin();
+ ASSERT_TRUE(packet_it != packet_events_.end());
+
+ linked_ptr<AggregatedPacketEvent> packet_event = *packet_it;
+
+ EXPECT_EQ(kMaxPacketsPerFrame,
+ packet_event->base_packet_event_size());
+
+ ++packet_it;
+ packet_event = *packet_it;
+ EXPECT_EQ(1, packet_event->base_packet_event_size());
+
+ for (int j = 0; j < kMaxEventsPerProto + 1; j++) {
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(),
+ PACKET_SENT_TO_NETWORK,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ /*frame_id*/ 0,
+ 0,
+ 0,
+ 123);
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30));
+ }
+
+ GetEventsAndReset();
+
+ EXPECT_EQ(2u, packet_events_.size());
+ packet_it = packet_events_.begin();
+ ASSERT_TRUE(packet_it != packet_events_.end());
+
+ packet_event = *packet_it;
+
+ EXPECT_EQ(kMaxEventsPerProto,
+ packet_event->base_packet_event(0).event_type_size());
+
+ ++packet_it;
+ packet_event = *packet_it;
+ EXPECT_EQ(1, packet_event->base_packet_event(0).event_type_size());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/log_deserializer.cc b/chromium/media/cast/logging/log_deserializer.cc
new file mode 100644
index 00000000000..1c6dd572240
--- /dev/null
+++ b/chromium/media/cast/logging/log_deserializer.cc
@@ -0,0 +1,252 @@
+// 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/logging/log_deserializer.h"
+
+#include <map>
+#include <utility>
+
+#include "base/big_endian.h"
+#include "base/memory/scoped_ptr.h"
+#include "third_party/zlib/zlib.h"
+
+using media::cast::FrameEventMap;
+using media::cast::PacketEventMap;
+using media::cast::RtpTimestamp;
+using media::cast::proto::AggregatedFrameEvent;
+using media::cast::proto::AggregatedPacketEvent;
+using media::cast::proto::BasePacketEvent;
+using media::cast::proto::LogMetadata;
+
+namespace {
+
+// Use 60MB of temp buffer to hold uncompressed data if |compress| is true.
+// This is double the size of temp buffer used during compression (30MB)
+// since the there are two streams in the blob.
+// Keep in sync with media/cast/logging/log_serializer.cc.
+const int kMaxUncompressedBytes = 60 * 1000 * 1000;
+
+void MergePacketEvent(const AggregatedPacketEvent& from,
+ linked_ptr<AggregatedPacketEvent> to) {
+ for (int i = 0; i < from.base_packet_event_size(); i++) {
+ const BasePacketEvent& from_base_event = from.base_packet_event(i);
+ bool merged = false;
+ for (int j = 0; j < to->base_packet_event_size(); j++) {
+ BasePacketEvent* to_base_event = to->mutable_base_packet_event(j);
+ if (from_base_event.packet_id() == to_base_event->packet_id()) {
+ int packet_size = std::max(
+ from_base_event.size(), to_base_event->size());
+ // Need special merge logic here because we need to prevent a valid
+ // packet size (> 0) from being overwritten with an invalid one (= 0).
+ to_base_event->MergeFrom(from_base_event);
+ to_base_event->set_size(packet_size);
+ merged = true;
+ break;
+ }
+ }
+ if (!merged) {
+ BasePacketEvent* to_base_event = to->add_base_packet_event();
+ to_base_event->CopyFrom(from_base_event);
+ }
+ }
+}
+
+void MergeFrameEvent(const AggregatedFrameEvent& from,
+ linked_ptr<AggregatedFrameEvent> to) {
+ to->mutable_event_type()->MergeFrom(from.event_type());
+ to->mutable_event_timestamp_ms()->MergeFrom(from.event_timestamp_ms());
+ if (!to->has_encoded_frame_size() && from.has_encoded_frame_size())
+ to->set_encoded_frame_size(from.encoded_frame_size());
+ if (!to->has_delay_millis() && from.has_delay_millis())
+ to->set_delay_millis(from.delay_millis());
+ if (!to->has_key_frame() && from.has_key_frame())
+ to->set_key_frame(from.key_frame());
+ if (!to->has_target_bitrate() && from.has_target_bitrate())
+ to->set_target_bitrate(from.target_bitrate());
+}
+
+bool PopulateDeserializedLog(base::BigEndianReader* reader,
+ media::cast::DeserializedLog* log) {
+ FrameEventMap frame_event_map;
+ PacketEventMap packet_event_map;
+
+ int num_frame_events = log->metadata.num_frame_events();
+ RtpTimestamp relative_rtp_timestamp = 0;
+ uint16 proto_size = 0;
+ for (int i = 0; i < num_frame_events; i++) {
+ if (!reader->ReadU16(&proto_size))
+ return false;
+
+ linked_ptr<AggregatedFrameEvent> frame_event(new AggregatedFrameEvent);
+ if (!frame_event->ParseFromArray(reader->ptr(), proto_size))
+ return false;
+ if (!reader->Skip(proto_size))
+ return false;
+
+ // During serialization the RTP timestamp in proto is relative to previous
+ // frame.
+ // Adjust RTP timestamp back to value relative to first RTP timestamp.
+ frame_event->set_relative_rtp_timestamp(
+ frame_event->relative_rtp_timestamp() + relative_rtp_timestamp);
+ relative_rtp_timestamp = frame_event->relative_rtp_timestamp();
+
+ FrameEventMap::iterator it = frame_event_map.find(
+ frame_event->relative_rtp_timestamp());
+ if (it == frame_event_map.end()) {
+ frame_event_map.insert(
+ std::make_pair(frame_event->relative_rtp_timestamp(), frame_event));
+ } else {
+ // Events for the same frame might have been split into more than one
+ // proto. Merge them.
+ MergeFrameEvent(*frame_event, it->second);
+ }
+ }
+
+ log->frame_events.swap(frame_event_map);
+
+ int num_packet_events = log->metadata.num_packet_events();
+ relative_rtp_timestamp = 0;
+ for (int i = 0; i < num_packet_events; i++) {
+ if (!reader->ReadU16(&proto_size))
+ return false;
+
+ linked_ptr<AggregatedPacketEvent> packet_event(new AggregatedPacketEvent);
+ if (!packet_event->ParseFromArray(reader->ptr(), proto_size))
+ return false;
+ if (!reader->Skip(proto_size))
+ return false;
+
+ packet_event->set_relative_rtp_timestamp(
+ packet_event->relative_rtp_timestamp() + relative_rtp_timestamp);
+ relative_rtp_timestamp = packet_event->relative_rtp_timestamp();
+
+ PacketEventMap::iterator it = packet_event_map.find(
+ packet_event->relative_rtp_timestamp());
+ if (it == packet_event_map.end()) {
+ packet_event_map.insert(
+ std::make_pair(packet_event->relative_rtp_timestamp(), packet_event));
+ } else {
+ // Events for the same frame might have been split into more than one
+ // proto. Merge them.
+ MergePacketEvent(*packet_event, it->second);
+ }
+ }
+
+ log->packet_events.swap(packet_event_map);
+
+ return true;
+}
+
+bool DoDeserializeEvents(const char* data,
+ int data_bytes,
+ media::cast::DeserializedLog* audio_log,
+ media::cast::DeserializedLog* video_log) {
+ bool got_audio = false;
+ bool got_video = false;
+ base::BigEndianReader reader(data, data_bytes);
+
+ LogMetadata metadata;
+ uint16 proto_size = 0;
+ while (reader.remaining() > 0) {
+ if (!reader.ReadU16(&proto_size))
+ return false;
+ if (!metadata.ParseFromArray(reader.ptr(), proto_size))
+ return false;
+ reader.Skip(proto_size);
+
+ if (metadata.is_audio()) {
+ if (got_audio) {
+ VLOG(1) << "Got audio data twice.";
+ return false;
+ }
+
+ got_audio = true;
+ audio_log->metadata = metadata;
+ if (!PopulateDeserializedLog(&reader, audio_log))
+ return false;
+ } else {
+ if (got_video) {
+ VLOG(1) << "Got duplicate video log.";
+ return false;
+ }
+
+ got_video = true;
+ video_log->metadata = metadata;
+ if (!PopulateDeserializedLog(&reader, video_log))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Uncompress(const char* data,
+ int data_bytes,
+ int max_uncompressed_bytes,
+ char* uncompressed,
+ int* uncompressed_bytes) {
+ z_stream stream = {0};
+
+ stream.next_in = reinterpret_cast<uint8*>(const_cast<char*>(data));
+ stream.avail_in = data_bytes;
+ stream.next_out = reinterpret_cast<uint8*>(uncompressed);
+ stream.avail_out = max_uncompressed_bytes;
+
+ bool success = false;
+ while (stream.avail_in > 0 && stream.avail_out > 0) {
+ // 16 is added to read in gzip format.
+ int result = inflateInit2(&stream, MAX_WBITS + 16);
+ DCHECK_EQ(Z_OK, result);
+
+ result = inflate(&stream, Z_FINISH);
+ success = (result == Z_STREAM_END);
+ if (!success) {
+ DVLOG(2) << "inflate() failed. Result: " << result;
+ break;
+ }
+
+ result = inflateEnd(&stream);
+ DCHECK(result == Z_OK);
+ }
+
+ if (stream.avail_in == 0) {
+ success = true;
+ *uncompressed_bytes = max_uncompressed_bytes - stream.avail_out;
+ }
+ return success;
+}
+
+} // namespace
+
+namespace media {
+namespace cast {
+
+bool DeserializeEvents(const char* data,
+ int data_bytes,
+ bool compressed,
+ DeserializedLog* audio_log,
+ DeserializedLog* video_log) {
+ DCHECK_GT(data_bytes, 0);
+
+ if (compressed) {
+ scoped_ptr<char[]> uncompressed(new char[kMaxUncompressedBytes]);
+ int uncompressed_bytes = 0;
+ if (!Uncompress(data,
+ data_bytes,
+ kMaxUncompressedBytes,
+ uncompressed.get(),
+ &uncompressed_bytes))
+ return false;
+
+ return DoDeserializeEvents(
+ uncompressed.get(), uncompressed_bytes, audio_log, video_log);
+ } else {
+ return DoDeserializeEvents(data, data_bytes, audio_log, video_log);
+ }
+}
+
+DeserializedLog::DeserializedLog() {}
+DeserializedLog::~DeserializedLog() {}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/log_deserializer.h b/chromium/media/cast/logging/log_deserializer.h
new file mode 100644
index 00000000000..01b6db7dd12
--- /dev/null
+++ b/chromium/media/cast/logging/log_deserializer.h
@@ -0,0 +1,51 @@
+// 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_LOGGING_LOG_DESERIALIZER_H_
+#define MEDIA_CAST_LOGGING_LOG_DESERIALIZER_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/linked_ptr.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/proto/raw_events.pb.h"
+
+namespace media {
+namespace cast {
+
+typedef std::map<RtpTimestamp,
+ linked_ptr<media::cast::proto::AggregatedFrameEvent> >
+ FrameEventMap;
+typedef std::map<RtpTimestamp,
+ linked_ptr<media::cast::proto::AggregatedPacketEvent> >
+ PacketEventMap;
+
+// Represents deserialized raw event logs for a particular stream.
+struct DeserializedLog {
+ DeserializedLog();
+ ~DeserializedLog();
+ proto::LogMetadata metadata;
+ FrameEventMap frame_events;
+ PacketEventMap packet_events;
+};
+
+// This function takes the output of LogSerializer and deserializes it into
+// its original format. Returns true if deserialization is successful. All
+// output arguments are valid if this function returns true.
+// |data|: Serialized event logs with length |data_bytes|.
+// |compressed|: true if |data| is compressed in gzip format.
+// |log_metadata|: This will be populated with deserialized LogMetadata proto.
+// |audio_log|, |video_log|: These will be populated with deserialized
+// log data for audio and video streams, respectively.
+bool DeserializeEvents(const char* data,
+ int data_bytes,
+ bool compressed,
+ DeserializedLog* audio_log,
+ DeserializedLog* video_log);
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_LOG_DESERIALIZER_H_
diff --git a/chromium/media/cast/logging/log_serializer.cc b/chromium/media/cast/logging/log_serializer.cc
new file mode 100644
index 00000000000..afcf77013f3
--- /dev/null
+++ b/chromium/media/cast/logging/log_serializer.cc
@@ -0,0 +1,190 @@
+// 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.
+//
+// The serialization format is as follows:
+// 16-bit integer describing the following LogMetadata proto size in bytes.
+// The LogMetadata proto.
+// 32-bit integer describing number of frame events.
+// (The following repeated for number of frame events):
+// 16-bit integer describing the following AggregatedFrameEvent proto size
+// in bytes.
+// The AggregatedFrameEvent proto.
+// 32-bit integer describing number of packet events.
+// (The following repeated for number of packet events):
+// 16-bit integer describing the following AggregatedPacketEvent proto
+// size in bytes.
+// The AggregatedPacketEvent proto.
+
+#include "media/cast/logging/log_serializer.h"
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "third_party/zlib/zlib.h"
+
+namespace media {
+namespace cast {
+
+namespace {
+
+using media::cast::proto::AggregatedFrameEvent;
+using media::cast::proto::AggregatedPacketEvent;
+using media::cast::proto::LogMetadata;
+
+// Use 30MB of temp buffer to hold uncompressed data if |compress| is true.
+const int kMaxUncompressedBytes = 30 * 1000 * 1000;
+
+// The maximum allowed size per serialized proto.
+const int kMaxSerializedProtoBytes = (1 << 16) - 1;
+bool DoSerializeEvents(const LogMetadata& metadata,
+ const FrameEventList& frame_events,
+ const PacketEventList& packet_events,
+ const int max_output_bytes,
+ char* output,
+ int* output_bytes) {
+ base::BigEndianWriter writer(output, max_output_bytes);
+
+ int proto_size = metadata.ByteSize();
+ DCHECK(proto_size <= kMaxSerializedProtoBytes);
+ if (!writer.WriteU16(proto_size))
+ return false;
+ if (!metadata.SerializeToArray(writer.ptr(), writer.remaining()))
+ return false;
+ if (!writer.Skip(proto_size))
+ return false;
+
+ RtpTimestamp prev_rtp_timestamp = 0;
+ for (media::cast::FrameEventList::const_iterator it = frame_events.begin();
+ it != frame_events.end();
+ ++it) {
+ media::cast::proto::AggregatedFrameEvent frame_event(**it);
+
+ // Adjust relative RTP timestamp so that it is relative to previous frame,
+ // rather than relative to first RTP timestamp.
+ // This is done to improve encoding size.
+ RtpTimestamp old_relative_rtp_timestamp =
+ frame_event.relative_rtp_timestamp();
+ frame_event.set_relative_rtp_timestamp(
+ old_relative_rtp_timestamp - prev_rtp_timestamp);
+ prev_rtp_timestamp = old_relative_rtp_timestamp;
+
+ proto_size = frame_event.ByteSize();
+ DCHECK(proto_size <= kMaxSerializedProtoBytes);
+
+ // Write size of the proto, then write the proto.
+ if (!writer.WriteU16(proto_size))
+ return false;
+ if (!frame_event.SerializeToArray(writer.ptr(), writer.remaining()))
+ return false;
+ if (!writer.Skip(proto_size))
+ return false;
+ }
+
+ // Write packet events.
+ prev_rtp_timestamp = 0;
+ for (media::cast::PacketEventList::const_iterator it = packet_events.begin();
+ it != packet_events.end();
+ ++it) {
+ media::cast::proto::AggregatedPacketEvent packet_event(**it);
+ RtpTimestamp old_relative_rtp_timestamp =
+ packet_event.relative_rtp_timestamp();
+ packet_event.set_relative_rtp_timestamp(
+ old_relative_rtp_timestamp - prev_rtp_timestamp);
+ prev_rtp_timestamp = old_relative_rtp_timestamp;
+
+ proto_size = packet_event.ByteSize();
+ DCHECK(proto_size <= kMaxSerializedProtoBytes);
+
+ // Write size of the proto, then write the proto.
+ if (!writer.WriteU16(proto_size))
+ return false;
+ if (!packet_event.SerializeToArray(writer.ptr(), writer.remaining()))
+ return false;
+ if (!writer.Skip(proto_size))
+ return false;
+ }
+
+ *output_bytes = max_output_bytes - writer.remaining();
+ return true;
+}
+
+bool Compress(char* uncompressed_buffer,
+ int uncompressed_bytes,
+ int max_output_bytes,
+ char* output,
+ int* output_bytes) {
+ z_stream stream = {0};
+ int result = deflateInit2(&stream,
+ Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ // 16 is added to produce a gzip header + trailer.
+ MAX_WBITS + 16,
+ 8, // memLevel = 8 is default.
+ Z_DEFAULT_STRATEGY);
+ DCHECK_EQ(Z_OK, result);
+
+ stream.next_in = reinterpret_cast<uint8*>(uncompressed_buffer);
+ stream.avail_in = uncompressed_bytes;
+ stream.next_out = reinterpret_cast<uint8*>(output);
+ stream.avail_out = max_output_bytes;
+
+ // Do a one-shot compression. This will return Z_STREAM_END only if |output|
+ // is large enough to hold all compressed data.
+ result = deflate(&stream, Z_FINISH);
+ bool success = (result == Z_STREAM_END);
+
+ if (!success)
+ DVLOG(2) << "deflate() failed. Result: " << result;
+
+ result = deflateEnd(&stream);
+ DCHECK(result == Z_OK || result == Z_DATA_ERROR);
+
+ if (success)
+ *output_bytes = max_output_bytes - stream.avail_out;
+
+ return success;
+}
+
+} // namespace
+
+bool SerializeEvents(const LogMetadata& log_metadata,
+ const FrameEventList& frame_events,
+ const PacketEventList& packet_events,
+ bool compress,
+ int max_output_bytes,
+ char* output,
+ int* output_bytes) {
+ DCHECK_GT(max_output_bytes, 0);
+ DCHECK(output);
+ DCHECK(output_bytes);
+
+ if (compress) {
+ // Allocate a reasonably large temp buffer to hold uncompressed data.
+ scoped_ptr<char[]> uncompressed_buffer(new char[kMaxUncompressedBytes]);
+ int uncompressed_bytes;
+ bool success = DoSerializeEvents(log_metadata,
+ frame_events,
+ packet_events,
+ kMaxUncompressedBytes,
+ uncompressed_buffer.get(),
+ &uncompressed_bytes);
+ if (!success)
+ return false;
+ return Compress(uncompressed_buffer.get(),
+ uncompressed_bytes,
+ max_output_bytes,
+ output,
+ output_bytes);
+ } else {
+ return DoSerializeEvents(log_metadata,
+ frame_events,
+ packet_events,
+ max_output_bytes,
+ output,
+ output_bytes);
+ }
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/log_serializer.h b/chromium/media/cast/logging/log_serializer.h
new file mode 100644
index 00000000000..8aff54fc95d
--- /dev/null
+++ b/chromium/media/cast/logging/log_serializer.h
@@ -0,0 +1,37 @@
+// 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_LOGGING_LOG_SERIALIZER_H_
+#define MEDIA_CAST_LOGGING_LOG_SERIALIZER_H_
+
+#include <string>
+
+#include "media/cast/logging/encoding_event_subscriber.h"
+
+namespace media {
+namespace cast {
+
+// Serialize |frame_events|, |packet_events|, |log_metadata|
+// returned from EncodingEventSubscriber.
+// Result is written to |output|, which can hold |max_output_bytes| of data.
+// If |compress| is true, |output| will be set with data compresssed in
+// gzip format.
+// |output_bytes| will be set to number of bytes written.
+//
+// Returns |true| if serialization is successful. This function
+// returns |false| if the serialized string will exceed |max_output_bytes|.
+//
+// See .cc file for format specification.
+bool SerializeEvents(const media::cast::proto::LogMetadata& log_metadata,
+ const FrameEventList& frame_events,
+ const PacketEventList& packet_events,
+ bool compress,
+ int max_output_bytes,
+ char* output,
+ int* output_bytes);
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_LOG_SERIALIZER_H_
diff --git a/chromium/media/cast/logging/logging_defines.cc b/chromium/media/cast/logging/logging_defines.cc
index 85abe7c5d45..05ceeb9521f 100644
--- a/chromium/media/cast/logging/logging_defines.cc
+++ b/chromium/media/cast/logging/logging_defines.cc
@@ -6,96 +6,46 @@
#include "base/logging.h"
+#define ENUM_TO_STRING(enum) \
+ case enum: \
+ return #enum
+
namespace media {
namespace cast {
-CastLoggingConfig::CastLoggingConfig()
- : enable_data_collection(false),
- enable_uma_stats(false),
- enable_tracing(false) {}
-
-CastLoggingConfig::~CastLoggingConfig() {}
-
-CastLoggingConfig GetDefaultCastLoggingConfig() {
- CastLoggingConfig config;
- return config;
-}
-
-std::string CastLoggingToString(CastLoggingEvent event) {
+const char* CastLoggingToString(CastLoggingEvent event) {
switch (event) {
- case(kUnknown):
- // Can happen if the sender and receiver of RTCP log messages are not
- // aligned.
- return "Unknown";
- case(kRttMs):
- return "RttMs";
- case(kPacketLoss):
- return "PacketLoss";
- case(kJitterMs):
- return "JitterMs";
- case(kAckReceived):
- return "AckReceived";
- case(kRembBitrate):
- return "RembBitrate";
- case(kAckSent):
- return "AckSent";
- case(kLastEvent):
- return "LastEvent";
- case(kAudioFrameReceived):
- return "AudioFrameReceived";
- case(kAudioFrameCaptured):
- return "AudioFrameCaptured";
- case(kAudioFrameEncoded):
- return "AudioFrameEncoded";
- case(kAudioPlayoutDelay):
- return "AudioPlayoutDelay";
- case(kAudioFrameDecoded):
- return "AudioFrameDecoded";
- case(kVideoFrameCaptured):
- return "VideoFrameCaptured";
- case(kVideoFrameReceived):
- return "VideoFrameReceived";
- case(kVideoFrameSentToEncoder):
- return "VideoFrameSentToEncoder";
- case(kVideoFrameEncoded):
- return "VideoFrameEncoded";
- case(kVideoFrameDecoded):
- return "VideoFrameDecoded";
- case(kVideoRenderDelay):
- return "VideoRenderDelay";
- case(kPacketSentToPacer):
- return "PacketSentToPacer";
- case(kPacketSentToNetwork):
- return "PacketSentToNetwork";
- case(kPacketRetransmited):
- return "PacketRetransmited";
- case(kPacketReceived):
- return "PacketReceived";
- default:
- NOTREACHED();
- return "";
+ ENUM_TO_STRING(UNKNOWN);
+ ENUM_TO_STRING(FRAME_CAPTURE_BEGIN);
+ ENUM_TO_STRING(FRAME_CAPTURE_END);
+ ENUM_TO_STRING(FRAME_ENCODED);
+ ENUM_TO_STRING(FRAME_ACK_RECEIVED);
+ ENUM_TO_STRING(FRAME_ACK_SENT);
+ ENUM_TO_STRING(FRAME_DECODED);
+ ENUM_TO_STRING(FRAME_PLAYOUT);
+ ENUM_TO_STRING(PACKET_SENT_TO_NETWORK);
+ ENUM_TO_STRING(PACKET_RETRANSMITTED);
+ ENUM_TO_STRING(PACKET_RTX_REJECTED);
+ ENUM_TO_STRING(PACKET_RECEIVED);
}
+ NOTREACHED();
+ return "";
}
-FrameEvent::FrameEvent() {}
+FrameEvent::FrameEvent()
+ : rtp_timestamp(0u), frame_id(kFrameIdUnknown), size(0u), type(UNKNOWN),
+ media_type(UNKNOWN_EVENT), key_frame(false), target_bitrate(0) {}
FrameEvent::~FrameEvent() {}
-BasePacketInfo::BasePacketInfo() {}
-BasePacketInfo::~BasePacketInfo() {}
-
-PacketEvent::PacketEvent() {}
+PacketEvent::PacketEvent()
+ : rtp_timestamp(0),
+ frame_id(kFrameIdUnknown),
+ max_packet_id(0),
+ packet_id(0),
+ size(0),
+ type(UNKNOWN),
+ media_type(UNKNOWN_EVENT) {}
PacketEvent::~PacketEvent() {}
-GenericEvent::GenericEvent() {}
-GenericEvent::~GenericEvent() {}
-
-FrameLogStats::FrameLogStats()
- : framerate_fps(0),
- bitrate_kbps(0),
- max_delay_ms(0),
- min_delay_ms(0),
- avg_delay_ms(0) {}
-FrameLogStats::~FrameLogStats() {}
-
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/logging/logging_defines.h b/chromium/media/cast/logging/logging_defines.h
index 5a7bca1500f..021a3c99a7a 100644
--- a/chromium/media/cast/logging/logging_defines.h
+++ b/chromium/media/cast/logging/logging_defines.h
@@ -9,120 +9,88 @@
#include <string>
#include <vector>
-#include "base/memory/linked_ptr.h"
#include "base/time/time.h"
namespace media {
namespace cast {
-static const uint32 kFrameIdUnknown = 0xFFFF;
+static const uint32 kFrameIdUnknown = 0xFFFFFFFF;
-struct CastLoggingConfig {
- CastLoggingConfig();
- ~CastLoggingConfig();
+typedef uint32 RtpTimestamp;
- bool enable_data_collection;
- bool enable_uma_stats;
- bool enable_tracing;
+enum CastLoggingEvent {
+ UNKNOWN,
+ // Sender side frame events.
+ FRAME_CAPTURE_BEGIN,
+ FRAME_CAPTURE_END,
+ FRAME_ENCODED,
+ FRAME_ACK_RECEIVED,
+ // Receiver side frame events.
+ FRAME_ACK_SENT,
+ FRAME_DECODED,
+ FRAME_PLAYOUT,
+ // Sender side packet events.
+ PACKET_SENT_TO_NETWORK,
+ PACKET_RETRANSMITTED,
+ PACKET_RTX_REJECTED,
+ // Receiver side packet events.
+ PACKET_RECEIVED,
+ kNumOfLoggingEvents = PACKET_RECEIVED
};
-// By default, enable raw and stats data collection. Disable tracing and UMA.
-CastLoggingConfig GetDefaultCastLoggingConfig();
+const char* CastLoggingToString(CastLoggingEvent event);
-enum CastLoggingEvent {
- // Generic events.
- kUnknown,
- kRttMs,
- kPacketLoss,
- kJitterMs,
- kAckReceived,
- kRembBitrate,
- kAckSent,
- kLastEvent,
- // Audio sender.
- kAudioFrameReceived,
- kAudioFrameCaptured,
- kAudioFrameEncoded,
- // Audio receiver.
- kAudioPlayoutDelay,
- kAudioFrameDecoded,
- // Video sender.
- kVideoFrameCaptured,
- kVideoFrameReceived,
- kVideoFrameSentToEncoder,
- kVideoFrameEncoded,
- // Video receiver.
- kVideoFrameDecoded,
- kVideoRenderDelay,
- // Send-side packet events.
- kPacketSentToPacer,
- kPacketSentToNetwork,
- kPacketRetransmited,
- // Receive-side packet events.
- kPacketReceived,
-
- kNumOfLoggingEvents,
+// CastLoggingEvent are classified into one of three following types.
+enum EventMediaType {
+ AUDIO_EVENT,
+ VIDEO_EVENT,
+ UNKNOWN_EVENT,
+ EVENT_MEDIA_TYPE_LAST = UNKNOWN_EVENT
};
-std::string CastLoggingToString(CastLoggingEvent event);
-
struct FrameEvent {
FrameEvent();
~FrameEvent();
+ RtpTimestamp rtp_timestamp;
uint32 frame_id;
- size_t size; // Encoded size only.
- std::vector<base::TimeTicks> timestamp;
- std::vector<CastLoggingEvent> type;
- base::TimeDelta delay_delta; // Render/playout delay.
-};
-
-// Internal map sorted by packet id.
-struct BasePacketInfo {
- BasePacketInfo();
- ~BasePacketInfo();
+ // Size of encoded frame. Only set for FRAME_ENCODED event.
size_t size;
- std::vector<base::TimeTicks> timestamp;
- std::vector<CastLoggingEvent> type;
-};
-typedef std::map<uint16, BasePacketInfo> BasePacketMap;
+ // Time of event logged.
+ base::TimeTicks timestamp;
-struct PacketEvent {
- PacketEvent();
- ~PacketEvent();
- uint32 frame_id;
- int max_packet_id;
- BasePacketMap packet_map;
-};
+ CastLoggingEvent type;
-struct GenericEvent {
- GenericEvent();
- ~GenericEvent();
- std::vector<int> value;
- std::vector<base::TimeTicks> timestamp;
-};
+ EventMediaType media_type;
+
+ // Render / playout delay. Only set for FRAME_PLAYOUT events.
+ base::TimeDelta delay_delta;
-struct FrameLogStats {
- FrameLogStats();
- ~FrameLogStats();
+ // Whether the frame is a key frame. Only set for video FRAME_ENCODED event.
+ bool key_frame;
- double framerate_fps;
- double bitrate_kbps;
- int max_delay_ms;
- int min_delay_ms;
- int avg_delay_ms;
+ // The requested target bitrate of the encoder at the time the frame is
+ // encoded. Only set for video FRAME_ENCODED event.
+ int target_bitrate;
};
-// Store all log types in a map based on the event.
-typedef std::map<uint32, FrameEvent> FrameRawMap;
-typedef std::map<uint32, PacketEvent> PacketRawMap;
-typedef std::map<CastLoggingEvent, GenericEvent> GenericRawMap;
+struct PacketEvent {
+ PacketEvent();
+ ~PacketEvent();
+
+ RtpTimestamp rtp_timestamp;
+ uint32 frame_id;
+ uint16 max_packet_id;
+ uint16 packet_id;
+ size_t size;
-typedef std::map<CastLoggingEvent, linked_ptr<FrameLogStats > > FrameStatsMap;
-typedef std::map<CastLoggingEvent, double> PacketStatsMap;
-typedef std::map<CastLoggingEvent, double> GenericStatsMap;
+ // Time of event logged.
+ base::TimeTicks timestamp;
+ CastLoggingEvent type;
+ EventMediaType media_type;
+};
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/logging/logging_impl.cc b/chromium/media/cast/logging/logging_impl.cc
index ea96b94b610..1143d1be217 100644
--- a/chromium/media/cast/logging/logging_impl.cc
+++ b/chromium/media/cast/logging/logging_impl.cc
@@ -2,223 +2,113 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/big_endian.h"
#include "base/debug/trace_event.h"
-#include "base/metrics/histogram.h"
#include "media/cast/logging/logging_impl.h"
-#include "net/base/big_endian.h"
namespace media {
namespace cast {
-LoggingImpl::LoggingImpl(base::TickClock* clock,
- scoped_refptr<base::TaskRunner> main_thread_proxy,
- const CastLoggingConfig& config)
- : main_thread_proxy_(main_thread_proxy),
- config_(config),
- raw_(clock),
- stats_(clock) {}
+// TODO(imcheng): Collapse LoggingRaw onto LoggingImpl.
+LoggingImpl::LoggingImpl() {
+ // LoggingImpl can be constructed on any thread, but its methods should all be
+ // called on the same thread.
+ thread_checker_.DetachFromThread();
+}
LoggingImpl::~LoggingImpl() {}
-void LoggingImpl::InsertFrameEvent(CastLoggingEvent event,
+void LoggingImpl::InsertFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
uint32 rtp_timestamp,
uint32 frame_id) {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- if (config_.enable_data_collection) {
- raw_.InsertFrameEvent(event, rtp_timestamp, frame_id);
- stats_.InsertFrameEvent(event, rtp_timestamp, frame_id);
- }
- if (config_.enable_tracing) {
- std::string event_string = CastLoggingToString(event);
- TRACE_EVENT_INSTANT2(event_string.c_str(), "FE",
- TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp, "frame_id",
- frame_id);
- }
-}
-
-void LoggingImpl::InsertFrameEventWithSize(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- int frame_size) {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- if (config_.enable_data_collection) {
- raw_.InsertFrameEventWithSize(event, rtp_timestamp, frame_id, frame_size);
- stats_.InsertFrameEventWithSize(event, rtp_timestamp, frame_id, frame_size);
- }
- if (config_.enable_uma_stats) {
- UMA_HISTOGRAM_COUNTS(CastLoggingToString(event), frame_size);
- }
- if (config_.enable_tracing) {
- std::string event_string = CastLoggingToString(event);
- TRACE_EVENT_INSTANT2(event_string.c_str(), "FES",
- TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp, "frame_size",
- frame_size);
- }
+ DCHECK(thread_checker_.CalledOnValidThread());
+ raw_.InsertFrameEvent(time_of_event, event, event_media_type,
+ rtp_timestamp, frame_id);
}
-void LoggingImpl::InsertFrameEventWithDelay(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- base::TimeDelta delay) {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- if (config_.enable_data_collection) {
- raw_.InsertFrameEventWithDelay(event, rtp_timestamp, frame_id, delay);
- stats_.InsertFrameEventWithDelay(event, rtp_timestamp, frame_id, delay);
- }
- if (config_.enable_uma_stats) {
- UMA_HISTOGRAM_TIMES(CastLoggingToString(event), delay);
- }
- if (config_.enable_tracing) {
- std::string event_string = CastLoggingToString(event);
- TRACE_EVENT_INSTANT2(event_string.c_str(), "FED",
- TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp, "delay",
- delay.InMilliseconds());
- }
+void LoggingImpl::InsertEncodedFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 rtp_timestamp,
+ uint32 frame_id, int frame_size,
+ bool key_frame,
+ int target_bitrate) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ raw_.InsertEncodedFrameEvent(time_of_event, event, event_media_type,
+ rtp_timestamp, frame_id, frame_size, key_frame, target_bitrate);
}
-void LoggingImpl::InsertPacketListEvent(CastLoggingEvent event,
- const PacketList& packets) {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- for (unsigned int i = 0; i < packets.size(); ++i) {
- const Packet& packet = packets[i];
- // Parse basic properties.
- uint32 rtp_timestamp;
- uint16 packet_id, max_packet_id;
- const uint8* packet_data = &packet[0];
- net::BigEndianReader big_endian_reader(packet_data + 4, 4);
- big_endian_reader.ReadU32(&rtp_timestamp);
- net::BigEndianReader cast_big_endian_reader(packet_data + 12 + 2, 4);
- cast_big_endian_reader.ReadU16(&packet_id);
- cast_big_endian_reader.ReadU16(&max_packet_id);
- // rtp_timestamp is enough - no need for frame_id as well.
- InsertPacketEvent(event, rtp_timestamp, kFrameIdUnknown, packet_id,
- max_packet_id, packet.size());
- }
+void LoggingImpl::InsertFrameEventWithDelay(
+ const base::TimeTicks& time_of_event, CastLoggingEvent event,
+ EventMediaType event_media_type, uint32 rtp_timestamp, uint32 frame_id,
+ base::TimeDelta delay) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ raw_.InsertFrameEventWithDelay(time_of_event, event, event_media_type,
+ rtp_timestamp, frame_id, delay);
}
-void LoggingImpl::InsertPacketEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- uint16 packet_id,
- uint16 max_packet_id,
- size_t size) {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- if (config_.enable_data_collection) {
- raw_.InsertPacketEvent(event, rtp_timestamp, frame_id, packet_id,
- max_packet_id, size);
- stats_.InsertPacketEvent(event, rtp_timestamp, frame_id, packet_id,
- max_packet_id, size);
- }
- if (config_.enable_tracing) {
- std::string event_string = CastLoggingToString(event);
- TRACE_EVENT_INSTANT2(event_string.c_str(), "PE",
- TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp,
- "packet_id", packet_id);
- }
+void LoggingImpl::InsertSinglePacketEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ const Packet& packet) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Parse basic properties.
+ uint32 rtp_timestamp;
+ uint16 packet_id, max_packet_id;
+ const uint8* packet_data = &packet[0];
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(packet_data + 4), 4);
+ big_endian_reader.ReadU32(&rtp_timestamp);
+ base::BigEndianReader cast_big_endian_reader(
+ reinterpret_cast<const char*>(packet_data + 12 + 2), 4);
+ cast_big_endian_reader.ReadU16(&packet_id);
+ cast_big_endian_reader.ReadU16(&max_packet_id);
+
+ // rtp_timestamp is enough - no need for frame_id as well.
+ InsertPacketEvent(time_of_event,
+ event,
+ event_media_type,
+ rtp_timestamp,
+ kFrameIdUnknown,
+ packet_id,
+ max_packet_id,
+ packet.size());
}
-void LoggingImpl::InsertGenericEvent(CastLoggingEvent event, int value) {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- if (config_.enable_data_collection) {
- raw_.InsertGenericEvent(event, value);
- stats_.InsertGenericEvent(event, value);
- }
- if (config_.enable_uma_stats) {
- UMA_HISTOGRAM_COUNTS(CastLoggingToString(event), value);
- }
- if (config_.enable_tracing) {
- std::string event_string = CastLoggingToString(event);
- TRACE_EVENT_INSTANT1(event_string.c_str(), "GE",
- TRACE_EVENT_SCOPE_THREAD, "value", value);
- }
-}
-
-// should just get the entire class, would be much easier.
-FrameRawMap LoggingImpl::GetFrameRawData() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- return raw_.GetFrameData();
-}
-
-PacketRawMap LoggingImpl::GetPacketRawData() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- return raw_.GetPacketData();
-}
-
-GenericRawMap LoggingImpl::GetGenericRawData() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- return raw_.GetGenericData();
-}
-
-const FrameStatsMap* LoggingImpl::GetFrameStatsData() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- // Get stats data.
- const FrameStatsMap* stats = stats_.GetFrameStatsData();
- if (config_.enable_uma_stats) {
- FrameStatsMap::const_iterator it;
- for (it = stats->begin(); it != stats->end(); ++it) {
- // Check for an active event.
- if (it->second->framerate_fps > 0) {
- std::string event_string = CastLoggingToString(it->first);
- UMA_HISTOGRAM_COUNTS(event_string.append("_framerate_fps"),
- it->second->framerate_fps);
- } else {
- // All active frame events trigger framerate computation.
- continue;
- }
- if (it->second->bitrate_kbps > 0) {
- std::string evnt_string = CastLoggingToString(it->first);
- UMA_HISTOGRAM_COUNTS(evnt_string.append("_bitrate_kbps"),
- it->second->framerate_fps);
- }
- if (it->second->avg_delay_ms > 0) {
- std::string event_string = CastLoggingToString(it->first);
- UMA_HISTOGRAM_COUNTS(event_string.append("_avg_delay_ms"),
- it->second->avg_delay_ms);
- UMA_HISTOGRAM_COUNTS(event_string.append("_min_delay_ms"),
- it->second->min_delay_ms);
- UMA_HISTOGRAM_COUNTS(event_string.append("_max_delay_ms"),
- it->second->max_delay_ms);
- }
- }
+void LoggingImpl::InsertPacketListEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ const PacketList& packets) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (PacketList::const_iterator it = packets.begin(); it != packets.end();
+ ++it) {
+ InsertSinglePacketEvent(time_of_event, event, event_media_type,
+ (*it)->data);
}
- return stats;
}
-const PacketStatsMap* LoggingImpl::GetPacketStatsData() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- // Get stats data.
- const PacketStatsMap* stats = stats_.GetPacketStatsData();
- if (config_.enable_uma_stats) {
- PacketStatsMap::const_iterator it;
- for (it = stats->begin(); it != stats->end(); ++it) {
- if (it->second > 0) {
- std::string event_string = CastLoggingToString(it->first);
- UMA_HISTOGRAM_COUNTS(event_string.append("_bitrate_kbps"), it->second);
- }
- }
- }
- return stats;
+void LoggingImpl::InsertPacketEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id,
+ uint16 packet_id, uint16 max_packet_id,
+ size_t size) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ raw_.InsertPacketEvent(time_of_event, event, event_media_type,
+ rtp_timestamp, frame_id, packet_id, max_packet_id, size);
}
-const GenericStatsMap* LoggingImpl::GetGenericStatsData() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- // Get stats data.
- const GenericStatsMap* stats = stats_.GetGenericStatsData();
- if (config_.enable_uma_stats) {
- GenericStatsMap::const_iterator it;
- for (it = stats->begin(); it != stats->end(); ++it) {
- if (it->second > 0) {
- UMA_HISTOGRAM_COUNTS(CastLoggingToString(it->first), it->second);
- }
- }
- }
- return stats;
+void LoggingImpl::AddRawEventSubscriber(RawEventSubscriber* subscriber) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ raw_.AddSubscriber(subscriber);
}
-void LoggingImpl::Reset() {
- DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
- raw_.Reset();
- stats_.Reset();
+void LoggingImpl::RemoveRawEventSubscriber(RawEventSubscriber* subscriber) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ raw_.RemoveSubscriber(subscriber);
}
} // namespace cast
diff --git a/chromium/media/cast/logging/logging_impl.h b/chromium/media/cast/logging/logging_impl.h
index 34021b7d03c..ba453c8c8ee 100644
--- a/chromium/media/cast/logging/logging_impl.h
+++ b/chromium/media/cast/logging/logging_impl.h
@@ -7,67 +7,66 @@
// Generic class that handles event logging for the cast library.
// Logging has three possible optional forms:
// 1. Raw data and stats accessible by the application.
-// 2. UMA stats.
-// 3. Tracing of raw events.
+// 2. Tracing of raw events.
#include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
+#include "base/threading/thread_checker.h"
#include "media/cast/cast_config.h"
#include "media/cast/logging/logging_defines.h"
#include "media/cast/logging/logging_raw.h"
-#include "media/cast/logging/logging_stats.h"
namespace media {
namespace cast {
-// Should only be called from the main thread.
-class LoggingImpl : public base::NonThreadSafe {
+class LoggingImpl {
public:
- LoggingImpl(base::TickClock* clock,
- scoped_refptr<base::TaskRunner> main_thread_proxy,
- const CastLoggingConfig& config);
-
+ LoggingImpl();
~LoggingImpl();
- // TODO(pwestin): Add argument to API to send in time of event instead of
- // grabbing now.
- void InsertFrameEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id);
- void InsertFrameEventWithSize(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- int frame_size);
- void InsertFrameEventWithDelay(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
+ // Note: All methods below should be called from the same thread.
+
+ void InsertFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event, EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id);
+
+ void InsertEncodedFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id,
+ int frame_size, bool key_frame,
+ int target_bitrate);
+
+ void InsertFrameEventWithDelay(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id,
base::TimeDelta delay);
- void InsertPacketListEvent(CastLoggingEvent event, const PacketList& packets);
-
- void InsertPacketEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- uint16 packet_id,
- uint16 max_packet_id,
- size_t size);
- void InsertGenericEvent(CastLoggingEvent event, int value);
-
- // Get raw data.
- FrameRawMap GetFrameRawData();
- PacketRawMap GetPacketRawData();
- GenericRawMap GetGenericRawData();
- // Get stats only (computed when called). Triggers UMA stats when enabled.
- const FrameStatsMap* GetFrameStatsData();
- const PacketStatsMap* GetPacketStatsData();
- const GenericStatsMap* GetGenericStatsData();
-
- void Reset();
+
+ void InsertSinglePacketEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ const Packet& packet);
+
+ void InsertPacketListEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ const PacketList& packets);
+
+ void InsertPacketEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type, uint32 rtp_timestamp,
+ uint32 frame_id, uint16 packet_id,
+ uint16 max_packet_id, size_t size);
+
+ // Delegates to |LoggingRaw::AddRawEventSubscriber()|.
+ void AddRawEventSubscriber(RawEventSubscriber* subscriber);
+
+ // Delegates to |LoggingRaw::RemoveRawEventSubscriber()|.
+ void RemoveRawEventSubscriber(RawEventSubscriber* subscriber);
private:
- scoped_refptr<base::TaskRunner> main_thread_proxy_;
- const CastLoggingConfig config_;
+ base::ThreadChecker thread_checker_;
LoggingRaw raw_;
- LoggingStats stats_;
DISALLOW_COPY_AND_ASSIGN(LoggingImpl);
};
diff --git a/chromium/media/cast/logging/logging_impl_unittest.cc b/chromium/media/cast/logging/logging_impl_unittest.cc
new file mode 100644
index 00000000000..712d76bae60
--- /dev/null
+++ b/chromium/media/cast/logging/logging_impl_unittest.cc
@@ -0,0 +1,234 @@
+// 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 <stdint.h>
+
+#include <vector>
+
+#include "base/rand_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/logging_impl.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+// Insert frame duration- one second.
+const int64 kIntervalTime1S = 1;
+// Test frame rate goal - 30fps.
+const int kFrameIntervalMs = 33;
+
+static const int64 kStartMillisecond = INT64_C(12345678900000);
+
+class LoggingImplTest : public ::testing::Test {
+ protected:
+ LoggingImplTest() {
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ logging_.AddRawEventSubscriber(&event_subscriber_);
+ }
+
+ virtual ~LoggingImplTest() {
+ logging_.RemoveRawEventSubscriber(&event_subscriber_);
+ }
+
+ LoggingImpl logging_;
+ base::SimpleTestTickClock testing_clock_;
+ SimpleEventSubscriber event_subscriber_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoggingImplTest);
+};
+
+TEST_F(LoggingImplTest, BasicFrameLogging) {
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ base::TimeTicks now;
+ do {
+ now = testing_clock_.NowTicks();
+ logging_.InsertFrameEvent(
+ now, FRAME_CAPTURE_BEGIN, VIDEO_EVENT, rtp_timestamp, frame_id);
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = now - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+
+ // Get logging data.
+ std::vector<FrameEvent> frame_events;
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ // Size of vector should be equal to the number of events logged,
+ // which equals to number of frames in this case.
+ EXPECT_EQ(frame_id, frame_events.size());
+}
+
+TEST_F(LoggingImplTest, FrameLoggingWithSize) {
+ // Average packet size.
+ const int kBaseFrameSizeBytes = 25000;
+ const int kRandomSizeInterval = 100;
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ size_t sum_size = 0;
+ int target_bitrate = 1234;
+ do {
+ int size = kBaseFrameSizeBytes +
+ base::RandInt(-kRandomSizeInterval, kRandomSizeInterval);
+ sum_size += static_cast<size_t>(size);
+ logging_.InsertEncodedFrameEvent(testing_clock_.NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT, rtp_timestamp,
+ frame_id, size, true, target_bitrate);
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ std::vector<FrameEvent> frame_events;
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ // Size of vector should be equal to the number of events logged, which
+ // equals to number of frames in this case.
+ EXPECT_EQ(frame_id, frame_events.size());
+}
+
+TEST_F(LoggingImplTest, FrameLoggingWithDelay) {
+ // Average packet size.
+ const int kPlayoutDelayMs = 50;
+ const int kRandomSizeInterval = 20;
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ do {
+ int delay = kPlayoutDelayMs +
+ base::RandInt(-kRandomSizeInterval, kRandomSizeInterval);
+ logging_.InsertFrameEventWithDelay(
+ testing_clock_.NowTicks(),
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ base::TimeDelta::FromMilliseconds(delay));
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ std::vector<FrameEvent> frame_events;
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ // Size of vector should be equal to the number of frames logged.
+ EXPECT_EQ(frame_id, frame_events.size());
+}
+
+TEST_F(LoggingImplTest, MultipleEventFrameLogging) {
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0u;
+ uint32 frame_id = 0u;
+ uint32 num_events = 0u;
+ do {
+ logging_.InsertFrameEvent(testing_clock_.NowTicks(),
+ FRAME_CAPTURE_END,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id);
+ ++num_events;
+ if (frame_id % 2) {
+ logging_.InsertEncodedFrameEvent(testing_clock_.NowTicks(),
+ FRAME_ENCODED, AUDIO_EVENT,
+ rtp_timestamp,
+ frame_id, 1500, true, 0);
+ } else if (frame_id % 3) {
+ logging_.InsertFrameEvent(testing_clock_.NowTicks(), FRAME_DECODED,
+ VIDEO_EVENT, rtp_timestamp, frame_id);
+ } else {
+ logging_.InsertFrameEventWithDelay(
+ testing_clock_.NowTicks(), FRAME_PLAYOUT, VIDEO_EVENT,
+ rtp_timestamp, frame_id, base::TimeDelta::FromMilliseconds(20));
+ }
+ ++num_events;
+
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ std::vector<FrameEvent> frame_events;
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ // Size of vector should be equal to the number of frames logged.
+ EXPECT_EQ(num_events, frame_events.size());
+ // Multiple events captured per frame.
+}
+
+TEST_F(LoggingImplTest, PacketLogging) {
+ const int kNumPacketsPerFrame = 10;
+ const int kBaseSize = 2500;
+ const int kSizeInterval = 100;
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeTicks latest_time;
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ RtpTimestamp rtp_timestamp = 0;
+ int frame_id = 0;
+ int num_packets = 0;
+ int sum_size = 0u;
+ do {
+ for (int i = 0; i < kNumPacketsPerFrame; ++i) {
+ int size = kBaseSize + base::RandInt(-kSizeInterval, kSizeInterval);
+ sum_size += size;
+ latest_time = testing_clock_.NowTicks();
+ ++num_packets;
+ logging_.InsertPacketEvent(latest_time,
+ PACKET_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ i,
+ kNumPacketsPerFrame,
+ size);
+ }
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ std::vector<PacketEvent> packet_events;
+ event_subscriber_.GetPacketEventsAndReset(&packet_events);
+ // Size of vector should be equal to the number of packets logged.
+ EXPECT_EQ(num_packets, static_cast<int>(packet_events.size()));
+}
+
+TEST_F(LoggingImplTest, MultipleRawEventSubscribers) {
+ SimpleEventSubscriber event_subscriber_2;
+
+ // Now logging_ has two subscribers.
+ logging_.AddRawEventSubscriber(&event_subscriber_2);
+
+ logging_.InsertFrameEvent(testing_clock_.NowTicks(),
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ /*rtp_timestamp*/ 0u,
+ /*frame_id*/ 0u);
+
+ std::vector<FrameEvent> frame_events;
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ EXPECT_EQ(1u, frame_events.size());
+ frame_events.clear();
+ event_subscriber_2.GetFrameEventsAndReset(&frame_events);
+ EXPECT_EQ(1u, frame_events.size());
+
+ logging_.RemoveRawEventSubscriber(&event_subscriber_2);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/logging_internal.cc b/chromium/media/cast/logging/logging_internal.cc
deleted file mode 100644
index ce2249ee4e0..00000000000
--- a/chromium/media/cast/logging/logging_internal.cc
+++ /dev/null
@@ -1,79 +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/logging/logging_internal.h"
-
-namespace media {
-namespace cast {
-
-FrameLogData::FrameLogData(base::TickClock* clock)
- : clock_(clock),
- frame_map_() {}
-
-FrameLogData::~FrameLogData() {}
-
-void FrameLogData::Insert(uint32 rtp_timestamp, uint32 frame_id) {
- FrameEvent info;
- InsertBase(rtp_timestamp, frame_id, info);
-}
-
-void FrameLogData::InsertWithSize(
- uint32 rtp_timestamp, uint32 frame_id, int size) {
- FrameEvent info;
- info.size = size;
- InsertBase(rtp_timestamp, frame_id, info);
-}
-
-void FrameLogData::InsertWithDelay(
- uint32 rtp_timestamp, uint32 frame_id, base::TimeDelta delay) {
- FrameEvent info;
- info.delay_delta = delay;
- InsertBase(rtp_timestamp, frame_id, info);
-}
-
-void FrameLogData::InsertBase(
- uint32 rtp_timestamp, uint32 frame_id, FrameEvent info) {
- info.timestamp = clock_->NowTicks();
- info.frame_id = frame_id;
- frame_map_.insert(std::make_pair(rtp_timestamp, info));
-}
-
-PacketLogData::PacketLogData(base::TickClock* clock)
- : clock_(clock),
- packet_map_() {}
-
-PacketLogData::~PacketLogData() {}
-
-void PacketLogData::Insert(uint32 rtp_timestamp,
- uint32 frame_id, uint16 packet_id, uint16 max_packet_id, int size) {
- PacketEvent info;
- info.size = size;
- info.max_packet_id = max_packet_id;
- info.frame_id = frame_id;
- info.timestamp = clock_->NowTicks();
- // Is this a new frame?
- PacketMap::iterator it = packet_map_.find(rtp_timestamp);
- if (it == packet_map_.end()) {
- // New rtp_timestamp id - create base packet map.
- BasePacketMap base_map;
- base_map.insert(std::make_pair(packet_id, info));
- packet_map_.insert(std::make_pair(rtp_timestamp, base_map));
- } else {
- // Existing rtp_timestamp.
- it->second.insert(std::make_pair(packet_id, info));
- }
-}
-
-GenericLogData::GenericLogData(base::TickClock* clock)
- : clock_(clock) {}
-
-GenericLogData::~GenericLogData() {}
-
-void GenericLogData::Insert(int data) {
- data_.push_back(data);
- timestamp_.push_back(clock_->NowTicks());
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/logging/logging_internal.h b/chromium/media/cast/logging/logging_internal.h
deleted file mode 100644
index 6f028b925fe..00000000000
--- a/chromium/media/cast/logging/logging_internal.h
+++ /dev/null
@@ -1,95 +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_LOGGING_LOGGING_INTERNAL_H_
-#define MEDIA_CAST_LOGGING_LOGGING_INTERNAL_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-
-namespace media {
-namespace cast {
-
-// TODO(mikhal): Consider storing only the delta time and not absolute time.
-struct FrameEvent {
- uint32 frame_id;
- int size;
- base::TimeTicks timestamp;
- base::TimeDelta delay_delta; // render/playout delay.
-};
-
-struct PacketEvent {
- uint32 frame_id;
- int max_packet_id;
- size_t size;
- base::TimeTicks timestamp;
-};
-
-// Frame and packet maps are sorted based on the rtp_timestamp.
-typedef std::map<uint32, FrameEvent> FrameMap;
-typedef std::map<uint16, PacketEvent> BasePacketMap;
-typedef std::map<uint32, BasePacketMap> PacketMap;
-
-class FrameLogData {
- public:
- explicit FrameLogData(base::TickClock* clock);
- ~FrameLogData();
- void Insert(uint32 rtp_timestamp, uint32 frame_id);
- // Include size for encoded images (compute bitrate),
- void InsertWithSize(uint32 rtp_timestamp, uint32 frame_id, int size);
- // Include playout/render delay info.
- void InsertWithDelay(
- uint32 rtp_timestamp, uint32 frame_id, base::TimeDelta delay);
- void Reset();
-
- private:
- void InsertBase(uint32 rtp_timestamp, uint32 frame_id, FrameEvent info);
-
- base::TickClock* const clock_; // Not owned by this class.
- FrameMap frame_map_;
-
- DISALLOW_COPY_AND_ASSIGN(FrameLogData);
-};
-
-// TODO(mikhal): Should be able to handle packet bursts.
-class PacketLogData {
- public:
- explicit PacketLogData(base::TickClock* clock);
- ~PacketLogData();
- void Insert(uint32 rtp_timestamp, uint32 frame_id, uint16 packet_id,
- uint16 max_packet_id, int size);
- void Reset();
-
- private:
- base::TickClock* const clock_; // Not owned by this class.
- PacketMap packet_map_;
-
- DISALLOW_COPY_AND_ASSIGN(PacketLogData);
-};
-
-class GenericLogData {
- public:
- explicit GenericLogData(base::TickClock* clock);
- ~GenericLogData();
- void Insert(int value);
- void Reset();
-
- private:
- base::TickClock* const clock_; // Not owned by this class.
- std::vector<int> data_;
- std::vector<base::TimeTicks> timestamp_;
-
- DISALLOW_COPY_AND_ASSIGN(GenericLogData);
-};
-
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_LOGGING_LOGGING_INTERNAL_H_
diff --git a/chromium/media/cast/logging/logging_raw.cc b/chromium/media/cast/logging/logging_raw.cc
index 6a389617f62..229064d7b69 100644
--- a/chromium/media/cast/logging/logging_raw.cc
+++ b/chromium/media/cast/logging/logging_raw.cc
@@ -4,139 +4,110 @@
#include "media/cast/logging/logging_raw.h"
+#include <algorithm>
+
#include "base/logging.h"
-#include "base/metrics/histogram.h"
#include "base/time/time.h"
namespace media {
namespace cast {
-LoggingRaw::LoggingRaw(base::TickClock* clock)
- : clock_(clock),
- frame_map_(),
- packet_map_(),
- generic_map_(),
- weak_factory_(this) {}
+LoggingRaw::LoggingRaw() {}
LoggingRaw::~LoggingRaw() {}
-void LoggingRaw::InsertFrameEvent(CastLoggingEvent event,
+void LoggingRaw::InsertFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
uint32 rtp_timestamp,
uint32 frame_id) {
- InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+ InsertBaseFrameEvent(time_of_event, event, event_media_type, frame_id,
+ rtp_timestamp, base::TimeDelta(), 0, false, 0);
}
-void LoggingRaw::InsertFrameEventWithSize(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- int size) {
- InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
- // Now insert size.
- FrameRawMap::iterator it = frame_map_.find(rtp_timestamp);
- DCHECK(it != frame_map_.end());
- it->second.size = size;
+void LoggingRaw::InsertEncodedFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id,
+ int size, bool key_frame,
+ int target_bitrate) {
+ InsertBaseFrameEvent(time_of_event, event, event_media_type,
+ frame_id, rtp_timestamp, base::TimeDelta(), size,
+ key_frame, target_bitrate);
}
-void LoggingRaw::InsertFrameEventWithDelay(CastLoggingEvent event,
+void LoggingRaw::InsertFrameEventWithDelay(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
uint32 rtp_timestamp,
uint32 frame_id,
base::TimeDelta delay) {
- InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
- // Now insert delay.
- FrameRawMap::iterator it = frame_map_.find(rtp_timestamp);
- DCHECK(it != frame_map_.end());
- it->second.delay_delta = delay;
+ InsertBaseFrameEvent(time_of_event, event, event_media_type, frame_id,
+ rtp_timestamp, delay, 0, false, 0);
}
-void LoggingRaw::InsertBaseFrameEvent(CastLoggingEvent event,
+void LoggingRaw::InsertBaseFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
uint32 frame_id,
- uint32 rtp_timestamp) {
- // Is this a new event?
- FrameRawMap::iterator it = frame_map_.find(rtp_timestamp);
- if (it == frame_map_.end()) {
- // Create a new map entry.
- FrameEvent info;
- info.frame_id = frame_id;
- info.timestamp.push_back(clock_->NowTicks());
- info.type.push_back(event);
- frame_map_.insert(std::make_pair(rtp_timestamp, info));
- } else {
- // Insert to an existing entry.
- it->second.timestamp.push_back(clock_->NowTicks());
- it->second.type.push_back(event);
- // Do we have a valid frame_id?
- // Not all events have a valid frame id.
- if (it->second.frame_id == kFrameIdUnknown && frame_id != kFrameIdUnknown)
- it->second.frame_id = frame_id;
+ uint32 rtp_timestamp,
+ base::TimeDelta delay, int size,
+ bool key_frame, int target_bitrate) {
+ FrameEvent frame_event;
+ frame_event.rtp_timestamp = rtp_timestamp;
+ frame_event.frame_id = frame_id;
+ frame_event.size = size;
+ frame_event.timestamp = time_of_event;
+ frame_event.type = event;
+ frame_event.media_type = event_media_type;
+ frame_event.delay_delta = delay;
+ frame_event.key_frame = key_frame;
+ frame_event.target_bitrate = target_bitrate;
+ for (std::vector<RawEventSubscriber*>::const_iterator it =
+ subscribers_.begin();
+ it != subscribers_.end(); ++it) {
+ (*it)->OnReceiveFrameEvent(frame_event);
}
}
-void LoggingRaw::InsertPacketEvent(CastLoggingEvent event,
+void LoggingRaw::InsertPacketEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
uint32 rtp_timestamp,
- uint32 frame_id,
- uint16 packet_id,
- uint16 max_packet_id,
- size_t size) {
- // Is this packet belonging to a new frame?
- PacketRawMap::iterator it = packet_map_.find(rtp_timestamp);
- if (it == packet_map_.end()) {
- // Create a new entry - start with base packet map.
- PacketEvent info;
- info.frame_id = frame_id;
- info.max_packet_id = max_packet_id;
- BasePacketInfo base_info;
- base_info.size = size;
- base_info.timestamp.push_back(clock_->NowTicks());
- base_info.type.push_back(event);
- packet_map_.insert(std::make_pair(rtp_timestamp, info));
- } else {
- // Is this a new packet?
- BasePacketMap::iterator packet_it = it->second.packet_map.find(packet_id);
- if (packet_it == it->second.packet_map.end()) {
- BasePacketInfo base_info;
- base_info.size = size;
- base_info.timestamp.push_back(clock_->NowTicks());
- base_info.type.push_back(event);
- it->second.packet_map.insert(std::make_pair(packet_id, base_info));
- } else {
- packet_it->second.timestamp.push_back(clock_->NowTicks());
- packet_it->second.type.push_back(event);
- }
- }
-}
-
-void LoggingRaw::InsertGenericEvent(CastLoggingEvent event, int value) {
- GenericEvent event_data;
- event_data.value.push_back(value);
- event_data.timestamp.push_back(clock_->NowTicks());
- // Is this a new event?
- GenericRawMap::iterator it = generic_map_.find(event);
- if (it == generic_map_.end()) {
- // Create new entry.
- generic_map_.insert(std::make_pair(event, event_data));
- } else {
- // Insert to existing entry.
- it->second.value.push_back(value);
- it->second.timestamp.push_back(clock_->NowTicks());
+ uint32 frame_id, uint16 packet_id,
+ uint16 max_packet_id, size_t size) {
+ PacketEvent packet_event;
+ packet_event.rtp_timestamp = rtp_timestamp;
+ packet_event.frame_id = frame_id;
+ packet_event.max_packet_id = max_packet_id;
+ packet_event.packet_id = packet_id;
+ packet_event.size = size;
+ packet_event.timestamp = time_of_event;
+ packet_event.type = event;
+ packet_event.media_type = event_media_type;
+ for (std::vector<RawEventSubscriber*>::const_iterator it =
+ subscribers_.begin();
+ it != subscribers_.end(); ++it) {
+ (*it)->OnReceivePacketEvent(packet_event);
}
}
-FrameRawMap LoggingRaw::GetFrameData() const {
- return frame_map_;
-}
+void LoggingRaw::AddSubscriber(RawEventSubscriber* subscriber) {
+ DCHECK(subscriber);
+ DCHECK(std::find(subscribers_.begin(), subscribers_.end(), subscriber) ==
+ subscribers_.end());
-PacketRawMap LoggingRaw::GetPacketData() const {
- return packet_map_;
+ subscribers_.push_back(subscriber);
}
-GenericRawMap LoggingRaw::GetGenericData() const {
- return generic_map_;
-}
+void LoggingRaw::RemoveSubscriber(RawEventSubscriber* subscriber) {
+ DCHECK(subscriber);
+ DCHECK(std::find(subscribers_.begin(), subscribers_.end(), subscriber) !=
+ subscribers_.end());
-void LoggingRaw::Reset() {
- frame_map_.clear();
- packet_map_.clear();
- generic_map_.clear();
+ subscribers_.erase(
+ std::remove(subscribers_.begin(), subscribers_.end(), subscriber),
+ subscribers_.end());
}
} // namespace cast
diff --git a/chromium/media/cast/logging/logging_raw.h b/chromium/media/cast/logging/logging_raw.h
index 4ac8d0fb7ad..8ed4a599602 100644
--- a/chromium/media/cast/logging/logging_raw.h
+++ b/chromium/media/cast/logging/logging_raw.h
@@ -5,75 +5,80 @@
#ifndef MEDIA_CAST_LOGGING_LOGGING_RAW_H_
#define MEDIA_CAST_LOGGING_LOGGING_RAW_H_
-#include <map>
-#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/linked_ptr.h"
-#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "base/time/tick_clock.h"
#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/raw_event_subscriber.h"
namespace media {
namespace cast {
// This class is not thread safe, and should only be called from the main
// thread.
-class LoggingRaw : public base::NonThreadSafe,
- public base::SupportsWeakPtr<LoggingRaw> {
+class LoggingRaw : public base::NonThreadSafe {
public:
- explicit LoggingRaw(base::TickClock* clock);
+ LoggingRaw();
~LoggingRaw();
- // Inform of new event: three types of events: frame, packets and generic.
+ // Inform of new event: two types of events: frame and packet.
// Frame events can be inserted with different parameters.
- void InsertFrameEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id);
-
- // Size - Inserting the size implies that this is an encoded frame.
- void InsertFrameEventWithSize(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- int frame_size);
+ void InsertFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event, EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id);
+
+ // This function is only applicable for FRAME_ENCODED event.
+ // |size| - Size of encoded frame.
+ // |key_frame| - Whether the frame is a key frame. This field is only
+ // applicable for video event.
+ // |target_bitrate| - The target bitrate of the encoder the time the frame
+ // was encoded. Only applicable for video event.
+ void InsertEncodedFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 rtp_timestamp, uint32 frame_id,
+ int size, bool key_frame,
+ int target_bitrate);
// Render/playout delay
- void InsertFrameEventWithDelay(CastLoggingEvent event,
+ // This function is only applicable for FRAME_PLAYOUT event.
+ void InsertFrameEventWithDelay(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
uint32 rtp_timestamp,
- uint32 frame_id,
- base::TimeDelta delay);
+ uint32 frame_id, base::TimeDelta delay);
// Insert a packet event.
- void InsertPacketEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- uint16 packet_id,
- uint16 max_packet_id,
- size_t size);
-
- void InsertGenericEvent(CastLoggingEvent event, int value);
-
- // Get raw log data.
- FrameRawMap GetFrameData() const;
- PacketRawMap GetPacketData() const;
- GenericRawMap GetGenericData() const;
-
-
- // Reset all log data.
- void Reset();
+ void InsertPacketEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type, uint32 rtp_timestamp,
+ uint32 frame_id, uint16 packet_id,
+ uint16 max_packet_id, size_t size);
+
+ // Adds |subscriber| so that it will start receiving events on main thread.
+ // Note that this class does not own |subscriber|.
+ // It is a no-op to add a subscriber that already exists.
+ void AddSubscriber(RawEventSubscriber* subscriber);
+
+ // Removes |subscriber| so that it will stop receiving events.
+ // Note that this class does NOT own the subscribers. This function MUST be
+ // called before |subscriber| is destroyed if it was previously added.
+ // It is a no-op to remove a subscriber that doesn't exist.
+ void RemoveSubscriber(RawEventSubscriber* subscriber);
private:
- void InsertBaseFrameEvent(CastLoggingEvent event,
- uint32 frame_id,
- uint32 rtp_timestamp);
+ void InsertBaseFrameEvent(const base::TimeTicks& time_of_event,
+ CastLoggingEvent event,
+ EventMediaType event_media_type,
+ uint32 frame_id, uint32 rtp_timestamp,
+ base::TimeDelta delay, int size, bool key_frame,
+ int target_bitrate);
- base::TickClock* const clock_; // Not owned by this class.
- FrameRawMap frame_map_;
- PacketRawMap packet_map_;
- GenericRawMap generic_map_;
- base::WeakPtrFactory<LoggingRaw> weak_factory_;
+ // List of subscriber pointers. This class does not own the subscribers.
+ std::vector<RawEventSubscriber*> subscribers_;
DISALLOW_COPY_AND_ASSIGN(LoggingRaw);
};
@@ -82,4 +87,3 @@ class LoggingRaw : public base::NonThreadSafe,
} // namespace media
#endif // MEDIA_CAST_LOGGING_LOGGING_RAW_H_
-
diff --git a/chromium/media/cast/logging/logging_raw_unittest.cc b/chromium/media/cast/logging/logging_raw_unittest.cc
new file mode 100644
index 00000000000..0b7c05aaac1
--- /dev/null
+++ b/chromium/media/cast/logging/logging_raw_unittest.cc
@@ -0,0 +1,196 @@
+// 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/logging/logging_defines.h"
+#include "media/cast/logging/logging_raw.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+class LoggingRawTest : public ::testing::Test {
+ protected:
+ LoggingRawTest() {
+ raw_.AddSubscriber(&event_subscriber_);
+ }
+
+ virtual ~LoggingRawTest() { raw_.RemoveSubscriber(&event_subscriber_); }
+
+ LoggingRaw raw_;
+ SimpleEventSubscriber event_subscriber_;
+ std::vector<FrameEvent> frame_events_;
+ std::vector<PacketEvent> packet_events_;
+};
+
+TEST_F(LoggingRawTest, FrameEvent) {
+ CastLoggingEvent event_type = FRAME_DECODED;
+ EventMediaType media_type = VIDEO_EVENT;
+ uint32 frame_id = 456u;
+ RtpTimestamp rtp_timestamp = 123u;
+ base::TimeTicks timestamp = base::TimeTicks();
+ raw_.InsertFrameEvent(timestamp, event_type, media_type,
+ rtp_timestamp, frame_id);
+
+ event_subscriber_.GetPacketEventsAndReset(&packet_events_);
+ EXPECT_TRUE(packet_events_.empty());
+
+ event_subscriber_.GetFrameEventsAndReset(&frame_events_);
+ ASSERT_EQ(1u, frame_events_.size());
+ EXPECT_EQ(rtp_timestamp, frame_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, frame_events_[0].frame_id);
+ EXPECT_EQ(0u, frame_events_[0].size);
+ EXPECT_EQ(timestamp, frame_events_[0].timestamp);
+ EXPECT_EQ(event_type, frame_events_[0].type);
+ EXPECT_EQ(media_type, frame_events_[0].media_type);
+ EXPECT_EQ(base::TimeDelta(), frame_events_[0].delay_delta);
+}
+
+TEST_F(LoggingRawTest, EncodedFrameEvent) {
+ CastLoggingEvent event_type = FRAME_ENCODED;
+ EventMediaType media_type = VIDEO_EVENT;
+ uint32 frame_id = 456u;
+ RtpTimestamp rtp_timestamp = 123u;
+ base::TimeTicks timestamp = base::TimeTicks();
+ int size = 1024;
+ bool key_frame = true;
+ int target_bitrate = 4096;
+ raw_.InsertEncodedFrameEvent(timestamp, event_type, media_type,
+ rtp_timestamp, frame_id, size, key_frame, target_bitrate);
+
+ event_subscriber_.GetPacketEventsAndReset(&packet_events_);
+ EXPECT_TRUE(packet_events_.empty());
+
+ event_subscriber_.GetFrameEventsAndReset(&frame_events_);
+ ASSERT_EQ(1u, frame_events_.size());
+ EXPECT_EQ(rtp_timestamp, frame_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, frame_events_[0].frame_id);
+ EXPECT_EQ(size, static_cast<int>(frame_events_[0].size));
+ EXPECT_EQ(timestamp, frame_events_[0].timestamp);
+ EXPECT_EQ(event_type, frame_events_[0].type);
+ EXPECT_EQ(media_type, frame_events_[0].media_type);
+ EXPECT_EQ(base::TimeDelta(), frame_events_[0].delay_delta);
+ EXPECT_EQ(key_frame, frame_events_[0].key_frame);
+ EXPECT_EQ(target_bitrate, frame_events_[0].target_bitrate);
+}
+
+TEST_F(LoggingRawTest, FrameEventWithDelay) {
+ CastLoggingEvent event_type = FRAME_PLAYOUT;
+ EventMediaType media_type = VIDEO_EVENT;
+ uint32 frame_id = 456u;
+ RtpTimestamp rtp_timestamp = 123u;
+ base::TimeTicks timestamp = base::TimeTicks();
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(20);
+ raw_.InsertFrameEventWithDelay(timestamp, event_type, media_type,
+ rtp_timestamp, frame_id, delay);
+
+ event_subscriber_.GetPacketEventsAndReset(&packet_events_);
+ EXPECT_TRUE(packet_events_.empty());
+
+ event_subscriber_.GetFrameEventsAndReset(&frame_events_);
+ ASSERT_EQ(1u, frame_events_.size());
+ EXPECT_EQ(rtp_timestamp, frame_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, frame_events_[0].frame_id);
+ EXPECT_EQ(0u, frame_events_[0].size);
+ EXPECT_EQ(timestamp, frame_events_[0].timestamp);
+ EXPECT_EQ(event_type, frame_events_[0].type);
+ EXPECT_EQ(media_type, frame_events_[0].media_type);
+ EXPECT_EQ(delay, frame_events_[0].delay_delta);
+}
+
+TEST_F(LoggingRawTest, PacketEvent) {
+ CastLoggingEvent event_type = PACKET_RECEIVED;
+ EventMediaType media_type = VIDEO_EVENT;
+ uint32 frame_id = 456u;
+ uint16 packet_id = 1u;
+ uint16 max_packet_id = 10u;
+ RtpTimestamp rtp_timestamp = 123u;
+ base::TimeTicks timestamp = base::TimeTicks();
+ size_t size = 1024u;
+ raw_.InsertPacketEvent(timestamp, event_type, media_type,
+ rtp_timestamp, frame_id, packet_id, max_packet_id, size);
+
+ event_subscriber_.GetFrameEventsAndReset(&frame_events_);
+ EXPECT_TRUE(frame_events_.empty());
+
+ event_subscriber_.GetPacketEventsAndReset(&packet_events_);
+ ASSERT_EQ(1u, packet_events_.size());
+
+ EXPECT_EQ(rtp_timestamp, packet_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, packet_events_[0].frame_id);
+ EXPECT_EQ(max_packet_id, packet_events_[0].max_packet_id);
+ EXPECT_EQ(packet_id, packet_events_[0].packet_id);
+ EXPECT_EQ(size, packet_events_[0].size);
+ EXPECT_EQ(timestamp, packet_events_[0].timestamp);
+ EXPECT_EQ(event_type, packet_events_[0].type);
+ EXPECT_EQ(media_type, packet_events_[0].media_type);
+}
+
+TEST_F(LoggingRawTest, MultipleSubscribers) {
+ SimpleEventSubscriber event_subscriber_2;
+
+ // Now raw_ has two subscribers.
+ raw_.AddSubscriber(&event_subscriber_2);
+
+ CastLoggingEvent event_type = FRAME_DECODED;
+ EventMediaType media_type = VIDEO_EVENT;
+ uint32 frame_id = 456u;
+ RtpTimestamp rtp_timestamp = 123u;
+ base::TimeTicks timestamp = base::TimeTicks();
+ raw_.InsertFrameEvent(timestamp, event_type, media_type,
+ rtp_timestamp, frame_id);
+
+ event_subscriber_.GetPacketEventsAndReset(&packet_events_);
+ EXPECT_TRUE(packet_events_.empty());
+
+ event_subscriber_.GetFrameEventsAndReset(&frame_events_);
+ ASSERT_EQ(1u, frame_events_.size());
+ EXPECT_EQ(rtp_timestamp, frame_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, frame_events_[0].frame_id);
+ EXPECT_EQ(0u, frame_events_[0].size);
+ EXPECT_EQ(timestamp, frame_events_[0].timestamp);
+ EXPECT_EQ(event_type, frame_events_[0].type);
+ EXPECT_EQ(media_type, frame_events_[0].media_type);
+ EXPECT_EQ(base::TimeDelta(), frame_events_[0].delay_delta);
+
+ event_subscriber_2.GetPacketEventsAndReset(&packet_events_);
+ EXPECT_TRUE(packet_events_.empty());
+
+ event_subscriber_2.GetFrameEventsAndReset(&frame_events_);
+ ASSERT_EQ(1u, frame_events_.size());
+ EXPECT_EQ(rtp_timestamp, frame_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, frame_events_[0].frame_id);
+ EXPECT_EQ(0u, frame_events_[0].size);
+ EXPECT_EQ(timestamp, frame_events_[0].timestamp);
+ EXPECT_EQ(event_type, frame_events_[0].type);
+ EXPECT_EQ(media_type, frame_events_[0].media_type);
+ EXPECT_EQ(base::TimeDelta(), frame_events_[0].delay_delta);
+
+ // Remove event_subscriber_2, so it shouldn't receive events after this.
+ raw_.RemoveSubscriber(&event_subscriber_2);
+
+ media_type = AUDIO_EVENT;
+ frame_id = 789;
+ rtp_timestamp = 456;
+ timestamp = base::TimeTicks();
+ raw_.InsertFrameEvent(timestamp, event_type, media_type,
+ rtp_timestamp, frame_id);
+
+ // |event_subscriber_| should still receive events.
+ event_subscriber_.GetFrameEventsAndReset(&frame_events_);
+ ASSERT_EQ(1u, frame_events_.size());
+ EXPECT_EQ(rtp_timestamp, frame_events_[0].rtp_timestamp);
+ EXPECT_EQ(frame_id, frame_events_[0].frame_id);
+ EXPECT_EQ(0u, frame_events_[0].size);
+ EXPECT_EQ(timestamp, frame_events_[0].timestamp);
+ EXPECT_EQ(event_type, frame_events_[0].type);
+ EXPECT_EQ(media_type, frame_events_[0].media_type);
+ EXPECT_EQ(base::TimeDelta(), frame_events_[0].delay_delta);
+
+ event_subscriber_2.GetFrameEventsAndReset(&frame_events_);
+ EXPECT_TRUE(frame_events_.empty());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/logging_stats.cc b/chromium/media/cast/logging/logging_stats.cc
deleted file mode 100644
index 84fdbf7a615..00000000000
--- a/chromium/media/cast/logging/logging_stats.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 "base/memory/linked_ptr.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/cast/logging/logging_stats.h"
-
-namespace media {
-namespace cast {
-
-LoggingStats::LoggingStats(base::TickClock* clock)
- : frame_stats_(),
- packet_stats_(),
- generic_stats_(),
- start_time_(),
- clock_(clock) {
- memset(counts_, 0, sizeof(counts_));
- memset(start_time_, 0, sizeof(start_time_));
-}
-
-LoggingStats::~LoggingStats() {}
-
-void LoggingStats::Reset() {
- frame_stats_.clear();
- packet_stats_.clear();
- generic_stats_.clear();
- memset(counts_, 0, sizeof(counts_));
-}
-
-void LoggingStats::InsertFrameEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id) {
- InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
-}
-
-void LoggingStats::InsertFrameEventWithSize(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- int frame_size) {
- InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
- // Update size.
- FrameStatsMap::iterator it = frame_stats_.find(event);
- DCHECK(it != frame_stats_.end());
- it->second->bitrate_kbps += frame_size;
-}
-
-void LoggingStats::InsertFrameEventWithDelay(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- base::TimeDelta delay) {
- InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
- // Update size.
- FrameStatsMap::iterator it = frame_stats_.find(event);
- DCHECK(it != frame_stats_.end());
- // Using the average delay as a counter, will divide by the counter when
- // triggered.
- it->second->avg_delay_ms += delay.InMilliseconds();
- if (delay.InMilliseconds() > it->second->max_delay_ms)
- it->second->max_delay_ms = delay.InMilliseconds();
- if ((delay.InMilliseconds() < it->second->min_delay_ms) ||
- (counts_[event] == 1) )
- it->second->min_delay_ms = delay.InMilliseconds();
-}
-
-void LoggingStats::InsertBaseFrameEvent(CastLoggingEvent event,
- uint32 frame_id,
- uint32 rtp_timestamp) {
- // Does this belong to an existing event?
- FrameStatsMap::iterator it = frame_stats_.find(event);
- if (it == frame_stats_.end()) {
- // New event.
- start_time_[event] = clock_->NowTicks();
- linked_ptr<FrameLogStats> stats(new FrameLogStats());
- frame_stats_.insert(std::make_pair(event, stats));
- }
-
- ++counts_[event];
-}
-
-void LoggingStats::InsertPacketEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- uint16 packet_id,
- uint16 max_packet_id,
- size_t size) {
- // Does this packet belong to an existing event?
- PacketStatsMap::iterator it = packet_stats_.find(event);
- if (it == packet_stats_.end()) {
- // New event.
- start_time_[event] = clock_->NowTicks();
- packet_stats_.insert(std::make_pair(event, size));
- } else {
- // Add to existing.
- it->second += size;
- }
- ++counts_[event];
-}
-
-void LoggingStats::InsertGenericEvent(CastLoggingEvent event, int value) {
- // Does this event belong to an existing event?
- GenericStatsMap::iterator it = generic_stats_.find(event);
- if (it == generic_stats_.end()) {
- // New event.
- start_time_[event] = clock_->NowTicks();
- generic_stats_.insert(std::make_pair(event, value));
- } else {
- // Add to existing (will be used to compute average).
- it->second += value;
- }
- ++counts_[event];
-}
-
-const FrameStatsMap* LoggingStats::GetFrameStatsData() {
- // Compute framerate and bitrate (when available).
- FrameStatsMap::iterator it;
- for (it = frame_stats_.begin(); it != frame_stats_.end(); ++it) {
- base::TimeDelta time_diff = clock_->NowTicks() - start_time_[it->first];
- it->second->framerate_fps = counts_[it->first] / time_diff.InSecondsF();
- if (it->second->bitrate_kbps > 0) {
- it->second->bitrate_kbps = (8 / 1000) *
- it->second->bitrate_kbps / time_diff.InSecondsF();
- }
- if (it->second->avg_delay_ms > 0)
- it->second->avg_delay_ms /= counts_[it->first];
- }
- return &frame_stats_;
-}
-
-const PacketStatsMap* LoggingStats::GetPacketStatsData() {
- PacketStatsMap::iterator it;
- for (it = packet_stats_.begin(); it != packet_stats_.end(); ++it) {
- if (counts_[it->first] == 0) continue;
- base::TimeDelta time_diff = clock_->NowTicks() - start_time_[it->first];
- it->second = (8 / 1000) * it->second / time_diff.InSecondsF();
- }
- return &packet_stats_;
-}
-
-const GenericStatsMap* LoggingStats::GetGenericStatsData() {
- // Compute averages.
- GenericStatsMap::iterator it;
- for (it = generic_stats_.begin(); it != generic_stats_.end(); ++it) {
- it->second /= counts_[ it->first];
- }
- return &generic_stats_;
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/logging/logging_stats.h b/chromium/media/cast/logging/logging_stats.h
deleted file mode 100644
index f08649cc777..00000000000
--- a/chromium/media/cast/logging/logging_stats.h
+++ /dev/null
@@ -1,75 +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_LOGGING_LOGGING_STATS_H_
-#define MEDIA_CAST_LOGGING_LOGGING_STATS_H_
-
-#include "base/basictypes.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "media/cast/logging/logging_defines.h"
-
-namespace media {
-namespace cast {
-
-class LoggingStats {
- public:
- explicit LoggingStats(base::TickClock* clock);
-
- ~LoggingStats();
-
- void Reset();
-
- void InsertFrameEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id);
-
- void InsertFrameEventWithSize(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- int frame_size);
-
- void InsertFrameEventWithDelay(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- base::TimeDelta delay);
-
- void InsertPacketEvent(CastLoggingEvent event,
- uint32 rtp_timestamp,
- uint32 frame_id,
- uint16 packet_id,
- uint16 max_packet_id,
- size_t size);
-
- void InsertGenericEvent(CastLoggingEvent event, int value);
-
- // Get log stats: some of the values, such as frame rate and bit rates are
- // computed at the time of the call.
- const FrameStatsMap* GetFrameStatsData();
-
- const PacketStatsMap* GetPacketStatsData();
-
- const GenericStatsMap* GetGenericStatsData();
-
- private:
- void InsertBaseFrameEvent(CastLoggingEvent event,
- uint32 frame_id,
- uint32 rtp_timestamp);
- FrameStatsMap frame_stats_;
- PacketStatsMap packet_stats_;
- GenericStatsMap generic_stats_;
- // Every event has an individual start time
- base::TimeTicks start_time_[kNumOfLoggingEvents];
- // Keep track of event counts.
- int counts_[kNumOfLoggingEvents];
- base::TickClock* const clock_; // Not owned by this class.
-
- DISALLOW_COPY_AND_ASSIGN(LoggingStats);
- };
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_LOGGING_LOGGING_STATS_H_
-
diff --git a/chromium/media/cast/logging/logging_unittest.cc b/chromium/media/cast/logging/logging_unittest.cc
deleted file mode 100644
index 5ce760ec4c7..00000000000
--- a/chromium/media/cast/logging/logging_unittest.cc
+++ /dev/null
@@ -1,248 +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 <gtest/gtest.h>
-
-#include "base/rand_util.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "media/cast/logging/logging_impl.h"
-
-
-namespace media {
-namespace cast {
-
- // Insert frame duration- one second.
-const int64 kIntervalTime1S = 1;
-// Test frame rate goal - 30fps.
-const int kFrameIntervalMs = 33;
-
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
-
-class TestLogging : public ::testing::Test {
- protected:
- TestLogging()
- // Enable logging, disable tracing and uma.
- : logging_(&testing_clock_, true, false, false) {
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- }
-
- virtual ~TestLogging() {}
-
- LoggingImpl logging_;
- base::SimpleTestTickClock testing_clock_;
-};
-
-TEST_F(TestLogging, BasicFrameLogging) {
- base::TimeTicks start_time = testing_clock_.NowTicks();
- base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
- uint32 rtp_timestamp = 0;
- uint32 frame_id = 0;
- do {
- logging_.InsertFrameEvent(kAudioFrameCaptured, rtp_timestamp, frame_id);
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
- rtp_timestamp += kFrameIntervalMs * 90;
- ++frame_id;
- time_interval = testing_clock_.NowTicks() - start_time;
- } while (time_interval.InSeconds() < kIntervalTime1S);
- // Get logging data.
- FrameRawMap frame_map = logging_.GetFrameRawData();
- // Size of map should be equal to the number of frames logged.
- EXPECT_EQ(frame_id, frame_map.size());
- // Verify stats.
- const FrameStatsMap* frame_stats = logging_.GetFrameStatsData();
- // Size of stats equals the number of events.
- EXPECT_EQ(1u, frame_stats->size());
- FrameStatsMap::const_iterator it = frame_stats->find(kAudioFrameCaptured);
- EXPECT_TRUE(it != frame_stats->end());
- EXPECT_NEAR(30.3, it->second->framerate_fps, 0.1);
- EXPECT_EQ(0, it->second->bitrate_kbps);
- EXPECT_EQ(0, it->second->max_delay_ms);
- EXPECT_EQ(0, it->second->min_delay_ms);
- EXPECT_EQ(0, it->second->avg_delay_ms);
-}
-
-TEST_F(TestLogging, FrameLoggingWithSize) {
- // Average packet size.
- const int kBaseFrameSizeBytes = 25000;
- const int kRandomSizeInterval = 100;
- base::TimeTicks start_time = testing_clock_.NowTicks();
- base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
- uint32 rtp_timestamp = 0;
- uint32 frame_id = 0;
- do {
- int size = kBaseFrameSizeBytes +
- base::RandInt(-kRandomSizeInterval, kRandomSizeInterval);
- logging_.InsertFrameEventWithSize(
- kAudioFrameCaptured, rtp_timestamp, frame_id, size);
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
- rtp_timestamp += kFrameIntervalMs * 90;
- ++frame_id;
- time_interval = testing_clock_.NowTicks() - start_time;
- } while (time_interval.InSeconds() < kIntervalTime1S);
- // Get logging data.
- FrameRawMap frame_map = logging_.GetFrameRawData();
- // Size of map should be equal to the number of frames logged.
- EXPECT_EQ(frame_id, frame_map.size());
- // Verify stats.
- const FrameStatsMap* frame_stats = logging_.GetFrameStatsData();
- // Size of stats equals the number of events.
- EXPECT_EQ(1u, frame_stats->size());
- FrameStatsMap::const_iterator it = frame_stats->find(kAudioFrameCaptured);
- EXPECT_TRUE(it != frame_stats->end());
- EXPECT_NEAR(30.3, it->second->framerate_fps, 0.1);
- EXPECT_NEAR(8 * kBaseFrameSizeBytes / (kFrameIntervalMs * 1000),
- it->second->bitrate_kbps, kRandomSizeInterval);
- EXPECT_EQ(0, it->second->max_delay_ms);
- EXPECT_EQ(0, it->second->min_delay_ms);
- EXPECT_EQ(0, it->second->avg_delay_ms);
-}
-
-TEST_F(TestLogging, FrameLoggingWithDelay) {
- // Average packet size.
- const int kPlayoutDelayMs = 50;
- const int kRandomSizeInterval = 20;
- base::TimeTicks start_time = testing_clock_.NowTicks();
- base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
- uint32 rtp_timestamp = 0;
- uint32 frame_id = 0;
- do {
- int delay = kPlayoutDelayMs +
- base::RandInt(-kRandomSizeInterval, kRandomSizeInterval);
- logging_.InsertFrameEventWithDelay(
- kAudioFrameCaptured, rtp_timestamp, frame_id,
- base::TimeDelta::FromMilliseconds(delay));
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
- rtp_timestamp += kFrameIntervalMs * 90;
- ++frame_id;
- time_interval = testing_clock_.NowTicks() - start_time;
- } while (time_interval.InSeconds() < kIntervalTime1S);
- // Get logging data.
- FrameRawMap frame_map = logging_.GetFrameRawData();
- // Size of map should be equal to the number of frames logged.
- EXPECT_EQ(frame_id, frame_map.size());
- // Verify stats.
- const FrameStatsMap* frame_stats = logging_.GetFrameStatsData();
- // Size of stats equals the number of events.
- EXPECT_EQ(1u, frame_stats->size());
- FrameStatsMap::const_iterator it = frame_stats->find(kAudioFrameCaptured);
- EXPECT_TRUE(it != frame_stats->end());
- EXPECT_NEAR(30.3, it->second->framerate_fps, 0.1);
- EXPECT_EQ(0, it->second->bitrate_kbps);
- EXPECT_GE(kPlayoutDelayMs + kRandomSizeInterval, it->second->max_delay_ms);
- EXPECT_LE(kPlayoutDelayMs - kRandomSizeInterval, it->second->min_delay_ms);
- EXPECT_NEAR(kPlayoutDelayMs, it->second->avg_delay_ms,
- 0.2 * kRandomSizeInterval);
-}
-
-TEST_F(TestLogging, MultipleEventFrameLogging) {
- base::TimeTicks start_time = testing_clock_.NowTicks();
- base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
- uint32 rtp_timestamp = 0;
- uint32 frame_id = 0;
- do {
- logging_.InsertFrameEvent(kAudioFrameCaptured, rtp_timestamp, frame_id);
- if (frame_id % 2) {
- logging_.InsertFrameEventWithSize(
- kAudioFrameEncoded, rtp_timestamp, frame_id, 1500);
- } else if (frame_id % 3) {
- logging_.InsertFrameEvent(kVideoFrameDecoded, rtp_timestamp, frame_id);
- } else {
- logging_.InsertFrameEventWithDelay(
- kVideoRenderDelay, rtp_timestamp, frame_id,
- base::TimeDelta::FromMilliseconds(20));
- }
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
- rtp_timestamp += kFrameIntervalMs * 90;
- ++frame_id;
- time_interval = testing_clock_.NowTicks() - start_time;
- } while (time_interval.InSeconds() < kIntervalTime1S);
- // Get logging data.
- FrameRawMap frame_map = logging_.GetFrameRawData();
- // Size of map should be equal to the number of frames logged.
- EXPECT_EQ(frame_id, frame_map.size());
- // Multiple events captured per frame.
-}
-
-TEST_F(TestLogging, PacketLogging) {
- const int kNumPacketsPerFrame = 10;
- const int kBaseSize = 2500;
- const int kSizeInterval = 100;
- base::TimeTicks start_time = testing_clock_.NowTicks();
- base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
- uint32 rtp_timestamp = 0;
- uint32 frame_id = 0;
- do {
- for (int i = 0; i < kNumPacketsPerFrame; ++i) {
- int size = kBaseSize + base::RandInt(-kSizeInterval, kSizeInterval);
- logging_.InsertPacketEvent(kPacketSentToPacer, rtp_timestamp, frame_id,
- i, kNumPacketsPerFrame, size);
- }
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
- rtp_timestamp += kFrameIntervalMs * 90;
- ++frame_id;
- time_interval = testing_clock_.NowTicks() - start_time;
- } while (time_interval.InSeconds() < kIntervalTime1S);
- // Get logging data.
- PacketRawMap raw_map = logging_.GetPacketRawData();
- // Size of map should be equal to the number of frames logged.
- EXPECT_EQ(frame_id, raw_map.size());
- // Verify stats.
- const PacketStatsMap* stats_map = logging_.GetPacketStatsData();
- // Size of stats equals the number of events.
- EXPECT_EQ(1u, stats_map->size());
- PacketStatsMap::const_iterator it = stats_map->find(kPacketSentToPacer);
- EXPECT_TRUE(it != stats_map->end());
- // We only store the bitrate as a packet statistic.
- EXPECT_NEAR(8 * kNumPacketsPerFrame * kBaseSize / (kFrameIntervalMs * 1000),
- it->second, kSizeInterval);
-}
-
-TEST_F(TestLogging, GenericLogging) {
- // Insert multiple generic types.
- const int kNumRuns = 1000;
- const int kBaseValue = 20;
- for (int i = 0; i < kNumRuns; ++i) {
- int value = kBaseValue + base::RandInt(-5, 5);
- logging_.InsertGenericEvent(kRtt, value);
- if (i % 2) {
- logging_.InsertGenericEvent(kPacketLoss, value);
- }
- if (!(i % 4)) {
- logging_.InsertGenericEvent(kJitter, value);
- }
- }
- GenericRawMap raw_map = logging_.GetGenericRawData();
- const GenericStatsMap* stats_map = logging_.GetGenericStatsData();
- // Size of generic map = number of different events.
- EXPECT_EQ(3u, raw_map.size());
- EXPECT_EQ(3u, stats_map->size());
- // Raw events - size of internal map = number of calls.
- GenericRawMap::iterator rit = raw_map.find(kRtt);
- EXPECT_EQ(kNumRuns, rit->second.value.size());
- EXPECT_EQ(kNumRuns, rit->second.timestamp.size());
- rit = raw_map.find(kPacketLoss);
- EXPECT_EQ(kNumRuns / 2, rit->second.value.size());
- EXPECT_EQ(kNumRuns / 2, rit->second.timestamp.size());
- rit = raw_map.find(kJitter);
- EXPECT_EQ(kNumRuns / 4, rit->second.value.size());
- EXPECT_EQ(kNumRuns / 4, rit->second.timestamp.size());
- // Stats - one value per event.
- GenericStatsMap::const_iterator sit = stats_map->find(kRtt);
- EXPECT_NEAR(kBaseValue, sit->second, 2.5);
- sit = stats_map->find(kPacketLoss);
- EXPECT_NEAR(kBaseValue, sit->second, 2.5);
- sit = stats_map->find(kJitter);
- EXPECT_NEAR(kBaseValue, sit->second, 2.5);
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/logging/proto/proto_utils.cc b/chromium/media/cast/logging/proto/proto_utils.cc
new file mode 100644
index 00000000000..03251e64c03
--- /dev/null
+++ b/chromium/media/cast/logging/proto/proto_utils.cc
@@ -0,0 +1,36 @@
+// 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/logging/proto/proto_utils.h"
+
+#include "base/logging.h"
+
+#define TO_PROTO_ENUM(enum) \
+ case enum: \
+ return proto::enum
+
+namespace media {
+namespace cast {
+
+proto::EventType ToProtoEventType(CastLoggingEvent event) {
+ switch (event) {
+ TO_PROTO_ENUM(UNKNOWN);
+ TO_PROTO_ENUM(FRAME_CAPTURE_BEGIN);
+ TO_PROTO_ENUM(FRAME_CAPTURE_END);
+ TO_PROTO_ENUM(FRAME_ENCODED);
+ TO_PROTO_ENUM(FRAME_ACK_RECEIVED);
+ TO_PROTO_ENUM(FRAME_ACK_SENT);
+ TO_PROTO_ENUM(FRAME_DECODED);
+ TO_PROTO_ENUM(FRAME_PLAYOUT);
+ TO_PROTO_ENUM(PACKET_SENT_TO_NETWORK);
+ TO_PROTO_ENUM(PACKET_RETRANSMITTED);
+ TO_PROTO_ENUM(PACKET_RTX_REJECTED);
+ TO_PROTO_ENUM(PACKET_RECEIVED);
+ }
+ NOTREACHED();
+ return proto::UNKNOWN;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/proto/proto_utils.h b/chromium/media/cast/logging/proto/proto_utils.h
new file mode 100644
index 00000000000..51232fdb964
--- /dev/null
+++ b/chromium/media/cast/logging/proto/proto_utils.h
@@ -0,0 +1,21 @@
+// 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_LOGGING_PROTO_PROTO_UTILS_H_
+#define MEDIA_CAST_LOGGING_PROTO_PROTO_UTILS_H_
+
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/proto/raw_events.pb.h"
+
+// Utility functions for cast logging protos.
+namespace media {
+namespace cast {
+
+// Converts |event| to a corresponding value in |media::cast::proto::EventType|.
+media::cast::proto::EventType ToProtoEventType(CastLoggingEvent event);
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_PROTO_PROTO_UTILS_H_
diff --git a/chromium/media/cast/logging/proto/raw_events.proto b/chromium/media/cast/logging/proto/raw_events.proto
new file mode 100644
index 00000000000..1d2c537db8f
--- /dev/null
+++ b/chromium/media/cast/logging/proto/raw_events.proto
@@ -0,0 +1,149 @@
+// 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.
+
+// Protocol for audio messages.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package media.cast.proto;
+
+// Keep in sync with media/cast/logging/logging_defines.h.
+// For compatibility reasons, existing values in this enum must not be changed.
+enum EventType {
+ UNKNOWN = 0;
+
+ // Note: 1-28 are deprecated in favor of unified event types. Do not use.
+ // Generic events. No longer used.
+ RTT_MS = 1;
+ PACKET_LOSS = 2;
+ JITTER_MS = 3;
+ VIDEO_ACK_RECEIVED = 4; // Sender side frame event.
+ REMB_BITRATE = 5; // Generic event. No longer used.
+ // Audio receiver.
+ AUDIO_ACK_SENT = 6;
+ // Video receiver.
+ VIDEO_ACK_SENT = 7;
+ // Audio sender.
+ AUDIO_FRAME_CAPTURE_END = 8;
+ AUDIO_FRAME_CAPTURE_BEGIN = 9;
+ AUDIO_FRAME_ENCODED = 10;
+ // Audio receiver.
+ AUDIO_PLAYOUT_DELAY = 11;
+ AUDIO_FRAME_DECODED = 12;
+ // Video sender.
+ VIDEO_FRAME_CAPTURE_BEGIN = 13;
+ VIDEO_FRAME_CAPTURE_END = 14;
+ VIDEO_FRAME_SENT_TO_ENCODER = 15; // Deprecated
+ VIDEO_FRAME_ENCODED = 16;
+ // Video receiver.
+ VIDEO_FRAME_DECODED = 17;
+ VIDEO_RENDER_DELAY = 18;
+ // Send-side packet events.
+ // AUDIO_PACKET_SENT_TO_PACER = 19; // Deprecated
+ // VIDEO_PACKET_SENT_TO_PACER = 20; // Deprecated
+ AUDIO_PACKET_SENT_TO_NETWORK = 21;
+ VIDEO_PACKET_SENT_TO_NETWORK = 22;
+ AUDIO_PACKET_RETRANSMITTED = 23;
+ VIDEO_PACKET_RETRANSMITTED = 24;
+ // Receiver-side packet events.
+ AUDIO_PACKET_RECEIVED = 25;
+ VIDEO_PACKET_RECEIVED = 26;
+ DUPLICATE_AUDIO_PACKET_RECEIVED = 27;
+ DUPLICATE_VIDEO_PACKET_RECEIVED = 28;
+
+
+ // New, unified event types.
+ FRAME_CAPTURE_BEGIN = 29;
+ FRAME_CAPTURE_END = 30;
+ FRAME_ENCODED = 31;
+ FRAME_ACK_RECEIVED = 32;
+ FRAME_ACK_SENT = 33;
+ FRAME_DECODED = 34;
+ FRAME_PLAYOUT = 35;
+ PACKET_SENT_TO_NETWORK = 36;
+ PACKET_RETRANSMITTED = 37;
+ PACKET_RECEIVED = 38;
+ PACKET_RTX_REJECTED = 39;
+}
+
+// Contains information independent of the stream that describes the system
+// setup, e.g. OS and hardware info.
+message GeneralDescription {
+ optional string product = 1;
+ optional string product_version = 2;
+ optional string os = 3;
+}
+
+// Each log will contain one |LogMetadata|.
+message LogMetadata {
+ // |true| if the events are related to audio. |false| if they are related to
+ // video.
+ optional bool is_audio = 1;
+
+ // Used as a reference for all event entries.
+ // i.e. the original RTP timestamp for each event will be
+ // |first_rtp_timestamp| + |relative_rtp_timestamp|.
+ optional uint32 first_rtp_timestamp = 2;
+
+ // Number of AggregatedFrameEvent's.
+ optional int32 num_frame_events = 3;
+
+ // Number of AggregatedPacketEvent's.
+ optional int32 num_packet_events = 4;
+
+ // The internal timestamp value in milliseconds that represents the time
+ // of the Unix epoch. This is used for relating the timestamps in the events
+ // to a real time and date.
+ optional int64 reference_timestamp_ms_at_unix_epoch = 5;
+
+ // Extra data to attach to the log, e.g. experiment tags,
+ // in key-value JSON string format. The data is supplied by the application.
+ optional string extra_data = 6;
+
+ optional GeneralDescription general_description = 7;
+}
+
+message AggregatedFrameEvent {
+ optional uint32 relative_rtp_timestamp = 1;
+
+ repeated EventType event_type = 2 [packed = true];
+
+ // The internal timestamp value in milliseconds. Use
+ // LogMetadata.reference_timestamp_ms_at_unix_epoch to relate to a real time
+ // and date.
+ repeated int64 event_timestamp_ms = 3 [packed = true];
+
+ // Only set if there is a frame encoded event.
+ optional int32 encoded_frame_size = 4;
+
+ // Only set if there is a frame playout event.
+ optional int32 delay_millis = 5;
+
+ // Only set if there is a video frame encoded event.
+ optional bool key_frame = 6;
+
+ // Only set if there is a video frame encoded event.
+ optional int32 target_bitrate = 7;
+};
+
+message BasePacketEvent {
+ optional int32 packet_id = 1;
+ repeated EventType event_type = 2 [packed = true];
+
+ // The internal timestamp value in milliseconds. Use
+ // LogMetadata.reference_timestamp_ms_at_unix_epoch to relate to a real time
+ // and date.
+ repeated int64 event_timestamp_ms = 3 [packed = true];
+
+ // Size of the packet.
+ optional int32 size = 4;
+}
+
+message AggregatedPacketEvent {
+ optional uint32 relative_rtp_timestamp = 1;
+ repeated BasePacketEvent base_packet_event = 2;
+};
+
diff --git a/chromium/media/cast/logging/raw_event_subscriber.h b/chromium/media/cast/logging/raw_event_subscriber.h
new file mode 100644
index 00000000000..b8ebe8c0cde
--- /dev/null
+++ b/chromium/media/cast/logging/raw_event_subscriber.h
@@ -0,0 +1,32 @@
+// 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_LOGGING_RAW_EVENT_SUBSCRIBER_H_
+#define MEDIA_CAST_LOGGING_RAW_EVENT_SUBSCRIBER_H_
+
+#include "media/cast/logging/logging_defines.h"
+
+namespace media {
+namespace cast {
+
+// A subscriber interface to subscribe to cast raw event logs.
+// Those who wish to subscribe to raw event logs must implement this interface,
+// and call LoggingImpl::AddRawEventSubscriber() with the subscriber, in order
+// to start receiving raw event logs.
+class RawEventSubscriber {
+ public:
+ virtual ~RawEventSubscriber() {}
+
+ // Called on main thread when a FrameEvent, given by |frame_event|, is logged.
+ virtual void OnReceiveFrameEvent(const FrameEvent& frame_event) = 0;
+
+ // Called on main thread when a PacketEvent, given by |packet_event|,
+ // is logged.
+ virtual void OnReceivePacketEvent(const PacketEvent& packet_event) = 0;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_RAW_EVENT_SUBSCRIBER_H_
diff --git a/chromium/media/cast/logging/raw_event_subscriber_bundle.cc b/chromium/media/cast/logging/raw_event_subscriber_bundle.cc
new file mode 100644
index 00000000000..1946b6ce82a
--- /dev/null
+++ b/chromium/media/cast/logging/raw_event_subscriber_bundle.cc
@@ -0,0 +1,99 @@
+// 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/logging/raw_event_subscriber_bundle.h"
+
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/receiver_time_offset_estimator_impl.h"
+
+namespace media {
+namespace cast {
+
+RawEventSubscriberBundleForStream::RawEventSubscriberBundleForStream(
+ const scoped_refptr<CastEnvironment>& cast_environment,
+ bool is_audio,
+ ReceiverTimeOffsetEstimator* offset_estimator)
+ : cast_environment_(cast_environment),
+ event_subscriber_(
+ is_audio ? AUDIO_EVENT : VIDEO_EVENT,
+ is_audio ? kMaxAudioEventEntries : kMaxVideoEventEntries),
+ stats_subscriber_(
+ is_audio ? AUDIO_EVENT : VIDEO_EVENT,
+ cast_environment->Clock(), offset_estimator) {
+ cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber_);
+ cast_environment_->Logging()->AddRawEventSubscriber(&stats_subscriber_);
+}
+
+RawEventSubscriberBundleForStream::~RawEventSubscriberBundleForStream() {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber_);
+ cast_environment_->Logging()->RemoveRawEventSubscriber(&stats_subscriber_);
+}
+
+EncodingEventSubscriber*
+RawEventSubscriberBundleForStream::GetEncodingEventSubscriber() {
+ return &event_subscriber_;
+}
+
+StatsEventSubscriber*
+RawEventSubscriberBundleForStream::GetStatsEventSubscriber() {
+ return &stats_subscriber_;
+}
+
+RawEventSubscriberBundle::RawEventSubscriberBundle(
+ const scoped_refptr<CastEnvironment>& cast_environment)
+ : cast_environment_(cast_environment) {}
+
+RawEventSubscriberBundle::~RawEventSubscriberBundle() {
+ if (receiver_offset_estimator_.get()) {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(
+ receiver_offset_estimator_.get());
+ }
+}
+
+void RawEventSubscriberBundle::AddEventSubscribers(bool is_audio) {
+ if (!receiver_offset_estimator_.get()) {
+ receiver_offset_estimator_.reset(
+ new ReceiverTimeOffsetEstimatorImpl);
+ cast_environment_->Logging()->AddRawEventSubscriber(
+ receiver_offset_estimator_.get());
+ }
+ SubscribersMapByStream::iterator it = subscribers_.find(is_audio);
+ if (it != subscribers_.end())
+ return;
+
+ subscribers_.insert(std::make_pair(
+ is_audio,
+ make_linked_ptr(new RawEventSubscriberBundleForStream(
+ cast_environment_, is_audio, receiver_offset_estimator_.get()))));
+}
+
+void RawEventSubscriberBundle::RemoveEventSubscribers(bool is_audio) {
+ SubscribersMapByStream::iterator it = subscribers_.find(is_audio);
+ if (it == subscribers_.end())
+ return;
+
+ subscribers_.erase(it);
+ if (subscribers_.empty()) {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(
+ receiver_offset_estimator_.get());
+ receiver_offset_estimator_.reset();
+ }
+}
+
+EncodingEventSubscriber*
+RawEventSubscriberBundle::GetEncodingEventSubscriber(bool is_audio) {
+ SubscribersMapByStream::iterator it = subscribers_.find(is_audio);
+ return it == subscribers_.end() ?
+ NULL : it->second->GetEncodingEventSubscriber();
+}
+
+StatsEventSubscriber*
+RawEventSubscriberBundle::GetStatsEventSubscriber(bool is_audio) {
+ SubscribersMapByStream::iterator it = subscribers_.find(is_audio);
+ return it == subscribers_.end() ?
+ NULL : it->second->GetStatsEventSubscriber();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/raw_event_subscriber_bundle.h b/chromium/media/cast/logging/raw_event_subscriber_bundle.h
new file mode 100644
index 00000000000..58ab21e6e8a
--- /dev/null
+++ b/chromium/media/cast/logging/raw_event_subscriber_bundle.h
@@ -0,0 +1,84 @@
+// 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_LOGGING_RAW_EVENT_SUBSCRIBER_BUNDLE_H_
+#define MEDIA_CAST_LOGGING_RAW_EVENT_SUBSCRIBER_BUNDLE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/cast/logging/encoding_event_subscriber.h"
+#include "media/cast/logging/stats_event_subscriber.h"
+
+namespace media {
+namespace cast {
+
+class CastEnvironment;
+class ReceiverTimeOffsetEstimator;
+
+// Allow 9MB for serialized video / audio event logs.
+const int kMaxSerializedBytes = 9000000;
+
+// Assume serialized log data for each frame will take up to 150 bytes.
+const int kMaxVideoEventEntries = kMaxSerializedBytes / 150;
+
+// Assume serialized log data for each frame will take up to 75 bytes.
+const int kMaxAudioEventEntries = kMaxSerializedBytes / 75;
+
+// A bundle for raw event subscribers for a single stream.
+// It contains an EncodingEventSubscriber and a StatsSubscriber.
+class RawEventSubscriberBundleForStream {
+ public:
+ RawEventSubscriberBundleForStream(
+ const scoped_refptr<CastEnvironment>& cast_environment,
+ bool is_audio,
+ ReceiverTimeOffsetEstimator* offset_estimator);
+ ~RawEventSubscriberBundleForStream();
+
+ EncodingEventSubscriber* GetEncodingEventSubscriber();
+ StatsEventSubscriber* GetStatsEventSubscriber();
+
+ private:
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ EncodingEventSubscriber event_subscriber_;
+ StatsEventSubscriber stats_subscriber_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawEventSubscriberBundleForStream);
+};
+
+// A bundle of subscribers for all streams. An instance of this object
+// is associated with a CastEnvironment.
+// This class can be used for managing event subscribers
+// in a session where they could be multiple streams (i.e. CastSessionDelegate).
+// It also contains a ReceiverTimeOffsetEstimator that is shared by subscribers
+// of different streams.
+class RawEventSubscriberBundle {
+ public:
+ explicit RawEventSubscriberBundle(
+ const scoped_refptr<CastEnvironment>& cast_environment);
+ ~RawEventSubscriberBundle();
+
+ void AddEventSubscribers(bool is_audio);
+ void RemoveEventSubscribers(bool is_audio);
+ EncodingEventSubscriber* GetEncodingEventSubscriber(
+ bool is_audio);
+ StatsEventSubscriber* GetStatsEventSubscriber(bool is_audio);
+
+ private:
+ // Map from (is_audio) -> RawEventSubscriberBundleForStream.
+ // TODO(imcheng): This works because we only have 1 audio and 1 video stream.
+ // This needs to scale better.
+ typedef std::map<bool, linked_ptr<RawEventSubscriberBundleForStream> >
+ SubscribersMapByStream;
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ SubscribersMapByStream subscribers_;
+ scoped_ptr<ReceiverTimeOffsetEstimator> receiver_offset_estimator_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawEventSubscriberBundle);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_RAW_EVENT_SUBSCRIBER_BUNDLE_H_
+
diff --git a/chromium/media/cast/logging/receiver_time_offset_estimator.h b/chromium/media/cast/logging/receiver_time_offset_estimator.h
new file mode 100644
index 00000000000..5880a8d5ac3
--- /dev/null
+++ b/chromium/media/cast/logging/receiver_time_offset_estimator.h
@@ -0,0 +1,39 @@
+// 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_LOGGING_RECEIVER_TIME_OFFSET_ESTIMATOR_H_
+#define MEDIA_CAST_LOGGING_RECEIVER_TIME_OFFSET_ESTIMATOR_H_
+
+#include "base/time/time.h"
+#include "media/cast/logging/raw_event_subscriber.h"
+
+namespace media {
+namespace cast {
+
+// Estimates receiver time offset based on raw events received.
+// In most cases, the sender and receiver run on different time lines.
+// In order to convert receiver time back to sender time (or vice versa)
+// a certain time offset has to be applied.
+// An implementation of this interface listens to raw events to figure out
+// the bounds for the offset value (assuming the true offset value is constant
+// over the lifetime of a cast session).
+// The offset values provided here should be used as follows:
+// - Convert from sender to receiver time: add offset value to sender timestamp.
+// - Convert from receiver to sender time: subtract offset value from receiver
+// timestamp.
+class ReceiverTimeOffsetEstimator : public RawEventSubscriber {
+ public:
+ virtual ~ReceiverTimeOffsetEstimator() {}
+
+ // If bounds are known, assigns |lower_bound| and |upper_bound| with the
+ // lower bound and upper bound for the offset value, respectively.
+ // Returns true if bounds are known.
+ virtual bool GetReceiverOffsetBounds(base::TimeDelta* lower_bound,
+ base::TimeDelta* upper_bound) = 0;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_RECEIVER_TIME_OFFSET_ESTIMATOR_H_
diff --git a/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc b/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc
new file mode 100644
index 00000000000..44d5eb0b3d7
--- /dev/null
+++ b/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc
@@ -0,0 +1,129 @@
+// 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 <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "media/cast/logging/receiver_time_offset_estimator_impl.h"
+
+namespace media {
+namespace cast {
+
+// This should be large enough so that we can collect all 3 events before
+// the entry gets removed from the map.
+const size_t kMaxEventTimesMapSize = 100;
+
+ReceiverTimeOffsetEstimatorImpl::ReceiverTimeOffsetEstimatorImpl()
+ : bounded_(false) {}
+
+ReceiverTimeOffsetEstimatorImpl::~ReceiverTimeOffsetEstimatorImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void ReceiverTimeOffsetEstimatorImpl::OnReceiveFrameEvent(
+ const FrameEvent& frame_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (frame_event.media_type != VIDEO_EVENT)
+ return;
+
+ CastLoggingEvent event = frame_event.type;
+ if (event != FRAME_ENCODED && event != FRAME_ACK_SENT &&
+ event != FRAME_ACK_RECEIVED)
+ return;
+
+ EventTimesMap::iterator it = event_times_map_.find(frame_event.rtp_timestamp);
+ if (it == event_times_map_.end()) {
+ EventTimes event_times;
+ it = event_times_map_.insert(std::make_pair(frame_event.rtp_timestamp,
+ event_times)).first;
+ }
+ switch (event) {
+ case FRAME_ENCODED:
+ // Encode is supposed to happen only once. If we see duplicate event,
+ // throw away the entry.
+ if (it->second.event_a_time.is_null()) {
+ it->second.event_a_time = frame_event.timestamp;
+ } else {
+ event_times_map_.erase(it);
+ return;
+ }
+ break;
+ case FRAME_ACK_SENT:
+ if (it->second.event_b_time.is_null()) {
+ it->second.event_b_time = frame_event.timestamp;
+ } else if (it->second.event_b_time != frame_event.timestamp) {
+ // Duplicate ack sent events are normal due to RTCP redundancy,
+ // but they must have the same event timestamp.
+ event_times_map_.erase(it);
+ return;
+ }
+ break;
+ case FRAME_ACK_RECEIVED:
+ // If there are duplicate ack received events, pick the one with the
+ // smallest event timestamp so we can get a better bound.
+ if (it->second.event_c_time.is_null()) {
+ it->second.event_c_time = frame_event.timestamp;
+ } else {
+ it->second.event_c_time =
+ std::min(frame_event.timestamp, it->second.event_c_time);
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ if (!it->second.event_a_time.is_null() &&
+ !it->second.event_b_time.is_null() &&
+ !it->second.event_c_time.is_null()) {
+ UpdateOffsetBounds(it->second);
+ event_times_map_.erase(it);
+ }
+
+ // Keep the map size at most |kMaxEventTimesMapSize|.
+ if (event_times_map_.size() > kMaxEventTimesMapSize)
+ event_times_map_.erase(event_times_map_.begin());
+}
+
+bool ReceiverTimeOffsetEstimatorImpl::GetReceiverOffsetBounds(
+ base::TimeDelta* lower_bound,
+ base::TimeDelta* upper_bound) {
+ if (!bounded_)
+ return false;
+
+ *lower_bound = offset_lower_bound_;
+ *upper_bound = offset_upper_bound_;
+ return true;
+}
+
+void ReceiverTimeOffsetEstimatorImpl::OnReceivePacketEvent(
+ const PacketEvent& packet_event) {
+ // Not interested in packet events.
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void ReceiverTimeOffsetEstimatorImpl::UpdateOffsetBounds(
+ const EventTimes& event) {
+ base::TimeDelta lower_bound = event.event_b_time - event.event_c_time;
+ base::TimeDelta upper_bound = event.event_b_time - event.event_a_time;
+
+ if (bounded_) {
+ lower_bound = std::max(lower_bound, offset_lower_bound_);
+ upper_bound = std::min(upper_bound, offset_upper_bound_);
+ }
+
+ if (lower_bound > upper_bound) {
+ VLOG(2) << "Got bogus offset bound values [" << lower_bound.InMilliseconds()
+ << ", " << upper_bound.InMilliseconds() << "].";
+ return;
+ }
+
+ offset_lower_bound_ = lower_bound;
+ offset_upper_bound_ = upper_bound;
+ bounded_ = true;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/receiver_time_offset_estimator_impl.h b/chromium/media/cast/logging/receiver_time_offset_estimator_impl.h
new file mode 100644
index 00000000000..1d0f6c8357f
--- /dev/null
+++ b/chromium/media/cast/logging/receiver_time_offset_estimator_impl.h
@@ -0,0 +1,64 @@
+// 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_LOGGING_RECEIVER_TIME_OFFSET_ESTIMATOR_IMPL_H_
+#define MEDIA_CAST_LOGGING_RECEIVER_TIME_OFFSET_ESTIMATOR_IMPL_H_
+
+#include "base/time/time.h"
+#include "base/threading/thread_checker.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/receiver_time_offset_estimator.h"
+
+namespace media {
+namespace cast {
+
+// This implementation listens to three types of video events:
+// 1. FRAME_ENCODED (sender side)
+// 2. FRAME_ACK_SENT (receiver side)
+// 3. FRAME_ACK_RECEIVED (sender side)
+// There is a causal relationship between these events in that these events
+// must happen in order. This class obtains the lower and upper bounds for
+// the offset by taking the difference of timestamps (2) - (1) and (2) - (3),
+// respectively.
+// The bound will become better as the latency between the events decreases.
+class ReceiverTimeOffsetEstimatorImpl : public ReceiverTimeOffsetEstimator {
+ public:
+ ReceiverTimeOffsetEstimatorImpl();
+
+ virtual ~ReceiverTimeOffsetEstimatorImpl();
+
+ // RawEventSubscriber implementations.
+ virtual void OnReceiveFrameEvent(const FrameEvent& frame_event) OVERRIDE;
+ virtual void OnReceivePacketEvent(const PacketEvent& packet_event) OVERRIDE;
+
+ // ReceiverTimeOffsetEstimator implementation.
+ virtual bool GetReceiverOffsetBounds(base::TimeDelta* lower_bound,
+ base::TimeDelta* upper_bound) OVERRIDE;
+
+ private:
+ struct EventTimes {
+ base::TimeTicks event_a_time;
+ base::TimeTicks event_b_time;
+ base::TimeTicks event_c_time;
+ };
+
+ typedef std::map<RtpTimestamp, EventTimes> EventTimesMap;
+
+ void UpdateOffsetBounds(const EventTimes& event);
+
+ // Fixed size storage to store event times for recent frames.
+ EventTimesMap event_times_map_;
+
+ bool bounded_;
+ base::TimeDelta offset_lower_bound_;
+ base::TimeDelta offset_upper_bound_;
+
+ base::ThreadChecker thread_checker_;
+ DISALLOW_COPY_AND_ASSIGN(ReceiverTimeOffsetEstimatorImpl);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_RECEIVER_TIME_OFFSET_ESTIMATOR_IMPL_H_
diff --git a/chromium/media/cast/logging/receiver_time_offset_estimator_impl_unittest.cc b/chromium/media/cast/logging/receiver_time_offset_estimator_impl_unittest.cc
new file mode 100644
index 00000000000..1cdbecf5de6
--- /dev/null
+++ b/chromium/media/cast/logging/receiver_time_offset_estimator_impl_unittest.cc
@@ -0,0 +1,242 @@
+// 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/receiver_time_offset_estimator_impl.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+class ReceiverTimeOffsetEstimatorImplTest : public ::testing::Test {
+ protected:
+ ReceiverTimeOffsetEstimatorImplTest()
+ : sender_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(sender_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(sender_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)) {
+ cast_environment_->Logging()->AddRawEventSubscriber(&estimator_);
+ }
+
+ virtual ~ReceiverTimeOffsetEstimatorImplTest() {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(&estimator_);
+ }
+
+ void AdvanceClocks(base::TimeDelta time) {
+ sender_clock_->Advance(time);
+ receiver_clock_.Advance(time);
+ }
+
+ base::SimpleTestTickClock* sender_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ base::SimpleTestTickClock receiver_clock_;
+ ReceiverTimeOffsetEstimatorImpl estimator_;
+};
+
+// Suppose the true offset is 100ms.
+// Event A occurred at sender time 20ms.
+// Event B occurred at receiver time 130ms. (sender time 30ms)
+// Event C occurred at sender time 60ms.
+// Then the bound after all 3 events have arrived is [130-60=70, 130-20=110].
+TEST_F(ReceiverTimeOffsetEstimatorImplTest, EstimateOffset) {
+ int64 true_offset_ms = 100;
+ receiver_clock_.Advance(base::TimeDelta::FromMilliseconds(true_offset_ms));
+
+ base::TimeDelta lower_bound;
+ base::TimeDelta upper_bound;
+
+ EXPECT_FALSE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ RtpTimestamp rtp_timestamp = 0;
+ uint32 frame_id = 0;
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(20));
+
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ 1234,
+ true,
+ 5678);
+
+ EXPECT_FALSE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
+ cast_environment_->Logging()->InsertFrameEvent(
+ receiver_clock_.NowTicks(), FRAME_ACK_SENT, VIDEO_EVENT,
+ rtp_timestamp, frame_id);
+
+ EXPECT_FALSE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
+ cast_environment_->Logging()->InsertFrameEvent(
+ sender_clock_->NowTicks(), FRAME_ACK_RECEIVED, VIDEO_EVENT,
+ rtp_timestamp, frame_id);
+
+ EXPECT_TRUE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ int64 lower_bound_ms = lower_bound.InMilliseconds();
+ int64 upper_bound_ms = upper_bound.InMilliseconds();
+ EXPECT_EQ(70, lower_bound_ms);
+ EXPECT_EQ(110, upper_bound_ms);
+ EXPECT_GE(true_offset_ms, lower_bound_ms);
+ EXPECT_LE(true_offset_ms, upper_bound_ms);
+}
+
+// Same scenario as above, but event C arrives before event B. It doens't mean
+// event C occurred before event B.
+TEST_F(ReceiverTimeOffsetEstimatorImplTest, EventCArrivesBeforeEventB) {
+ int64 true_offset_ms = 100;
+ receiver_clock_.Advance(base::TimeDelta::FromMilliseconds(true_offset_ms));
+
+ base::TimeDelta lower_bound;
+ base::TimeDelta upper_bound;
+
+ EXPECT_FALSE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ RtpTimestamp rtp_timestamp = 0;
+ uint32 frame_id = 0;
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(20));
+
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ 1234,
+ true,
+ 5678);
+
+ EXPECT_FALSE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
+ base::TimeTicks event_b_time = receiver_clock_.NowTicks();
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
+ base::TimeTicks event_c_time = sender_clock_->NowTicks();
+
+ cast_environment_->Logging()->InsertFrameEvent(
+ event_c_time, FRAME_ACK_RECEIVED, VIDEO_EVENT, rtp_timestamp, frame_id);
+
+ EXPECT_FALSE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ cast_environment_->Logging()->InsertFrameEvent(
+ event_b_time, FRAME_ACK_SENT, VIDEO_EVENT, rtp_timestamp, frame_id);
+
+ EXPECT_TRUE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+
+ int64 lower_bound_ms = lower_bound.InMilliseconds();
+ int64 upper_bound_ms = upper_bound.InMilliseconds();
+ EXPECT_EQ(70, lower_bound_ms);
+ EXPECT_EQ(110, upper_bound_ms);
+ EXPECT_GE(true_offset_ms, lower_bound_ms);
+ EXPECT_LE(true_offset_ms, upper_bound_ms);
+}
+
+TEST_F(ReceiverTimeOffsetEstimatorImplTest, MultipleIterations) {
+ int64 true_offset_ms = 100;
+ receiver_clock_.Advance(base::TimeDelta::FromMilliseconds(true_offset_ms));
+
+ base::TimeDelta lower_bound;
+ base::TimeDelta upper_bound;
+
+ RtpTimestamp rtp_timestamp_a = 0;
+ int frame_id_a = 0;
+ RtpTimestamp rtp_timestamp_b = 90;
+ int frame_id_b = 1;
+ RtpTimestamp rtp_timestamp_c = 180;
+ int frame_id_c = 2;
+
+ // Frame 1 times: [20, 30+100, 60]
+ // Frame 2 times: [30, 50+100, 55]
+ // Frame 3 times: [77, 80+100, 110]
+ // Bound should end up at [95, 103]
+ // Events times in chronological order: 20, 30 x2, 50, 55, 60, 77, 80, 110
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(20));
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT,
+ rtp_timestamp_a,
+ frame_id_a,
+ 1234,
+ true,
+ 5678);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT,
+ rtp_timestamp_b,
+ frame_id_b,
+ 1234,
+ true,
+ 5678);
+ cast_environment_->Logging()->InsertFrameEvent(
+ receiver_clock_.NowTicks(), FRAME_ACK_SENT, VIDEO_EVENT,
+ rtp_timestamp_a, frame_id_a);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(20));
+ cast_environment_->Logging()->InsertFrameEvent(
+ receiver_clock_.NowTicks(), FRAME_ACK_SENT, VIDEO_EVENT,
+ rtp_timestamp_b, frame_id_b);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(5));
+ cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
+ FRAME_ACK_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp_b,
+ frame_id_b);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(5));
+ cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
+ FRAME_ACK_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp_a,
+ frame_id_a);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(17));
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT,
+ rtp_timestamp_c,
+ frame_id_c,
+ 1234,
+ true,
+ 5678);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(3));
+ cast_environment_->Logging()->InsertFrameEvent(
+ receiver_clock_.NowTicks(), FRAME_ACK_SENT, VIDEO_EVENT,
+ rtp_timestamp_c, frame_id_c);
+
+ AdvanceClocks(base::TimeDelta::FromMilliseconds(30));
+ cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
+ FRAME_ACK_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp_c,
+ frame_id_c);
+
+ EXPECT_TRUE(estimator_.GetReceiverOffsetBounds(&lower_bound, &upper_bound));
+ int64 lower_bound_ms = lower_bound.InMilliseconds();
+ int64 upper_bound_ms = upper_bound.InMilliseconds();
+ EXPECT_EQ(95, lower_bound_ms);
+ EXPECT_EQ(103, upper_bound_ms);
+ EXPECT_GE(true_offset_ms, lower_bound_ms);
+ EXPECT_LE(true_offset_ms, upper_bound_ms);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/serialize_deserialize_test.cc b/chromium/media/cast/logging/serialize_deserialize_test.cc
new file mode 100644
index 00000000000..7e5aa7d3b5e
--- /dev/null
+++ b/chromium/media/cast/logging/serialize_deserialize_test.cc
@@ -0,0 +1,214 @@
+// 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.
+
+// Joint LogSerializer and LogDeserializer testing to make sure they stay in
+// sync.
+
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/logging/log_deserializer.h"
+#include "media/cast/logging/log_serializer.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/proto/proto_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using media::cast::proto::AggregatedFrameEvent;
+using media::cast::proto::AggregatedPacketEvent;
+using media::cast::proto::BasePacketEvent;
+using media::cast::proto::LogMetadata;
+
+namespace {
+
+const media::cast::CastLoggingEvent kVideoFrameEvents[] = {
+ media::cast::FRAME_CAPTURE_BEGIN, media::cast::FRAME_CAPTURE_END,
+ media::cast::FRAME_ENCODED, media::cast::FRAME_DECODED,
+ media::cast::FRAME_PLAYOUT };
+
+const media::cast::CastLoggingEvent kVideoPacketEvents[] = {
+ media::cast::PACKET_SENT_TO_NETWORK, media::cast::PACKET_RECEIVED};
+
+// The frame event fields cycle through these numbers.
+const int kEncodedFrameSize[] = {512, 425, 399, 400, 237};
+const int kDelayMillis[] = {15, 4, 8, 42, 23, 16};
+
+const int kMaxSerializedBytes = 10000;
+
+}
+
+namespace media {
+namespace cast {
+
+class SerializeDeserializeTest : public ::testing::Test {
+ protected:
+ SerializeDeserializeTest()
+ : serialized_(new char[kMaxSerializedBytes]), output_bytes_(0) {}
+
+ virtual ~SerializeDeserializeTest() {}
+
+ void Init() {
+ metadata_.set_first_rtp_timestamp(12345678 * 90);
+ metadata_.set_is_audio(false);
+ metadata_.set_num_frame_events(10);
+ metadata_.set_num_packet_events(10);
+
+ int64 event_time_ms = 0;
+ // Insert frame and packet events with RTP timestamps 0, 90, 180, ...
+ for (int i = 0; i < metadata_.num_frame_events(); i++) {
+ linked_ptr<AggregatedFrameEvent> frame_event(new AggregatedFrameEvent);
+ frame_event->set_relative_rtp_timestamp(i * 90);
+ for (uint32 event_index = 0; event_index < arraysize(kVideoFrameEvents);
+ ++event_index) {
+ frame_event->add_event_type(
+ ToProtoEventType(kVideoFrameEvents[event_index]));
+ frame_event->add_event_timestamp_ms(event_time_ms);
+ event_time_ms += 1024;
+ }
+ frame_event->set_encoded_frame_size(
+ kEncodedFrameSize[i % arraysize(kEncodedFrameSize)]);
+ frame_event->set_delay_millis(kDelayMillis[i % arraysize(kDelayMillis)]);
+
+ frame_event_list_.push_back(frame_event);
+ }
+
+ event_time_ms = 0;
+ int packet_id = 0;
+ for (int i = 0; i < metadata_.num_packet_events(); i++) {
+ linked_ptr<AggregatedPacketEvent> packet_event(new AggregatedPacketEvent);
+ packet_event->set_relative_rtp_timestamp(i * 90);
+ for (int j = 0; j < 10; j++) {
+ BasePacketEvent* base_event = packet_event->add_base_packet_event();
+ base_event->set_packet_id(packet_id);
+ packet_id++;
+ for (uint32 event_index = 0;
+ event_index < arraysize(kVideoPacketEvents);
+ ++event_index) {
+ base_event->add_event_type(
+ ToProtoEventType(kVideoPacketEvents[event_index]));
+ base_event->add_event_timestamp_ms(event_time_ms);
+ event_time_ms += 256;
+ }
+ }
+ packet_event_list_.push_back(packet_event);
+ }
+ }
+
+ void Verify(const DeserializedLog& video_log) {
+ const LogMetadata& returned_metadata = video_log.metadata;
+ const FrameEventMap& returned_frame_events = video_log.frame_events;
+ const PacketEventMap& returned_packet_events = video_log.packet_events;
+
+ EXPECT_EQ(metadata_.SerializeAsString(),
+ returned_metadata.SerializeAsString());
+
+ // Check that the returned map is equal to the original map.
+ EXPECT_EQ(frame_event_list_.size(), returned_frame_events.size());
+ for (FrameEventMap::const_iterator frame_it = returned_frame_events.begin();
+ frame_it != returned_frame_events.end();
+ ++frame_it) {
+ FrameEventList::iterator original_it = frame_event_list_.begin();
+ ASSERT_NE(frame_event_list_.end(), original_it);
+ // Compare protos by serializing and checking the bytes.
+ EXPECT_EQ((*original_it)->SerializeAsString(),
+ frame_it->second->SerializeAsString());
+ frame_event_list_.erase(frame_event_list_.begin());
+ }
+ EXPECT_TRUE(frame_event_list_.empty());
+
+ EXPECT_EQ(packet_event_list_.size(), returned_packet_events.size());
+ for (PacketEventMap::const_iterator packet_it =
+ returned_packet_events.begin();
+ packet_it != returned_packet_events.end();
+ ++packet_it) {
+ PacketEventList::iterator original_it = packet_event_list_.begin();
+ ASSERT_NE(packet_event_list_.end(), original_it);
+ // Compare protos by serializing and checking the bytes.
+ EXPECT_EQ((*original_it)->SerializeAsString(),
+ packet_it->second->SerializeAsString());
+ packet_event_list_.erase(packet_event_list_.begin());
+ }
+ EXPECT_TRUE(packet_event_list_.empty());
+ }
+
+ LogMetadata metadata_;
+ FrameEventList frame_event_list_;
+ PacketEventList packet_event_list_;
+ scoped_ptr<char[]> serialized_;
+ int output_bytes_;
+};
+
+TEST_F(SerializeDeserializeTest, Uncompressed) {
+ bool compressed = false;
+ Init();
+
+ bool success = SerializeEvents(metadata_,
+ frame_event_list_,
+ packet_event_list_,
+ compressed,
+ kMaxSerializedBytes,
+ serialized_.get(),
+ &output_bytes_);
+ ASSERT_TRUE(success);
+ ASSERT_GT(output_bytes_, 0);
+
+ DeserializedLog audio_log;
+ DeserializedLog video_log;
+ success = DeserializeEvents(
+ serialized_.get(), output_bytes_, compressed, &audio_log, &video_log);
+ ASSERT_TRUE(success);
+
+ Verify(video_log);
+}
+
+TEST_F(SerializeDeserializeTest, UncompressedInsufficientSpace) {
+ bool compressed = false;
+ Init();
+ serialized_.reset(new char[100]);
+ bool success = SerializeEvents(metadata_,
+ frame_event_list_,
+ packet_event_list_,
+ compressed,
+ 100,
+ serialized_.get(),
+ &output_bytes_);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(0, output_bytes_);
+}
+
+TEST_F(SerializeDeserializeTest, Compressed) {
+ bool compressed = true;
+ Init();
+ bool success = SerializeEvents(metadata_,
+ frame_event_list_,
+ packet_event_list_,
+ compressed,
+ kMaxSerializedBytes,
+ serialized_.get(),
+ &output_bytes_);
+ ASSERT_TRUE(success);
+ ASSERT_GT(output_bytes_, 0);
+
+ DeserializedLog audio_log;
+ DeserializedLog video_log;
+ success = DeserializeEvents(
+ serialized_.get(), output_bytes_, compressed, &audio_log, &video_log);
+ ASSERT_TRUE(success);
+ Verify(video_log);
+}
+
+TEST_F(SerializeDeserializeTest, CompressedInsufficientSpace) {
+ bool compressed = true;
+ Init();
+ serialized_.reset(new char[100]);
+ bool success = SerializeEvents(metadata_,
+ frame_event_list_,
+ packet_event_list_,
+ compressed,
+ 100,
+ serialized_.get(),
+ &output_bytes_);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(0, output_bytes_);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/simple_event_subscriber.cc b/chromium/media/cast/logging/simple_event_subscriber.cc
new file mode 100644
index 00000000000..984d8f7d830
--- /dev/null
+++ b/chromium/media/cast/logging/simple_event_subscriber.cc
@@ -0,0 +1,46 @@
+// 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/logging/simple_event_subscriber.h"
+
+#include <vector>
+
+#include "base/logging.h"
+
+namespace media {
+namespace cast {
+
+SimpleEventSubscriber::SimpleEventSubscriber() {}
+
+SimpleEventSubscriber::~SimpleEventSubscriber() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void SimpleEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ frame_events_.push_back(frame_event);
+}
+
+void SimpleEventSubscriber::OnReceivePacketEvent(
+ const PacketEvent& packet_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ packet_events_.push_back(packet_event);
+}
+
+void SimpleEventSubscriber::GetFrameEventsAndReset(
+ std::vector<FrameEvent>* frame_events) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ frame_events->swap(frame_events_);
+ frame_events_.clear();
+}
+
+void SimpleEventSubscriber::GetPacketEventsAndReset(
+ std::vector<PacketEvent>* packet_events) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ packet_events->swap(packet_events_);
+ packet_events_.clear();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/simple_event_subscriber.h b/chromium/media/cast/logging/simple_event_subscriber.h
new file mode 100644
index 00000000000..adc4763f5f4
--- /dev/null
+++ b/chromium/media/cast/logging/simple_event_subscriber.h
@@ -0,0 +1,52 @@
+// 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_LOGGING_SIMPLE_EVENT_SUBSCRIBER_H_
+#define MEDIA_CAST_LOGGING_SIMPLE_EVENT_SUBSCRIBER_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/threading/thread_checker.h"
+#include "media/cast/logging/raw_event_subscriber.h"
+
+namespace media {
+namespace cast {
+
+// RawEventSubscriber implementation that records all incoming raw events
+// in std::vector's.
+// The user of this class can call the GetXXXEventsAndReset functions to get
+// list of events that have acccumulated since last inovcation.
+class SimpleEventSubscriber : public RawEventSubscriber {
+ public:
+ SimpleEventSubscriber();
+
+ virtual ~SimpleEventSubscriber();
+
+ // RawEventSubscriber implementations.
+ virtual void OnReceiveFrameEvent(const FrameEvent& frame_event) OVERRIDE;
+ virtual void OnReceivePacketEvent(const PacketEvent& packet_event) OVERRIDE;
+
+ // Assigns frame events received so far to |frame_events| and clears them
+ // from this object.
+ void GetFrameEventsAndReset(std::vector<FrameEvent>* frame_events);
+
+ // Assigns packet events received so far to |packet_events| and clears them
+ // from this object.
+ void GetPacketEventsAndReset(std::vector<PacketEvent>* packet_events);
+
+ private:
+ std::vector<FrameEvent> frame_events_;
+ std::vector<PacketEvent> packet_events_;
+
+ // All functions must be called on the main thread.
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleEventSubscriber);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_SIMPLE_EVENT_SUBSCRIBER_H_
diff --git a/chromium/media/cast/logging/simple_event_subscriber_unittest.cc b/chromium/media/cast/logging/simple_event_subscriber_unittest.cc
new file mode 100644
index 00000000000..311a2341951
--- /dev/null
+++ b/chromium/media/cast/logging/simple_event_subscriber_unittest.cc
@@ -0,0 +1,87 @@
+// 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+class SimpleEventSubscriberTest : public ::testing::Test {
+ protected:
+ SimpleEventSubscriberTest()
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)) {
+ cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber_);
+ }
+
+ virtual ~SimpleEventSubscriberTest() {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber_);
+ }
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ SimpleEventSubscriber event_subscriber_;
+};
+
+TEST_F(SimpleEventSubscriberTest, GetAndResetEvents) {
+ // Log some frame events.
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ testing_clock_->NowTicks(), FRAME_ENCODED, AUDIO_EVENT,
+ /*rtp_timestamp*/ 100u, /*frame_id*/ 0u, /*frame_size*/ 123,
+ /*key_frame*/ false, 0);
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ testing_clock_->NowTicks(), FRAME_PLAYOUT, AUDIO_EVENT,
+ /*rtp_timestamp*/ 100u,
+ /*frame_id*/ 0u, /*delay*/ base::TimeDelta::FromMilliseconds(100));
+ cast_environment_->Logging()->InsertFrameEvent(
+ testing_clock_->NowTicks(), FRAME_DECODED, AUDIO_EVENT,
+ /*rtp_timestamp*/ 200u,
+ /*frame_id*/ 0u);
+
+ // Log some packet events.
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(), PACKET_RECEIVED, AUDIO_EVENT,
+ /*rtp_timestamp*/ 200u,
+ /*frame_id*/ 0u, /*packet_id*/ 1u, /*max_packet_id*/ 5u, /*size*/ 100u);
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(), FRAME_DECODED, VIDEO_EVENT,
+ /*rtp_timestamp*/ 200u, /*frame_id*/ 0u, /*packet_id*/ 1u,
+ /*max_packet_id*/ 5u, /*size*/ 100u);
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(), FRAME_DECODED, VIDEO_EVENT,
+ /*rtp_timestamp*/ 300u, /*frame_id*/ 0u, /*packet_id*/ 1u,
+ /*max_packet_id*/ 5u, /*size*/ 100u);
+
+ std::vector<FrameEvent> frame_events;
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ EXPECT_EQ(3u, frame_events.size());
+
+ std::vector<PacketEvent> packet_events;
+ event_subscriber_.GetPacketEventsAndReset(&packet_events);
+ EXPECT_EQ(3u, packet_events.size());
+
+ // Calling this function again should result in empty vector because no events
+ // were logged since last call.
+ event_subscriber_.GetFrameEventsAndReset(&frame_events);
+ event_subscriber_.GetPacketEventsAndReset(&packet_events);
+ EXPECT_TRUE(frame_events.empty());
+ EXPECT_TRUE(packet_events.empty());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/stats_event_subscriber.cc b/chromium/media/cast/logging/stats_event_subscriber.cc
new file mode 100644
index 00000000000..9e3226a2161
--- /dev/null
+++ b/chromium/media/cast/logging/stats_event_subscriber.cc
@@ -0,0 +1,400 @@
+// 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/logging/stats_event_subscriber.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+
+#define STAT_ENUM_TO_STRING(enum) \
+ case enum: \
+ return #enum
+
+namespace media {
+namespace cast {
+
+namespace {
+
+using media::cast::CastLoggingEvent;
+using media::cast::EventMediaType;
+
+const size_t kMaxFrameEventTimeMapSize = 100;
+const size_t kMaxPacketEventTimeMapSize = 1000;
+
+bool IsReceiverEvent(CastLoggingEvent event) {
+ return event == FRAME_DECODED
+ || event == FRAME_PLAYOUT
+ || event == FRAME_ACK_SENT
+ || event == PACKET_RECEIVED;
+}
+
+} // namespace
+
+StatsEventSubscriber::StatsEventSubscriber(
+ EventMediaType event_media_type,
+ base::TickClock* clock,
+ ReceiverTimeOffsetEstimator* offset_estimator)
+ : event_media_type_(event_media_type),
+ clock_(clock),
+ offset_estimator_(offset_estimator),
+ network_latency_datapoints_(0),
+ e2e_latency_datapoints_(0) {
+ DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT);
+ base::TimeTicks now = clock_->NowTicks();
+ start_time_ = now;
+ last_response_received_time_ = base::TimeTicks();
+}
+
+StatsEventSubscriber::~StatsEventSubscriber() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ CastLoggingEvent type = frame_event.type;
+ if (frame_event.media_type != event_media_type_)
+ return;
+
+ FrameStatsMap::iterator it = frame_stats_.find(type);
+ if (it == frame_stats_.end()) {
+ FrameLogStats stats;
+ stats.event_counter = 1;
+ stats.sum_size = frame_event.size;
+ stats.sum_delay = frame_event.delay_delta;
+ frame_stats_.insert(std::make_pair(type, stats));
+ } else {
+ ++(it->second.event_counter);
+ it->second.sum_size += frame_event.size;
+ it->second.sum_delay += frame_event.delay_delta;
+ }
+
+ if (type == FRAME_CAPTURE_BEGIN) {
+ RecordFrameCapturedTime(frame_event);
+ } else if (type == FRAME_PLAYOUT) {
+ RecordE2ELatency(frame_event);
+ }
+
+ if (IsReceiverEvent(type))
+ UpdateLastResponseTime(frame_event.timestamp);
+}
+
+void StatsEventSubscriber::OnReceivePacketEvent(
+ const PacketEvent& packet_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ CastLoggingEvent type = packet_event.type;
+ if (packet_event.media_type != event_media_type_)
+ return;
+
+ PacketStatsMap::iterator it = packet_stats_.find(type);
+ if (it == packet_stats_.end()) {
+ PacketLogStats stats;
+ stats.event_counter = 1;
+ stats.sum_size = packet_event.size;
+ packet_stats_.insert(std::make_pair(type, stats));
+ } else {
+ ++(it->second.event_counter);
+ it->second.sum_size += packet_event.size;
+ }
+
+ if (type == PACKET_SENT_TO_NETWORK ||
+ type == PACKET_RECEIVED) {
+ RecordNetworkLatency(packet_event);
+ } else if (type == PACKET_RETRANSMITTED) {
+ // We only measure network latency using packets that doesn't have to be
+ // retransmitted as there is precisely one sent-receive timestamp pairs.
+ ErasePacketSentTime(packet_event);
+ }
+
+ if (IsReceiverEvent(type))
+ UpdateLastResponseTime(packet_event.timestamp);
+}
+
+scoped_ptr<base::DictionaryValue> StatsEventSubscriber::GetStats() const {
+ StatsMap stats_map;
+ GetStatsInternal(&stats_map);
+ scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue);
+
+ scoped_ptr<base::DictionaryValue> stats(new base::DictionaryValue);
+ for (StatsMap::const_iterator it = stats_map.begin(); it != stats_map.end();
+ ++it) {
+ stats->SetDouble(CastStatToString(it->first), it->second);
+ }
+
+ ret->Set(event_media_type_ == AUDIO_EVENT ? "audio" : "video",
+ stats.release());
+
+ return ret.Pass();
+}
+
+void StatsEventSubscriber::Reset() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ frame_stats_.clear();
+ packet_stats_.clear();
+ total_network_latency_ = base::TimeDelta();
+ network_latency_datapoints_ = 0;
+ total_e2e_latency_ = base::TimeDelta();
+ e2e_latency_datapoints_ = 0;
+ frame_captured_times_.clear();
+ packet_sent_times_.clear();
+ start_time_ = clock_->NowTicks();
+ last_response_received_time_ = base::TimeTicks();
+}
+
+// static
+const char* StatsEventSubscriber::CastStatToString(CastStat stat) {
+ switch (stat) {
+ STAT_ENUM_TO_STRING(CAPTURE_FPS);
+ STAT_ENUM_TO_STRING(ENCODE_FPS);
+ STAT_ENUM_TO_STRING(DECODE_FPS);
+ STAT_ENUM_TO_STRING(AVG_ENCODE_TIME_MS);
+ STAT_ENUM_TO_STRING(AVG_PLAYOUT_DELAY_MS);
+ STAT_ENUM_TO_STRING(AVG_NETWORK_LATENCY_MS);
+ STAT_ENUM_TO_STRING(AVG_E2E_LATENCY_MS);
+ STAT_ENUM_TO_STRING(ENCODE_KBPS);
+ STAT_ENUM_TO_STRING(TRANSMISSION_KBPS);
+ STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS);
+ STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION);
+ STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE);
+ }
+ NOTREACHED();
+ return "";
+}
+
+void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ stats_map->clear();
+
+ base::TimeTicks end_time = clock_->NowTicks();
+
+ PopulateFpsStat(
+ end_time, FRAME_CAPTURE_BEGIN, CAPTURE_FPS, stats_map);
+ PopulateFpsStat(
+ end_time, FRAME_ENCODED, ENCODE_FPS, stats_map);
+ PopulateFpsStat(
+ end_time, FRAME_DECODED, DECODE_FPS, stats_map);
+ PopulatePlayoutDelayStat(stats_map);
+ PopulateFrameBitrateStat(end_time, stats_map);
+ PopulatePacketBitrateStat(end_time,
+ PACKET_SENT_TO_NETWORK,
+ TRANSMISSION_KBPS,
+ stats_map);
+ PopulatePacketBitrateStat(end_time,
+ PACKET_RETRANSMITTED,
+ RETRANSMISSION_KBPS,
+ stats_map);
+ PopulatePacketLossPercentageStat(stats_map);
+
+ if (network_latency_datapoints_ > 0) {
+ double avg_network_latency_ms =
+ total_network_latency_.InMillisecondsF() /
+ network_latency_datapoints_;
+ stats_map->insert(
+ std::make_pair(AVG_NETWORK_LATENCY_MS, avg_network_latency_ms));
+ }
+
+ if (e2e_latency_datapoints_ > 0) {
+ double avg_e2e_latency_ms =
+ total_e2e_latency_.InMillisecondsF() / e2e_latency_datapoints_;
+ stats_map->insert(std::make_pair(AVG_E2E_LATENCY_MS, avg_e2e_latency_ms));
+ }
+
+ if (!last_response_received_time_.is_null()) {
+ stats_map->insert(
+ std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE,
+ (end_time - last_response_received_time_).InMillisecondsF()));
+ }
+}
+
+bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) {
+ base::TimeDelta receiver_offset_lower_bound;
+ base::TimeDelta receiver_offset_upper_bound;
+ if (!offset_estimator_->GetReceiverOffsetBounds(
+ &receiver_offset_lower_bound, &receiver_offset_upper_bound)) {
+ return false;
+ }
+
+ *offset = (receiver_offset_lower_bound + receiver_offset_upper_bound) / 2;
+ return true;
+}
+
+void StatsEventSubscriber::RecordFrameCapturedTime(
+ const FrameEvent& frame_event) {
+ frame_captured_times_.insert(
+ std::make_pair(frame_event.rtp_timestamp, frame_event.timestamp));
+ if (frame_captured_times_.size() > kMaxFrameEventTimeMapSize)
+ frame_captured_times_.erase(frame_captured_times_.begin());
+}
+
+void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) {
+ base::TimeDelta receiver_offset;
+ if (!GetReceiverOffset(&receiver_offset))
+ return;
+
+ FrameEventTimeMap::iterator it =
+ frame_captured_times_.find(frame_event.rtp_timestamp);
+ if (it == frame_captured_times_.end())
+ return;
+
+ // Playout time is event time + playout delay.
+ base::TimeTicks playout_time =
+ frame_event.timestamp + frame_event.delay_delta - receiver_offset;
+ total_e2e_latency_ += playout_time - it->second;
+ e2e_latency_datapoints_++;
+}
+
+void StatsEventSubscriber::UpdateLastResponseTime(
+ base::TimeTicks receiver_time) {
+ base::TimeDelta receiver_offset;
+ if (!GetReceiverOffset(&receiver_offset))
+ return;
+ base::TimeTicks sender_time = receiver_time - receiver_offset;
+ last_response_received_time_ = sender_time;
+}
+
+void StatsEventSubscriber::ErasePacketSentTime(
+ const PacketEvent& packet_event) {
+ std::pair<RtpTimestamp, uint16> key(
+ std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
+ packet_sent_times_.erase(key);
+}
+
+void StatsEventSubscriber::RecordNetworkLatency(
+ const PacketEvent& packet_event) {
+ base::TimeDelta receiver_offset;
+ if (!GetReceiverOffset(&receiver_offset))
+ return;
+
+ std::pair<RtpTimestamp, uint16> key(
+ std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
+ PacketEventTimeMap::iterator it = packet_sent_times_.find(key);
+ if (it == packet_sent_times_.end()) {
+ std::pair<RtpTimestamp, uint16> key(
+ std::make_pair(packet_event.rtp_timestamp, packet_event.packet_id));
+ std::pair<base::TimeTicks, CastLoggingEvent> value =
+ std::make_pair(packet_event.timestamp, packet_event.type);
+ packet_sent_times_.insert(std::make_pair(key, value));
+ if (packet_sent_times_.size() > kMaxPacketEventTimeMapSize)
+ packet_sent_times_.erase(packet_sent_times_.begin());
+ } else {
+ std::pair<base::TimeTicks, CastLoggingEvent> value = it->second;
+ CastLoggingEvent recorded_type = value.second;
+ bool match = false;
+ base::TimeTicks packet_sent_time;
+ base::TimeTicks packet_received_time;
+ if (recorded_type == PACKET_SENT_TO_NETWORK &&
+ packet_event.type == PACKET_RECEIVED) {
+ packet_sent_time = value.first;
+ packet_received_time = packet_event.timestamp;
+ match = true;
+ } else if (recorded_type == PACKET_RECEIVED &&
+ packet_event.type == PACKET_SENT_TO_NETWORK) {
+ packet_sent_time = packet_event.timestamp;
+ packet_received_time = value.first;
+ match = true;
+ }
+ if (match) {
+ // Subtract by offset.
+ packet_received_time -= receiver_offset;
+
+ total_network_latency_ += packet_received_time - packet_sent_time;
+ network_latency_datapoints_++;
+ packet_sent_times_.erase(it);
+ }
+ }
+}
+
+void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time,
+ CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const {
+ FrameStatsMap::const_iterator it = frame_stats_.find(event);
+ if (it != frame_stats_.end()) {
+ double fps = 0.0;
+ base::TimeDelta duration = (end_time - start_time_);
+ int count = it->second.event_counter;
+ if (duration > base::TimeDelta())
+ fps = count / duration.InSecondsF();
+ stats_map->insert(std::make_pair(stat, fps));
+ }
+}
+
+void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const {
+ FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_PLAYOUT);
+ if (it != frame_stats_.end()) {
+ double avg_delay_ms = 0.0;
+ base::TimeDelta sum_delay = it->second.sum_delay;
+ int count = it->second.event_counter;
+ if (count != 0)
+ avg_delay_ms = sum_delay.InMillisecondsF() / count;
+ stats_map->insert(std::make_pair(AVG_PLAYOUT_DELAY_MS, avg_delay_ms));
+ }
+}
+
+void StatsEventSubscriber::PopulateFrameBitrateStat(base::TimeTicks end_time,
+ StatsMap* stats_map) const {
+ FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_ENCODED);
+ if (it != frame_stats_.end()) {
+ double kbps = 0.0;
+ base::TimeDelta duration = end_time - start_time_;
+ if (duration > base::TimeDelta()) {
+ kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
+ }
+
+ stats_map->insert(std::make_pair(ENCODE_KBPS, kbps));
+ }
+}
+
+void StatsEventSubscriber::PopulatePacketBitrateStat(
+ base::TimeTicks end_time,
+ CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const {
+ PacketStatsMap::const_iterator it = packet_stats_.find(event);
+ if (it != packet_stats_.end()) {
+ double kbps = 0;
+ base::TimeDelta duration = end_time - start_time_;
+ if (duration > base::TimeDelta()) {
+ kbps = it->second.sum_size / duration.InMillisecondsF() * 8;
+ }
+
+ stats_map->insert(std::make_pair(stat, kbps));
+ }
+}
+
+void StatsEventSubscriber::PopulatePacketLossPercentageStat(
+ StatsMap* stats_map) const {
+ // We assume that retransmission means that the packet's previous
+ // (re)transmission was lost.
+ // This means the percentage of packet loss is
+ // (# of retransmit events) / (# of transmit + retransmit events).
+ PacketStatsMap::const_iterator sent_it =
+ packet_stats_.find(PACKET_SENT_TO_NETWORK);
+ if (sent_it == packet_stats_.end())
+ return;
+ PacketStatsMap::const_iterator retransmitted_it =
+ packet_stats_.find(PACKET_RETRANSMITTED);
+ int sent_count = sent_it->second.event_counter;
+ int retransmitted_count = 0;
+ if (retransmitted_it != packet_stats_.end())
+ retransmitted_count = retransmitted_it->second.event_counter;
+ double packet_loss_fraction = static_cast<double>(retransmitted_count) /
+ (sent_count + retransmitted_count);
+ stats_map->insert(
+ std::make_pair(PACKET_LOSS_FRACTION, packet_loss_fraction));
+}
+
+StatsEventSubscriber::FrameLogStats::FrameLogStats()
+ : event_counter(0), sum_size(0) {}
+StatsEventSubscriber::FrameLogStats::~FrameLogStats() {}
+
+StatsEventSubscriber::PacketLogStats::PacketLogStats()
+ : event_counter(0), sum_size(0) {}
+StatsEventSubscriber::PacketLogStats::~PacketLogStats() {}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/logging/stats_event_subscriber.h b/chromium/media/cast/logging/stats_event_subscriber.h
new file mode 100644
index 00000000000..173378ab0b2
--- /dev/null
+++ b/chromium/media/cast/logging/stats_event_subscriber.h
@@ -0,0 +1,176 @@
+// 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_LOGGING_STATS_EVENT_SUBSCRIBER_H_
+#define MEDIA_CAST_LOGGING_STATS_EVENT_SUBSCRIBER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/raw_event_subscriber.h"
+#include "media/cast/logging/receiver_time_offset_estimator.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace media {
+namespace cast {
+
+class StatsEventSubscriberTest;
+
+// A RawEventSubscriber implementation that subscribes to events,
+// and aggregates them into stats.
+class StatsEventSubscriber : public RawEventSubscriber {
+ public:
+ StatsEventSubscriber(EventMediaType event_media_type,
+ base::TickClock* clock,
+ ReceiverTimeOffsetEstimator* offset_estimator);
+
+ virtual ~StatsEventSubscriber();
+
+ // RawReventSubscriber implementations.
+ virtual void OnReceiveFrameEvent(const FrameEvent& frame_event) OVERRIDE;
+ virtual void OnReceivePacketEvent(const PacketEvent& packet_event) OVERRIDE;
+
+ // Returns stats as a DictionaryValue. The dictionary contains one entry -
+ // "audio" or "video" pointing to an inner dictionary.
+ // The inner dictionary consists of string - double entries, where the string
+ // describes the name of the stat, and the double describes
+ // the value of the stat. See CastStat and StatsMap below.
+ scoped_ptr<base::DictionaryValue> GetStats() const;
+
+ // Resets stats in this object.
+ void Reset();
+
+ private:
+ friend class StatsEventSubscriberTest;
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, EmptyStats);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Capture);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Encode);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Decode);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, PlayoutDelay);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, E2ELatency);
+ FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Packets);
+
+ // Generic statistics given the raw data. More specific data (e.g. frame rate
+ // and bit rate) can be computed given the basic metrics.
+ // Some of the metrics will only be set when applicable, e.g. delay and size.
+ struct FrameLogStats {
+ FrameLogStats();
+ ~FrameLogStats();
+ int event_counter;
+ size_t sum_size;
+ base::TimeDelta sum_delay;
+ };
+
+ struct PacketLogStats {
+ PacketLogStats();
+ ~PacketLogStats();
+ int event_counter;
+ size_t sum_size;
+ };
+
+ enum CastStat {
+ // Capture frame rate.
+ CAPTURE_FPS,
+ // Encode frame rate.
+ ENCODE_FPS,
+ // Decode frame rate.
+ DECODE_FPS,
+ // Average encode duration in milliseconds.
+ // TODO(imcheng): This stat is not populated yet because we do not have
+ // the time when encode started. Record it in FRAME_ENCODED event.
+ AVG_ENCODE_TIME_MS,
+ // Average playout delay in milliseconds, with target delay already
+ // accounted for. Ideally, every frame should have a playout delay of 0.
+ AVG_PLAYOUT_DELAY_MS,
+ // Duration from when a packet is transmitted to when it is received.
+ // This measures latency from sender to receiver.
+ AVG_NETWORK_LATENCY_MS,
+ // Duration from when a frame is captured to when it should be played out.
+ AVG_E2E_LATENCY_MS,
+ // Encode bitrate in kbps.
+ ENCODE_KBPS,
+ // Packet transmission bitrate in kbps.
+ TRANSMISSION_KBPS,
+ // Packet retransmission bitrate in kbps.
+ RETRANSMISSION_KBPS,
+ // Fraction of packet loss.
+ PACKET_LOSS_FRACTION,
+ // Duration in milliseconds since last receiver response.
+ MS_SINCE_LAST_RECEIVER_RESPONSE
+ };
+
+ typedef std::map<CastStat, double> StatsMap;
+ typedef std::map<RtpTimestamp, base::TimeTicks> FrameEventTimeMap;
+ typedef std::map<
+ std::pair<RtpTimestamp, uint16>,
+ std::pair<base::TimeTicks, CastLoggingEvent> >
+ PacketEventTimeMap;
+ typedef std::map<CastLoggingEvent, FrameLogStats> FrameStatsMap;
+ typedef std::map<CastLoggingEvent, PacketLogStats> PacketStatsMap;
+
+ static const char* CastStatToString(CastStat stat);
+
+ // Assigns |stats_map| with stats data. Used for testing.
+ void GetStatsInternal(StatsMap* stats_map) const;
+
+ bool GetReceiverOffset(base::TimeDelta* offset);
+ void RecordFrameCapturedTime(const FrameEvent& frame_event);
+ void RecordE2ELatency(const FrameEvent& frame_event);
+ void RecordPacketSentTime(const PacketEvent& packet_event);
+ void ErasePacketSentTime(const PacketEvent& packet_event);
+ void RecordNetworkLatency(const PacketEvent& packet_event);
+ void UpdateLastResponseTime(base::TimeTicks receiver_time);
+
+ void PopulateFpsStat(base::TimeTicks now,
+ CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const;
+ void PopulatePlayoutDelayStat(StatsMap* stats_map) const;
+ void PopulateFrameBitrateStat(base::TimeTicks now, StatsMap* stats_map) const;
+ void PopulatePacketBitrateStat(base::TimeTicks now,
+ CastLoggingEvent event,
+ CastStat stat,
+ StatsMap* stats_map) const;
+ void PopulatePacketLossPercentageStat(StatsMap* stats_map) const;
+
+ const EventMediaType event_media_type_;
+
+ // Not owned by this class.
+ base::TickClock* const clock_;
+
+ // Not owned by this class.
+ ReceiverTimeOffsetEstimator* const offset_estimator_;
+
+ FrameStatsMap frame_stats_;
+ PacketStatsMap packet_stats_;
+
+ base::TimeDelta total_network_latency_;
+ int network_latency_datapoints_;
+ base::TimeDelta total_e2e_latency_;
+ int e2e_latency_datapoints_;
+
+ base::TimeTicks last_response_received_time_;
+
+ // Fixed size map to record when recent frames were captured.
+ FrameEventTimeMap frame_captured_times_;
+
+ // Fixed size map to record when recent packets were sent.
+ PacketEventTimeMap packet_sent_times_;
+
+ // Sender time assigned on creation and |Reset()|.
+ base::TimeTicks start_time_;
+
+ base::ThreadChecker thread_checker_;
+ DISALLOW_COPY_AND_ASSIGN(StatsEventSubscriber);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_STATS_EVENT_SUBSCRIBER_H_
diff --git a/chromium/media/cast/logging/stats_event_subscriber_unittest.cc b/chromium/media/cast/logging/stats_event_subscriber_unittest.cc
new file mode 100644
index 00000000000..33faa020596
--- /dev/null
+++ b/chromium/media/cast/logging/stats_event_subscriber_unittest.cc
@@ -0,0 +1,401 @@
+// 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/rand_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/stats_event_subscriber.h"
+#include "media/cast/test/fake_receiver_time_offset_estimator.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const int kReceiverOffsetSecs = 100;
+}
+
+namespace media {
+namespace cast {
+
+class StatsEventSubscriberTest : public ::testing::Test {
+ protected:
+ StatsEventSubscriberTest()
+ : sender_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(sender_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(sender_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)),
+ fake_offset_estimator_(
+ base::TimeDelta::FromSeconds(kReceiverOffsetSecs)) {
+ receiver_clock_.Advance(base::TimeDelta::FromSeconds(kReceiverOffsetSecs));
+ cast_environment_->Logging()->AddRawEventSubscriber(
+ &fake_offset_estimator_);
+ }
+
+ virtual ~StatsEventSubscriberTest() {
+ if (subscriber_.get())
+ cast_environment_->Logging()->RemoveRawEventSubscriber(subscriber_.get());
+ cast_environment_->Logging()->RemoveRawEventSubscriber(
+ &fake_offset_estimator_);
+ }
+
+ void AdvanceClocks(base::TimeDelta delta) {
+ sender_clock_->Advance(delta);
+ receiver_clock_.Advance(delta);
+ }
+
+ void Init(EventMediaType event_media_type) {
+ DCHECK(!subscriber_.get());
+ subscriber_.reset(new StatsEventSubscriber(
+ event_media_type, cast_environment_->Clock(), &fake_offset_estimator_));
+ cast_environment_->Logging()->AddRawEventSubscriber(subscriber_.get());
+ }
+
+ base::SimpleTestTickClock* sender_clock_; // Owned by CastEnvironment.
+ base::SimpleTestTickClock receiver_clock_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ test::FakeReceiverTimeOffsetEstimator fake_offset_estimator_;
+ scoped_ptr<StatsEventSubscriber> subscriber_;
+};
+
+TEST_F(StatsEventSubscriberTest, Capture) {
+ Init(VIDEO_EVENT);
+
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ int num_frames = 10;
+ base::TimeTicks start_time = sender_clock_->NowTicks();
+ for (int i = 0; i < num_frames; i++) {
+ cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id);
+
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(34567));
+ rtp_timestamp += 90;
+ frame_id++;
+ }
+
+ base::TimeTicks end_time = sender_clock_->NowTicks();
+
+ StatsEventSubscriber::StatsMap stats_map;
+ subscriber_->GetStatsInternal(&stats_map);
+
+ StatsEventSubscriber::StatsMap::iterator it =
+ stats_map.find(StatsEventSubscriber::CAPTURE_FPS);
+ ASSERT_NE(it, stats_map.end());
+
+ base::TimeDelta duration = end_time - start_time;
+ EXPECT_DOUBLE_EQ(
+ it->second,
+ static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);
+}
+
+TEST_F(StatsEventSubscriberTest, Encode) {
+ Init(VIDEO_EVENT);
+
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ int num_frames = 10;
+ base::TimeTicks start_time = sender_clock_->NowTicks();
+ int total_size = 0;
+ for (int i = 0; i < num_frames; i++) {
+ int size = 1000 + base::RandInt(-100, 100);
+ total_size += size;
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ sender_clock_->NowTicks(),
+ FRAME_ENCODED, VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ size,
+ true,
+ 5678);
+
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(35678));
+ rtp_timestamp += 90;
+ frame_id++;
+ }
+
+ base::TimeTicks end_time = sender_clock_->NowTicks();
+
+ StatsEventSubscriber::StatsMap stats_map;
+ subscriber_->GetStatsInternal(&stats_map);
+
+ StatsEventSubscriber::StatsMap::iterator it =
+ stats_map.find(StatsEventSubscriber::ENCODE_FPS);
+ ASSERT_NE(it, stats_map.end());
+
+ base::TimeDelta duration = end_time - start_time;
+ EXPECT_DOUBLE_EQ(
+ it->second,
+ static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);
+
+ it = stats_map.find(StatsEventSubscriber::ENCODE_KBPS);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second,
+ static_cast<double>(total_size) / duration.InMillisecondsF() * 8);
+}
+
+TEST_F(StatsEventSubscriberTest, Decode) {
+ Init(VIDEO_EVENT);
+
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ int num_frames = 10;
+ base::TimeTicks start_time = sender_clock_->NowTicks();
+ for (int i = 0; i < num_frames; i++) {
+ cast_environment_->Logging()->InsertFrameEvent(receiver_clock_.NowTicks(),
+ FRAME_DECODED, VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id);
+
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(36789));
+ rtp_timestamp += 90;
+ frame_id++;
+ }
+
+ base::TimeTicks end_time = sender_clock_->NowTicks();
+
+ StatsEventSubscriber::StatsMap stats_map;
+ subscriber_->GetStatsInternal(&stats_map);
+
+ StatsEventSubscriber::StatsMap::iterator it =
+ stats_map.find(StatsEventSubscriber::DECODE_FPS);
+ ASSERT_NE(it, stats_map.end());
+
+ base::TimeDelta duration = end_time - start_time;
+ EXPECT_DOUBLE_EQ(
+ it->second,
+ static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);
+}
+
+TEST_F(StatsEventSubscriberTest, PlayoutDelay) {
+ Init(VIDEO_EVENT);
+
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ int num_frames = 10;
+ int total_delay_ms = 0;
+ for (int i = 0; i < num_frames; i++) {
+ int delay_ms = base::RandInt(-50, 50);
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms);
+ total_delay_ms += delay_ms;
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ receiver_clock_.NowTicks(),
+ FRAME_PLAYOUT,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ delay);
+
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(37890));
+ rtp_timestamp += 90;
+ frame_id++;
+ }
+
+ StatsEventSubscriber::StatsMap stats_map;
+ subscriber_->GetStatsInternal(&stats_map);
+
+ StatsEventSubscriber::StatsMap::iterator it =
+ stats_map.find(StatsEventSubscriber::AVG_PLAYOUT_DELAY_MS);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(
+ it->second, static_cast<double>(total_delay_ms) / num_frames);
+}
+
+TEST_F(StatsEventSubscriberTest, E2ELatency) {
+ Init(VIDEO_EVENT);
+
+ uint32 rtp_timestamp = 0;
+ uint32 frame_id = 0;
+ int num_frames = 10;
+ base::TimeDelta total_latency;
+ for (int i = 0; i < num_frames; i++) {
+ cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(),
+ FRAME_CAPTURE_BEGIN,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id);
+
+ int latency_micros = 100000 + base::RandInt(-5000, 50000);
+ base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros);
+ AdvanceClocks(latency);
+
+ int delay_micros = base::RandInt(-50000, 50000);
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_micros);
+ total_latency += latency + delay;
+
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ receiver_clock_.NowTicks(),
+ FRAME_PLAYOUT,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ frame_id,
+ delay);
+
+ rtp_timestamp += 90;
+ frame_id++;
+ }
+
+ StatsEventSubscriber::StatsMap stats_map;
+ subscriber_->GetStatsInternal(&stats_map);
+
+ StatsEventSubscriber::StatsMap::iterator it =
+ stats_map.find(StatsEventSubscriber::AVG_E2E_LATENCY_MS);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(
+ it->second, total_latency.InMillisecondsF() / num_frames);
+}
+
+TEST_F(StatsEventSubscriberTest, Packets) {
+ Init(VIDEO_EVENT);
+
+ uint32 rtp_timestamp = 0;
+ int num_packets = 10;
+ int num_latency_recorded_packets = 0;
+ base::TimeTicks start_time = sender_clock_->NowTicks();
+ int total_size = 0;
+ int retransmit_total_size = 0;
+ base::TimeDelta total_latency;
+ int num_packets_sent = 0;
+ int num_packets_retransmitted = 0;
+ // Every 2nd packet will be retransmitted once.
+ // Every 4th packet will be retransmitted twice.
+ // Every 8th packet will be retransmitted 3 times.
+ for (int i = 0; i < num_packets; i++) {
+ int size = 1000 + base::RandInt(-100, 100);
+ total_size += size;
+
+ cast_environment_->Logging()->InsertPacketEvent(sender_clock_->NowTicks(),
+ PACKET_SENT_TO_NETWORK,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ 0,
+ i,
+ num_packets - 1,
+ size);
+ num_packets_sent++;
+
+ int latency_micros = 20000 + base::RandInt(-10000, 10000);
+ base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros);
+ // Latency is only recorded for packets that aren't retransmitted.
+ if (i % 2 != 0) {
+ total_latency += latency;
+ num_latency_recorded_packets++;
+ }
+
+ AdvanceClocks(latency);
+
+ base::TimeTicks received_time = receiver_clock_.NowTicks();
+
+ // Retransmission 1.
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(12345));
+ if (i % 2 == 0) {
+ cast_environment_->Logging()->InsertPacketEvent(
+ receiver_clock_.NowTicks(),
+ PACKET_RETRANSMITTED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ 0,
+ i,
+ num_packets - 1,
+ size);
+ retransmit_total_size += size;
+ num_packets_sent++;
+ num_packets_retransmitted++;
+ }
+
+ // Retransmission 2.
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(13456));
+ if (i % 4 == 0) {
+ cast_environment_->Logging()->InsertPacketEvent(
+ receiver_clock_.NowTicks(),
+ PACKET_RETRANSMITTED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ 0,
+ i,
+ num_packets - 1,
+ size);
+ retransmit_total_size += size;
+ num_packets_sent++;
+ num_packets_retransmitted++;
+ }
+
+ // Retransmission 3.
+ AdvanceClocks(base::TimeDelta::FromMicroseconds(14567));
+ if (i % 8 == 0) {
+ cast_environment_->Logging()->InsertPacketEvent(
+ receiver_clock_.NowTicks(),
+ PACKET_RETRANSMITTED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ 0,
+ i,
+ num_packets - 1,
+ size);
+ retransmit_total_size += size;
+ num_packets_sent++;
+ num_packets_retransmitted++;
+ }
+
+ cast_environment_->Logging()->InsertPacketEvent(received_time,
+ PACKET_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ 0,
+ i,
+ num_packets - 1,
+ size);
+ }
+
+ base::TimeTicks end_time = sender_clock_->NowTicks();
+ base::TimeDelta duration = end_time - start_time;
+
+ StatsEventSubscriber::StatsMap stats_map;
+ subscriber_->GetStatsInternal(&stats_map);
+
+ // Measure AVG_NETWORK_LATENCY_MS, TRANSMISSION_KBPS, RETRANSMISSION_KBPS,
+ // and PACKET_LOSS_FRACTION.
+ StatsEventSubscriber::StatsMap::iterator it =
+ stats_map.find(StatsEventSubscriber::AVG_NETWORK_LATENCY_MS);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(
+ it->second,
+ total_latency.InMillisecondsF() / num_latency_recorded_packets);
+
+ it = stats_map.find(StatsEventSubscriber::TRANSMISSION_KBPS);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second,
+ static_cast<double>(total_size) / duration.InMillisecondsF() * 8);
+
+ it = stats_map.find(StatsEventSubscriber::RETRANSMISSION_KBPS);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(it->second,
+ static_cast<double>(retransmit_total_size) /
+ duration.InMillisecondsF() * 8);
+
+ it = stats_map.find(StatsEventSubscriber::PACKET_LOSS_FRACTION);
+ ASSERT_NE(it, stats_map.end());
+
+ EXPECT_DOUBLE_EQ(
+ it->second,
+ static_cast<double>(num_packets_retransmitted) / num_packets_sent);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/net/cast_net_defines.h b/chromium/media/cast/net/cast_net_defines.h
deleted file mode 100644
index a9f1629a91a..00000000000
--- a/chromium/media/cast/net/cast_net_defines.h
+++ /dev/null
@@ -1,81 +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_NET_CAST_NET_DEFINES_H_
-#define MEDIA_CAST_NET_CAST_NET_DEFINES_H_
-
-#include "base/basictypes.h"
-
-namespace media {
-namespace cast {
-
-class FrameIdWrapHelper {
- public:
- FrameIdWrapHelper()
- : first_(true),
- frame_id_wrap_count_(0),
- range_(kLowRange) {}
-
- uint32 MapTo32bitsFrameId(const uint8 over_the_wire_frame_id) {
- if (first_) {
- first_ = false;
- if (over_the_wire_frame_id == 0xff) {
- // Special case for startup.
- return kStartFrameId;
- }
- }
-
- uint32 wrap_count = frame_id_wrap_count_;
- switch (range_) {
- case kLowRange:
- if (over_the_wire_frame_id > kLowRangeThreshold &&
- over_the_wire_frame_id < kHighRangeThreshold) {
- range_ = kMiddleRange;
- }
- if (over_the_wire_frame_id > kHighRangeThreshold) {
- // Wrap count was incremented in High->Low transition, but this frame
- // is 'old', actually from before the wrap count got incremented.
- --wrap_count;
- }
- break;
- case kMiddleRange:
- if (over_the_wire_frame_id > kHighRangeThreshold) {
- range_ = kHighRange;
- }
- break;
- case kHighRange:
- if (over_the_wire_frame_id < kLowRangeThreshold) {
- // Wrap-around detected.
- range_ = kLowRange;
- ++frame_id_wrap_count_;
- // Frame triggering wrap-around so wrap count should be incremented as
- // as well to match |frame_id_wrap_count_|.
- ++wrap_count;
- }
- break;
- }
- return (wrap_count << 8) + over_the_wire_frame_id;
- }
-
- private:
- enum Range {
- kLowRange,
- kMiddleRange,
- kHighRange,
- };
-
- static const uint8 kLowRangeThreshold = 0x0f;
- static const uint8 kHighRangeThreshold = 0xf0;
- static const uint32 kStartFrameId = GG_UINT32_C(0xffffffff);
-
- bool first_;
- uint32 frame_id_wrap_count_;
- Range range_;
-};
-
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_CAST_NET_DEFINES_H_
diff --git a/chromium/media/cast/net/pacing/mock_paced_packet_sender.h b/chromium/media/cast/net/pacing/mock_paced_packet_sender.h
deleted file mode 100644
index 9933516f14c..00000000000
--- a/chromium/media/cast/net/pacing/mock_paced_packet_sender.h
+++ /dev/null
@@ -1,27 +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_NET_PACING_MOCK_PACED_PACKET_SENDER_H_
-#define MEDIA_CAST_NET_PACING_MOCK_PACED_PACKET_SENDER_H_
-
-#include "media/cast/net/pacing/paced_sender.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-class MockPacedPacketSender : public PacedPacketSender {
- public:
- MockPacedPacketSender();
- virtual ~MockPacedPacketSender();
-
- MOCK_METHOD1(SendPackets, bool(const PacketList& packets));
- MOCK_METHOD1(ResendPackets, bool(const PacketList& packets));
- MOCK_METHOD1(SendRtcpPacket, bool(const Packet& packet));
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_PACING_MOCK_PACED_PACKET_SENDER_H_
diff --git a/chromium/media/cast/net/pacing/paced_sender.cc b/chromium/media/cast/net/pacing/paced_sender.cc
deleted file mode 100644
index 8a07380df0d..00000000000
--- a/chromium/media/cast/net/pacing/paced_sender.cc
+++ /dev/null
@@ -1,148 +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/net/pacing/paced_sender.h"
-
-#include "base/bind.h"
-#include "base/message_loop/message_loop.h"
-
-namespace media {
-namespace cast {
-
-static const int64 kPacingIntervalMs = 10;
-// Each frame will be split into no more than kPacingMaxBurstsPerFrame
-// bursts of packets.
-static const size_t kPacingMaxBurstsPerFrame = 3;
-
-PacedSender::PacedSender(scoped_refptr<CastEnvironment> cast_environment,
- PacketSender* transport)
- : cast_environment_(cast_environment),
- burst_size_(1),
- packets_sent_in_burst_(0),
- transport_(transport),
- weak_factory_(this) {
- ScheduleNextSend();
-}
-
-PacedSender::~PacedSender() {}
-
-bool PacedSender::SendPackets(const PacketList& packets) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- cast_environment_->Logging()->InsertPacketListEvent(kPacketSentToPacer,
- packets);
- return SendPacketsToTransport(packets, &packet_list_);
-}
-
-bool PacedSender::ResendPackets(const PacketList& packets) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- cast_environment_->Logging()->InsertPacketListEvent(kPacketRetransmited,
- packets);
- return SendPacketsToTransport(packets, &resend_packet_list_);
-}
-
-bool PacedSender::SendPacketsToTransport(const PacketList& packets,
- PacketList* packets_not_sent) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- UpdateBurstSize(packets.size());
-
- if (!packets_not_sent->empty()) {
- packets_not_sent->insert(packets_not_sent->end(),
- packets.begin(), packets.end());
- return true;
- }
- PacketList packets_to_send;
- PacketList::const_iterator first_to_store_it = packets.begin();
-
- size_t max_packets_to_send_now = burst_size_ - packets_sent_in_burst_;
- if (max_packets_to_send_now > 0) {
- size_t packets_to_send_now = std::min(max_packets_to_send_now,
- packets.size());
-
- std::advance(first_to_store_it, packets_to_send_now);
- packets_to_send.insert(packets_to_send.begin(),
- packets.begin(), first_to_store_it);
- }
- packets_not_sent->insert(packets_not_sent->end(),
- first_to_store_it, packets.end());
- packets_sent_in_burst_ += packets_to_send.size();
- if (packets_to_send.empty()) return true;
-
- cast_environment_->Logging()->InsertPacketListEvent(kPacketSentToNetwork,
- packets);
- return transport_->SendPackets(packets_to_send);
-}
-
-bool PacedSender::SendRtcpPacket(const Packet& packet) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- // We pass the RTCP packets straight through.
- return transport_->SendPacket(packet);
-}
-
-void PacedSender::ScheduleNextSend() {
- base::TimeDelta time_to_next = time_last_process_ -
- cast_environment_->Clock()->NowTicks() +
- base::TimeDelta::FromMilliseconds(kPacingIntervalMs);
-
- time_to_next = std::max(time_to_next, base::TimeDelta());
-
- cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&PacedSender::SendNextPacketBurst, weak_factory_.GetWeakPtr()),
- time_to_next);
-}
-
-void PacedSender::SendNextPacketBurst() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- SendStoredPackets();
- time_last_process_ = cast_environment_->Clock()->NowTicks();
- ScheduleNextSend();
-}
-
-void PacedSender::SendStoredPackets() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (packet_list_.empty() && resend_packet_list_.empty()) return;
-
- size_t packets_to_send = burst_size_;
- PacketList packets_to_resend;
-
- // Send our re-send packets first.
- if (!resend_packet_list_.empty()) {
- PacketList::iterator it = resend_packet_list_.begin();
- size_t packets_to_send_now = std::min(packets_to_send,
- resend_packet_list_.size());
- std::advance(it, packets_to_send_now);
- packets_to_resend.insert(packets_to_resend.begin(),
- resend_packet_list_.begin(), it);
- resend_packet_list_.erase(resend_packet_list_.begin(), it);
- packets_to_send -= packets_to_resend.size();
- }
- if (!packet_list_.empty() && packets_to_send > 0) {
- PacketList::iterator it = packet_list_.begin();
- size_t packets_to_send_now = std::min(packets_to_send,
- packet_list_.size());
-
- std::advance(it, packets_to_send_now);
- packets_to_resend.insert(packets_to_resend.end(),
- packet_list_.begin(), it);
- packet_list_.erase(packet_list_.begin(), it);
-
- if (packet_list_.empty()) {
- burst_size_ = 1; // Reset burst size after we sent the last stored packet
- packets_sent_in_burst_ = 0;
- }
- }
- transport_->SendPackets(packets_to_resend);
-}
-
-void PacedSender::UpdateBurstSize(size_t packets_to_send) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- packets_to_send = std::max(packets_to_send,
- resend_packet_list_.size() + packet_list_.size());
-
- packets_to_send += (kPacingMaxBurstsPerFrame - 1); // Round up.
- burst_size_ = std::max(packets_to_send / kPacingMaxBurstsPerFrame,
- burst_size_);
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/pacing/paced_sender.gyp b/chromium/media/cast/net/pacing/paced_sender.gyp
deleted file mode 100644
index 1947dd4ec40..00000000000
--- a/chromium/media/cast/net/pacing/paced_sender.gyp
+++ /dev/null
@@ -1,22 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_paced_sender',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'sources': [
- 'paced_sender.h',
- 'paced_sender.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- ],
- },
- ], # targets
-}
diff --git a/chromium/media/cast/net/pacing/paced_sender.h b/chromium/media/cast/net/pacing/paced_sender.h
deleted file mode 100644
index 89283257134..00000000000
--- a/chromium/media/cast/net/pacing/paced_sender.h
+++ /dev/null
@@ -1,83 +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_NET_PACING_PACED_SENDER_H_
-#define MEDIA_CAST_NET_PACING_PACED_SENDER_H_
-
-#include <list>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "base/time/default_tick_clock.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/cast_environment.h"
-
-namespace media {
-namespace cast {
-
-// We have this pure virtual class to enable mocking.
-class PacedPacketSender {
- public:
- // Inform the pacer / sender of the total number of packets.
- virtual bool SendPackets(const PacketList& packets) = 0;
-
- virtual bool ResendPackets(const PacketList& packets) = 0;
-
- virtual bool SendRtcpPacket(const Packet& packet) = 0;
-
- virtual ~PacedPacketSender() {}
-};
-
-class PacedSender : public PacedPacketSender,
- public base::NonThreadSafe,
- public base::SupportsWeakPtr<PacedSender> {
- public:
- PacedSender(scoped_refptr<CastEnvironment> cast_environment,
- PacketSender* transport);
- virtual ~PacedSender();
-
- virtual bool SendPackets(const PacketList& packets) OVERRIDE;
-
- virtual bool ResendPackets(const PacketList& packets) OVERRIDE;
-
- virtual bool SendRtcpPacket(const Packet& packet) OVERRIDE;
-
- protected:
- // Schedule a delayed task on the main cast thread when it's time to send the
- // next packet burst.
- void ScheduleNextSend();
-
- // Process any pending packets in the queue(s).
- void SendNextPacketBurst();
-
- private:
- bool SendPacketsToTransport(const PacketList& packets,
- PacketList* packets_not_sent);
- void SendStoredPackets();
- void UpdateBurstSize(size_t num_of_packets);
-
- scoped_refptr<CastEnvironment> cast_environment_;
- size_t burst_size_;
- size_t packets_sent_in_burst_;
- base::TimeTicks time_last_process_;
- // Note: We can't combine the |packet_list_| and the |resend_packet_list_|
- // since then we might get reordering of the retransmitted packets.
- PacketList packet_list_;
- PacketList resend_packet_list_;
- PacketSender* transport_;
-
- base::WeakPtrFactory<PacedSender> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(PacedSender);
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_PACING_PACED_SENDER_H_
diff --git a/chromium/media/cast/net/pacing/paced_sender_unittest.cc b/chromium/media/cast/net/pacing/paced_sender_unittest.cc
deleted file mode 100644
index 15b81362f69..00000000000
--- a/chromium/media/cast/net/pacing/paced_sender_unittest.cc
+++ /dev/null
@@ -1,257 +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 "base/test/simple_test_tick_clock.h"
-#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-using testing::_;
-
-static const uint8 kValue = 123;
-static const size_t kSize1 = 100;
-static const size_t kSize2 = 101;
-static const size_t kSize3 = 102;
-static const size_t kSize4 = 103;
-static const size_t kNackSize = 104;
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
-
-class TestPacketSender : public PacketSender {
- public:
- virtual bool SendPackets(const PacketList& packets) OVERRIDE {
- PacketList::const_iterator it = packets.begin();
- for (; it != packets.end(); ++it) {
- EXPECT_FALSE(expected_packet_size_.empty());
- size_t expected_packet_size = expected_packet_size_.front();
- expected_packet_size_.pop_front();
- EXPECT_EQ(expected_packet_size, it->size());
- }
- return true;
- }
-
- virtual bool SendPacket(const Packet& packet) OVERRIDE {
- return true;
- }
-
- void AddExpectedSize(int expected_packet_size, int repeat_count) {
- for (int i = 0; i < repeat_count; ++i) {
- expected_packet_size_.push_back(expected_packet_size);
- }
- }
-
- private:
- std::list<int> expected_packet_size_;
-};
-
-class PacedSenderTest : public ::testing::Test {
- protected:
- PacedSenderTest() {
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- }
-
- virtual ~PacedSenderTest() {}
-
- virtual void SetUp() {
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
- paced_sender_.reset(new PacedSender(cast_environment_, &mock_transport_));
- }
-
- PacketList CreatePacketList(size_t packet_size, int num_of_packets_in_frame) {
- PacketList packets;
- for (int i = 0; i < num_of_packets_in_frame; ++i) {
- packets.push_back(Packet(packet_size, kValue));
- }
- return packets;
- }
-
- base::SimpleTestTickClock testing_clock_;
- TestPacketSender mock_transport_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
- scoped_ptr<PacedSender> paced_sender_;
- scoped_refptr<CastEnvironment> cast_environment_;
-};
-
-TEST_F(PacedSenderTest, PassThroughRtcp) {
- mock_transport_.AddExpectedSize(kSize1, 1);
- PacketList packets = CreatePacketList(kSize1, 1);
-
- EXPECT_TRUE(paced_sender_->SendPackets(packets));
- EXPECT_TRUE(paced_sender_->ResendPackets(packets));
-
- mock_transport_.AddExpectedSize(kSize2, 1);
- EXPECT_TRUE(paced_sender_->SendRtcpPacket(Packet(kSize2, kValue)));
-}
-
-TEST_F(PacedSenderTest, BasicPace) {
- int num_of_packets = 9;
- PacketList packets = CreatePacketList(kSize1, num_of_packets);
-
- mock_transport_.AddExpectedSize(kSize1, 3);
- EXPECT_TRUE(paced_sender_->SendPackets(packets));
-
- // Check that we get the next burst.
- mock_transport_.AddExpectedSize(kSize1, 3);
-
- base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(10);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // If we call process too early make sure we don't send any packets.
- timeout = base::TimeDelta::FromMilliseconds(5);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // Check that we get the next burst.
- mock_transport_.AddExpectedSize(kSize1, 3);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // Check that we don't get any more packets.
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-}
-
-TEST_F(PacedSenderTest, PaceWithNack) {
- // Testing what happen when we get multiple NACK requests for a fully lost
- // frames just as we sent the first packets in a frame.
- int num_of_packets_in_frame = 9;
- int num_of_packets_in_nack = 9;
-
- PacketList first_frame_packets =
- CreatePacketList(kSize1, num_of_packets_in_frame);
-
- PacketList second_frame_packets =
- CreatePacketList(kSize2, num_of_packets_in_frame);
-
- PacketList nack_packets =
- CreatePacketList(kNackSize, num_of_packets_in_nack);
-
- // Check that the first burst of the frame go out on the wire.
- mock_transport_.AddExpectedSize(kSize1, 3);
- EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
-
- // Add first NACK request.
- EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets));
-
- // Check that we get the first NACK burst.
- mock_transport_.AddExpectedSize(kNackSize, 5);
- base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(10);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // Add second NACK request.
- EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets));
-
- // Check that we get the next NACK burst.
- mock_transport_.AddExpectedSize(kNackSize, 7);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // End of NACK plus a packet from the oldest frame.
- mock_transport_.AddExpectedSize(kNackSize, 6);
- mock_transport_.AddExpectedSize(kSize1, 1);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // Add second frame.
- // Make sure we don't delay the second frame due to the previous packets.
- EXPECT_TRUE(paced_sender_->SendPackets(second_frame_packets));
-
- // Last packets of frame 1 and the first packets of frame 2.
- mock_transport_.AddExpectedSize(kSize1, 5);
- mock_transport_.AddExpectedSize(kSize2, 2);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // Last packets of frame 2.
- mock_transport_.AddExpectedSize(kSize2, 7);
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-
- // No more packets.
- testing_clock_.Advance(timeout);
- task_runner_->RunTasks();
-}
-
-TEST_F(PacedSenderTest, PaceWith60fps) {
- // Testing what happen when we get multiple NACK requests for a fully lost
- // frames just as we sent the first packets in a frame.
- int num_of_packets_in_frame = 9;
-
- PacketList first_frame_packets =
- CreatePacketList(kSize1, num_of_packets_in_frame);
-
- PacketList second_frame_packets =
- CreatePacketList(kSize2, num_of_packets_in_frame);
-
- PacketList third_frame_packets =
- CreatePacketList(kSize3, num_of_packets_in_frame);
-
- PacketList fourth_frame_packets =
- CreatePacketList(kSize4, num_of_packets_in_frame);
-
- base::TimeDelta timeout_10ms = base::TimeDelta::FromMilliseconds(10);
-
- // Check that the first burst of the frame go out on the wire.
- mock_transport_.AddExpectedSize(kSize1, 3);
- EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
-
- mock_transport_.AddExpectedSize(kSize1, 3);
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(6));
-
- // Add second frame, after 16 ms.
- EXPECT_TRUE(paced_sender_->SendPackets(second_frame_packets));
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(4));
-
- mock_transport_.AddExpectedSize(kSize1, 3);
- mock_transport_.AddExpectedSize(kSize2, 1);
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-
- mock_transport_.AddExpectedSize(kSize2, 4);
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(3));
-
- // Add third frame, after 33 ms.
- EXPECT_TRUE(paced_sender_->SendPackets(third_frame_packets));
- mock_transport_.AddExpectedSize(kSize2, 4);
- mock_transport_.AddExpectedSize(kSize3, 1);
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(7));
- task_runner_->RunTasks();
-
- // Add fourth frame, after 50 ms.
- EXPECT_TRUE(paced_sender_->SendPackets(fourth_frame_packets));
-
- mock_transport_.AddExpectedSize(kSize3, 6);
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-
- mock_transport_.AddExpectedSize(kSize3, 2);
- mock_transport_.AddExpectedSize(kSize4, 4);
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-
- mock_transport_.AddExpectedSize(kSize4, 5);
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-
- testing_clock_.Advance(timeout_10ms);
- task_runner_->RunTasks();
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/rtp_sender/mock_rtp_sender.h b/chromium/media/cast/net/rtp_sender/mock_rtp_sender.h
deleted file mode 100644
index 2c3f19f2ae9..00000000000
--- a/chromium/media/cast/net/rtp_sender/mock_rtp_sender.h
+++ /dev/null
@@ -1,34 +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_RTP_SENDER_MOCK_RTP_SENDER_H_
-#define MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
-
-#include <vector>
-
-#include "media/cast/net/rtp_sender/rtp_sender.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-class MockRtpSender : public RtpSender {
- public:
- MOCK_METHOD2(IncomingEncodedVideoFrame,
- bool(const EncodedVideoFrame& frame, int64 capture_time));
-
- MOCK_METHOD2(IncomingEncodedAudioFrame,
- bool(const EncodedAudioFrame& frame, int64 recorded_time));
-
- MOCK_METHOD3(ResendPacket,
- bool(bool is_audio, uint32 frame_id, uint16 packet_id));
-
- MOCK_METHOD0(RtpStatistics, void());
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
-
diff --git a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.cc b/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.cc
deleted file mode 100644
index 3bd8f900665..00000000000
--- a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.cc
+++ /dev/null
@@ -1,174 +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/net/rtp_sender/packet_storage/packet_storage.h"
-
-#include <string>
-
-#include "base/logging.h"
-#include "media/cast/cast_defines.h"
-
-namespace media {
-namespace cast {
-
-// Limit the max time delay to avoid frame id wrap around; 256 / 60 fps.
-const int kMaxAllowedTimeStoredMs = 4000;
-
-typedef PacketMap::iterator PacketMapIterator;
-typedef TimeToPacketMap::iterator TimeToPacketIterator;
-
-class StoredPacket {
- public:
- StoredPacket() {
- packet_.reserve(kIpPacketSize);
- }
-
- void Save(const Packet* packet) {
- DCHECK_LT(packet->size(), kIpPacketSize) << "Invalid argument";
- packet_.clear();
- packet_.insert(packet_.begin(), packet->begin(), packet->end());
- }
-
- void GetCopy(PacketList* packets) {
- packets->push_back(Packet(packet_.begin(), packet_.end()));
- }
-
- private:
- Packet packet_;
-};
-
-PacketStorage::PacketStorage(base::TickClock* clock,
- int max_time_stored_ms)
- : clock_(clock) {
- max_time_stored_ = base::TimeDelta::FromMilliseconds(max_time_stored_ms);
- DCHECK_LE(max_time_stored_ms, kMaxAllowedTimeStoredMs) << "Invalid argument";
-}
-
-PacketStorage::~PacketStorage() {
- time_to_packet_map_.clear();
-
- PacketMapIterator store_it = stored_packets_.begin();
- for (; store_it != stored_packets_.end();
- store_it = stored_packets_.begin()) {
- stored_packets_.erase(store_it);
- }
- while (!free_packets_.empty()) {
- free_packets_.pop_front();
- }
-}
-
-void PacketStorage::CleanupOldPackets(base::TimeTicks now) {
- TimeToPacketIterator time_it = time_to_packet_map_.begin();
-
- // Check max size.
- while (time_to_packet_map_.size() >= kMaxStoredPackets) {
- PacketMapIterator store_it = stored_packets_.find(time_it->second);
-
- // We should always find the packet.
- DCHECK(store_it != stored_packets_.end()) << "Invalid state";
- time_to_packet_map_.erase(time_it);
- // Save the pointer.
- linked_ptr<StoredPacket> storted_packet = store_it->second;
- stored_packets_.erase(store_it);
- // Add this packet to the free list for later re-use.
- free_packets_.push_back(storted_packet);
- time_it = time_to_packet_map_.begin();
- }
-
- // Time out old packets.
- while (time_it != time_to_packet_map_.end()) {
- if (now < time_it->first + max_time_stored_) {
- break;
- }
- // Packet too old.
- PacketMapIterator store_it = stored_packets_.find(time_it->second);
-
- // We should always find the packet.
- DCHECK(store_it != stored_packets_.end()) << "Invalid state";
- time_to_packet_map_.erase(time_it);
- // Save the pointer.
- linked_ptr<StoredPacket> storted_packet = store_it->second;
- stored_packets_.erase(store_it);
- // Add this packet to the free list for later re-use.
- free_packets_.push_back(storted_packet);
- time_it = time_to_packet_map_.begin();
- }
-}
-
-void PacketStorage::StorePacket(uint32 frame_id, uint16 packet_id,
- const Packet* packet) {
- base::TimeTicks now = clock_->NowTicks();
- CleanupOldPackets(now);
-
- // Internally we only use the 8 LSB of the frame id.
- uint32 index = ((0xff & frame_id) << 16) + packet_id;
- PacketMapIterator it = stored_packets_.find(index);
- if (it != stored_packets_.end()) {
- // We have already saved this.
- DCHECK(false) << "Invalid state";
- return;
- }
- linked_ptr<StoredPacket> stored_packet;
- if (free_packets_.empty()) {
- // No previous allocated packets allocate one.
- stored_packet.reset(new StoredPacket());
- } else {
- // Re-use previous allocated packet.
- stored_packet = free_packets_.front();
- free_packets_.pop_front();
- }
- stored_packet->Save(packet);
- stored_packets_[index] = stored_packet;
- time_to_packet_map_.insert(std::make_pair(now, index));
-}
-
-PacketList PacketStorage::GetPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets) {
- PacketList packets_to_resend;
-
- // Iterate over all frames in the list.
- for (MissingFramesAndPacketsMap::const_iterator it =
- missing_frames_and_packets.begin();
- it != missing_frames_and_packets.end(); ++it) {
- uint8 frame_id = it->first;
- const PacketIdSet& packets_set = it->second;
- bool success = false;
-
- if (packets_set.empty()) {
- VLOG(1) << "Missing all packets in frame " << static_cast<int>(frame_id);
-
- uint16 packet_id = 0;
- do {
- // Get packet from storage.
- success = GetPacket(frame_id, packet_id, &packets_to_resend);
- ++packet_id;
- } while (success);
- } else {
- // Iterate over all of the packets in the frame.
- for (PacketIdSet::const_iterator set_it = packets_set.begin();
- set_it != packets_set.end(); ++set_it) {
- GetPacket(frame_id, *set_it, &packets_to_resend);
- }
- }
- }
- return packets_to_resend;
-}
-
-bool PacketStorage::GetPacket(uint8 frame_id,
- uint16 packet_id,
- PacketList* packets) {
- // Internally we only use the 8 LSB of the frame id.
- uint32 index = (static_cast<uint32>(frame_id) << 16) + packet_id;
- PacketMapIterator it = stored_packets_.find(index);
- if (it == stored_packets_.end()) {
- return false;
- }
- it->second->GetCopy(packets);
- VLOG(1) << "Resend " << static_cast<int>(frame_id)
- << ":" << packet_id;
- return true;
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.gyp b/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.gyp
deleted file mode 100644
index f691d9e9b69..00000000000
--- a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.gyp
+++ /dev/null
@@ -1,23 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'packet_storage',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'sources': [
- 'packet_storage.h',
- 'packet_storage.cc',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- ],
- },
- ],
-}
-
diff --git a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.h b/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.h
deleted file mode 100644
index 34933ef5f6d..00000000000
--- a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage.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_NET_RTP_SENDER_PACKET_STORAGE_PACKET_STORAGE_H_
-#define MEDIA_CAST_NET_RTP_SENDER_PACKET_STORAGE_PACKET_STORAGE_H_
-
-#include <list>
-#include <map>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/memory/linked_ptr.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "media/cast/cast_config.h"
-
-namespace media {
-namespace cast {
-
-class StoredPacket;
-typedef std::map<uint32, linked_ptr<StoredPacket> > PacketMap;
-typedef std::multimap<base::TimeTicks, uint32> TimeToPacketMap;
-
-class PacketStorage {
- public:
- static const int kMaxStoredPackets = 1000;
-
- PacketStorage(base::TickClock* clock, int max_time_stored_ms);
- virtual ~PacketStorage();
-
- void StorePacket(uint32 frame_id, uint16 packet_id, const Packet* packet);
-
- // Copies all missing packets into the packet list.
- PacketList GetPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets);
-
- // Copies packet into the packet list.
- bool GetPacket(uint8 frame_id, uint16 packet_id, PacketList* packets);
-
- private:
- void CleanupOldPackets(base::TimeTicks now);
-
- base::TickClock* const clock_; // Not owned by this class.
- base::TimeDelta max_time_stored_;
- PacketMap stored_packets_;
- TimeToPacketMap time_to_packet_map_;
- std::list<linked_ptr<StoredPacket> > free_packets_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_RTP_SENDER_PACKET_STORAGE_PACKET_STORAGE_H_
diff --git a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage_unittest.cc b/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage_unittest.cc
deleted file mode 100644
index 049d3ae29b6..00000000000
--- a/chromium/media/cast/net/rtp_sender/packet_storage/packet_storage_unittest.cc
+++ /dev/null
@@ -1,110 +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/net/rtp_sender/packet_storage/packet_storage.h"
-
-#include <vector>
-
-#include "base/test/simple_test_tick_clock.h"
-#include "base/time/time.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-static const int kMaxDeltaStoredMs = 500;
-static const base::TimeDelta kDeltaBetweenFrames =
- base::TimeDelta::FromMilliseconds(33);
-
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
-
-class PacketStorageTest : public ::testing::Test {
- protected:
- PacketStorageTest() : packet_storage_(&testing_clock_, kMaxDeltaStoredMs) {
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- }
-
- base::SimpleTestTickClock testing_clock_;
- PacketStorage packet_storage_;
-};
-
-TEST_F(PacketStorageTest, TimeOut) {
- Packet test_123(100, 123); // 100 insertions of the value 123.
- PacketList packets;
- for (uint32 frame_id = 0; frame_id < 30; ++frame_id) {
- for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
- packet_storage_.StorePacket(frame_id, packet_id, &test_123);
- }
- testing_clock_.Advance(kDeltaBetweenFrames);
- }
-
- // All packets belonging to the first 14 frames is expected to be expired.
- for (uint32 frame_id = 0; frame_id < 14; ++frame_id) {
- for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
- Packet packet;
- EXPECT_FALSE(packet_storage_.GetPacket(frame_id, packet_id, &packets));
- }
- }
- // All packets belonging to the next 15 frames is expected to be valid.
- for (uint32 frame_id = 14; frame_id < 30; ++frame_id) {
- for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
- EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packets));
- EXPECT_TRUE(packets.front() == test_123);
- }
- }
-}
-
-TEST_F(PacketStorageTest, MaxNumberOfPackets) {
- Packet test_123(100, 123); // 100 insertions of the value 123.
- PacketList packets;
-
- uint32 frame_id = 0;
- for (uint16 packet_id = 0; packet_id <= PacketStorage::kMaxStoredPackets;
- ++packet_id) {
- packet_storage_.StorePacket(frame_id, packet_id, &test_123);
- }
- Packet packet;
- uint16 packet_id = 0;
- EXPECT_FALSE(packet_storage_.GetPacket(frame_id, packet_id, &packets));
-
- ++packet_id;
- for (; packet_id <= PacketStorage::kMaxStoredPackets; ++packet_id) {
- EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packets));
- EXPECT_TRUE(packets.back() == test_123);
- }
-}
-
-TEST_F(PacketStorageTest, PacketContent) {
- Packet test_123(100, 123); // 100 insertions of the value 123.
- Packet test_234(200, 234); // 200 insertions of the value 234.
- PacketList packets;
-
- for (uint32 frame_id = 0; frame_id < 10; ++frame_id) {
- for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
- // Every other packet.
- if (packet_id % 2 == 0) {
- packet_storage_.StorePacket(frame_id, packet_id, &test_123);
- } else {
- packet_storage_.StorePacket(frame_id, packet_id, &test_234);
- }
- }
- testing_clock_.Advance(kDeltaBetweenFrames);
- }
- for (uint32 frame_id = 0; frame_id < 10; ++frame_id) {
- for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
- EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packets));
- // Every other packet.
- if (packet_id % 2 == 0) {
- EXPECT_TRUE(packets.back() == test_123);
- } else {
- EXPECT_TRUE(packets.back() == test_234);
- }
- }
- }
-}
-
-} // namespace cast
-} // namespace media
-
diff --git a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.cc b/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.cc
deleted file mode 100644
index 8a50f8a8aad..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.cc
+++ /dev/null
@@ -1,153 +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/net/rtp_sender/rtp_packetizer/rtp_packetizer.h"
-
-#include "base/logging.h"
-#include "media/cast/cast_defines.h"
-#include "media/cast/net/pacing/paced_sender.h"
-#include "net/base/big_endian.h"
-
-namespace media {
-namespace cast {
-
-static const uint16 kCommonRtpHeaderLength = 12;
-static const uint16 kCastRtpHeaderLength = 7;
-static const uint8 kCastKeyFrameBitMask = 0x80;
-static const uint8 kCastReferenceFrameIdBitMask = 0x40;
-
-RtpPacketizer::RtpPacketizer(PacedPacketSender* transport,
- PacketStorage* packet_storage,
- RtpPacketizerConfig rtp_packetizer_config)
- : config_(rtp_packetizer_config),
- transport_(transport),
- packet_storage_(packet_storage),
- sequence_number_(config_.sequence_number),
- rtp_timestamp_(config_.rtp_timestamp),
- packet_id_(0),
- send_packets_count_(0),
- send_octet_count_(0) {
- DCHECK(transport) << "Invalid argument";
-}
-
-RtpPacketizer::~RtpPacketizer() {}
-
-void RtpPacketizer::IncomingEncodedVideoFrame(
- const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time) {
- DCHECK(!config_.audio) << "Invalid state";
- if (config_.audio) return;
-
- // Timestamp is in 90 KHz for video.
- rtp_timestamp_ = GetVideoRtpTimestamp(capture_time);
- time_last_sent_rtp_timestamp_ = capture_time;
-
- Cast(video_frame->key_frame,
- video_frame->frame_id,
- video_frame->last_referenced_frame_id,
- rtp_timestamp_,
- video_frame->data);
-}
-
-void RtpPacketizer::IncomingEncodedAudioFrame(
- const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time) {
- DCHECK(config_.audio) << "Invalid state";
- if (!config_.audio) return;
-
- rtp_timestamp_ += audio_frame->samples; // Timestamp is in samples for audio.
- time_last_sent_rtp_timestamp_ = recorded_time;
- Cast(true, audio_frame->frame_id, 0, rtp_timestamp_, audio_frame->data);
-}
-
-uint16 RtpPacketizer::NextSequenceNumber() {
- ++sequence_number_;
- return sequence_number_ - 1;
-}
-
-bool RtpPacketizer::LastSentTimestamp(base::TimeTicks* time_sent,
- uint32* rtp_timestamp) const {
- if (time_last_sent_rtp_timestamp_.is_null()) return false;
-
- *time_sent = time_last_sent_rtp_timestamp_;
- *rtp_timestamp = rtp_timestamp_;
- return true;
-}
-
-// TODO(mikhal): Switch to pass data with a const_ref.
-void RtpPacketizer::Cast(bool is_key,
- uint32 frame_id,
- uint32 reference_frame_id,
- uint32 timestamp,
- const std::string& data) {
- uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength;
- uint16 max_length = config_.max_payload_length - rtp_header_length - 1;
-
- // Split the payload evenly (round number up).
- size_t num_packets = (data.size() + max_length) / max_length;
- size_t payload_length = (data.size() + num_packets) / num_packets;
- DCHECK_LE(payload_length, max_length) << "Invalid argument";
-
- PacketList packets;
-
- size_t remaining_size = data.size();
- std::string::const_iterator data_iter = data.begin();
- while (remaining_size > 0) {
- Packet packet;
-
- if (remaining_size < payload_length) {
- payload_length = remaining_size;
- }
- remaining_size -= payload_length;
- BuildCommonRTPheader(&packet, remaining_size == 0, timestamp);
-
- // Build Cast header.
- packet.push_back(
- (is_key ? kCastKeyFrameBitMask : 0) | kCastReferenceFrameIdBitMask);
- packet.push_back(frame_id);
- size_t start_size = packet.size();
- packet.resize(start_size + 4);
- net::BigEndianWriter big_endian_writer(&(packet[start_size]), 4);
- big_endian_writer.WriteU16(packet_id_);
- big_endian_writer.WriteU16(static_cast<uint16>(num_packets - 1));
- packet.push_back(static_cast<uint8>(reference_frame_id));
-
- // Copy payload data.
- packet.insert(packet.end(), data_iter, data_iter + payload_length);
-
- // Store packet.
- packet_storage_->StorePacket(frame_id, packet_id_, &packet);
- ++packet_id_;
- data_iter += payload_length;
-
- // Update stats.
- ++send_packets_count_;
- send_octet_count_ += payload_length;
- packets.push_back(packet);
- }
- DCHECK(packet_id_ == num_packets) << "Invalid state";
-
- // Send to network.
- transport_->SendPackets(packets);
-
- // Prepare for next frame.
- packet_id_ = 0;
-}
-
-void RtpPacketizer::BuildCommonRTPheader(
- Packet* packet, bool marker_bit, uint32 time_stamp) {
- packet->push_back(0x80);
- packet->push_back(static_cast<uint8>(config_.payload_type) |
- (marker_bit ? kRtpMarkerBitMask : 0));
- size_t start_size = packet->size();
- packet->resize(start_size + 10);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 10);
- big_endian_writer.WriteU16(sequence_number_);
- big_endian_writer.WriteU32(time_stamp);
- big_endian_writer.WriteU32(config_.ssrc);
- ++sequence_number_;
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.gyp b/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.gyp
deleted file mode 100644
index d75d8a66911..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.gyp
+++ /dev/null
@@ -1,27 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_rtp_packetizer',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc/',
- ],
- 'sources': [
- 'rtp_packetizer.cc',
- 'rtp_packetizer.h',
- 'rtp_packetizer_config.cc',
- 'rtp_packetizer_config.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- '<(DEPTH)/net/net.gyp:net',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.h b/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.h
deleted file mode 100644
index 9f9be5fe163..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.h
+++ /dev/null
@@ -1,73 +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_NET_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
-#define MEDIA_CAST_NET_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
-
-#include <cmath>
-#include <list>
-#include <map>
-
-#include "base/time/time.h"
-#include "media/cast/net/rtp_sender/packet_storage/packet_storage.h"
-#include "media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
-
-namespace media {
-namespace cast {
-
-class PacedPacketSender;
-
-// This object is only called from the main cast thread.
-// This class break encoded audio and video frames into packets and add an RTP
-// header to each packet.
-class RtpPacketizer {
- public:
- RtpPacketizer(PacedPacketSender* transport,
- PacketStorage* packet_storage,
- RtpPacketizerConfig rtp_packetizer_config);
- ~RtpPacketizer();
-
- // The video_frame objects ownership is handled by the main cast thread.
- void IncomingEncodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time);
-
- // The audio_frame objects ownership is handled by the main cast thread.
- void IncomingEncodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time);
-
- bool LastSentTimestamp(base::TimeTicks* time_sent,
- uint32* rtp_timestamp) const;
-
- // Return the next sequence number, and increment by one. Enables unique
- // incremental sequence numbers for every packet (including retransmissions).
- uint16 NextSequenceNumber();
-
- int send_packets_count() { return send_packets_count_; }
-
- size_t send_octet_count() { return send_octet_count_; }
-
- private:
- void Cast(bool is_key, uint32 frame_id, uint32 reference_frame_id,
- uint32 timestamp, const std::string& data);
-
- void BuildCommonRTPheader(std::vector<uint8>* packet, bool marker_bit,
- uint32 time_stamp);
-
- RtpPacketizerConfig config_;
- PacedPacketSender* transport_;
- PacketStorage* packet_storage_;
-
- base::TimeTicks time_last_sent_rtp_timestamp_;
- uint16 sequence_number_;
- uint32 rtp_timestamp_;
- uint16 packet_id_;
-
- int send_packets_count_;
- size_t send_octet_count_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
diff --git a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.cc b/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.cc
deleted file mode 100644
index 5fe3a92b61b..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.cc
+++ /dev/null
@@ -1,21 +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/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
-
-namespace media {
-namespace cast {
-
-RtpPacketizerConfig::RtpPacketizerConfig()
- : ssrc(0),
- max_payload_length(kIpPacketSize - 28), // Default is IP-v4/UDP.
- audio(false),
- frequency(8000),
- payload_type(-1),
- sequence_number(0),
- rtp_timestamp(0) {
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h b/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h
deleted file mode 100644
index 1a2549e66b2..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h
+++ /dev/null
@@ -1,39 +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 CAST_NET_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
-#define CAST_NET_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
-
-#include "media/cast/cast_config.h"
-#include "media/cast/rtp_receiver/rtp_receiver_defines.h"
-
-namespace media {
-namespace cast {
-
-struct RtpPacketizerConfig {
- RtpPacketizerConfig();
-
- // General.
- bool audio;
- int payload_type;
- uint16 max_payload_length;
- uint16 sequence_number;
- uint32 rtp_timestamp;
- int frequency;
-
- // SSRC.
- unsigned int ssrc;
-
- // Video.
- VideoCodec video_codec;
-
- // Audio.
- uint8 channels;
- AudioCodec audio_codec;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // CAST_NET_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
diff --git a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc b/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
deleted file mode 100644
index defdecf7584..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
+++ /dev/null
@@ -1,153 +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/net/rtp_sender/rtp_packetizer/rtp_packetizer.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/net/rtp_sender/packet_storage/packet_storage.h"
-#include "media/cast/net/rtp_sender/rtp_packetizer/test/rtp_header_parser.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-static const int kPayload = 127;
-static const uint32 kTimestampMs = 10;
-static const uint16 kSeqNum = 33;
-static const int kMaxPacketLength = 1500;
-static const int kSsrc = 0x12345;
-static const unsigned int kFrameSize = 5000;
-static const int kMaxPacketStorageTimeMs = 300;
-
-class TestRtpPacketTransport : public PacedPacketSender {
- public:
- explicit TestRtpPacketTransport(RtpPacketizerConfig config)
- : config_(config),
- sequence_number_(kSeqNum),
- packets_sent_(0),
- expected_number_of_packets_(0),
- expected_packet_id_(0),
- expected_frame_id_(0) {}
-
- void VerifyRtpHeader(const RtpCastTestHeader& rtp_header) {
- VerifyCommonRtpHeader(rtp_header);
- VerifyCastRtpHeader(rtp_header);
- }
-
- void VerifyCommonRtpHeader(const RtpCastTestHeader& rtp_header) {
- EXPECT_EQ(expected_number_of_packets_ == packets_sent_,
- rtp_header.marker);
- EXPECT_EQ(kPayload, rtp_header.payload_type);
- EXPECT_EQ(sequence_number_, rtp_header.sequence_number);
- EXPECT_EQ(kTimestampMs * 90, rtp_header.rtp_timestamp);
- EXPECT_EQ(config_.ssrc, rtp_header.ssrc);
- EXPECT_EQ(0, rtp_header.num_csrcs);
- }
-
- void VerifyCastRtpHeader(const RtpCastTestHeader& rtp_header) {
- EXPECT_FALSE(rtp_header.is_key_frame);
- EXPECT_EQ(expected_frame_id_, rtp_header.frame_id);
- EXPECT_EQ(expected_packet_id_, rtp_header.packet_id);
- EXPECT_EQ(expected_number_of_packets_ - 1, rtp_header.max_packet_id);
- EXPECT_TRUE(rtp_header.is_reference);
- EXPECT_EQ(expected_frame_id_ - 1u, rtp_header.reference_frame_id);
- }
-
- virtual bool SendPackets(const PacketList& packets) OVERRIDE {
- EXPECT_EQ(expected_number_of_packets_, static_cast<int>(packets.size()));
- PacketList::const_iterator it = packets.begin();
- for (; it != packets.end(); ++it) {
- ++packets_sent_;
- RtpHeaderParser parser(it->data(), it->size());
- RtpCastTestHeader rtp_header;
- parser.Parse(&rtp_header);
- VerifyRtpHeader(rtp_header);
- ++sequence_number_;
- ++expected_packet_id_;
- }
- return true;
- }
-
- virtual bool ResendPackets(const PacketList& packets) OVERRIDE {
- EXPECT_TRUE(false);
- return false;
- }
-
- virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE {
- EXPECT_TRUE(false);
- return false;
- }
-
- void SetExpectedNumberOfPackets(int num) {
- expected_number_of_packets_ = num;
- }
-
- RtpPacketizerConfig config_;
- uint32 sequence_number_;
- int packets_sent_;
- int expected_number_of_packets_;
- // Assuming packets arrive in sequence.
- int expected_packet_id_;
- uint32 expected_frame_id_;
-};
-
-class RtpPacketizerTest : public ::testing::Test {
- protected:
- RtpPacketizerTest()
- :video_frame_(),
- packet_storage_(&testing_clock_, kMaxPacketStorageTimeMs) {
- config_.sequence_number = kSeqNum;
- config_.ssrc = kSsrc;
- config_.payload_type = kPayload;
- config_.max_payload_length = kMaxPacketLength;
- transport_.reset(new TestRtpPacketTransport(config_));
- rtp_packetizer_.reset(
- new RtpPacketizer(transport_.get(), &packet_storage_, config_));
- }
-
- virtual ~RtpPacketizerTest() {}
-
- virtual void SetUp() {
- video_frame_.key_frame = false;
- video_frame_.frame_id = 0;
- video_frame_.last_referenced_frame_id = kStartFrameId;
- video_frame_.data.assign(kFrameSize, 123);
- }
-
- base::SimpleTestTickClock testing_clock_;
- scoped_ptr<RtpPacketizer> rtp_packetizer_;
- RtpPacketizerConfig config_;
- scoped_ptr<TestRtpPacketTransport> transport_;
- EncodedVideoFrame video_frame_;
- PacketStorage packet_storage_;
-};
-
-TEST_F(RtpPacketizerTest, SendStandardPackets) {
- int expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
- transport_->SetExpectedNumberOfPackets(expected_num_of_packets);
-
- base::TimeTicks time;
- time += base::TimeDelta::FromMilliseconds(kTimestampMs);
- rtp_packetizer_->IncomingEncodedVideoFrame(&video_frame_, time);
-}
-
-TEST_F(RtpPacketizerTest, Stats) {
- EXPECT_FALSE(rtp_packetizer_->send_packets_count());
- EXPECT_FALSE(rtp_packetizer_->send_octet_count());
- // Insert packets at varying lengths.
- int expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
- transport_->SetExpectedNumberOfPackets(expected_num_of_packets);
-
- testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kTimestampMs));
- rtp_packetizer_->IncomingEncodedVideoFrame(&video_frame_,
- testing_clock_.NowTicks());
- EXPECT_EQ(expected_num_of_packets, rtp_packetizer_->send_packets_count());
- EXPECT_EQ(kFrameSize, rtp_packetizer_->send_octet_count());
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/rtp_sender/rtp_sender.cc b/chromium/media/cast/net/rtp_sender/rtp_sender.cc
deleted file mode 100644
index 2b017bc1784..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_sender.cc
+++ /dev/null
@@ -1,145 +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/net/rtp_sender/rtp_sender.h"
-
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "media/cast/cast_defines.h"
-#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/rtcp/rtcp_defines.h"
-#include "net/base/big_endian.h"
-
-namespace media {
-namespace cast {
-
-RtpSender::RtpSender(scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig* audio_config,
- const VideoSenderConfig* video_config,
- PacedPacketSender* transport)
- : cast_environment_(cast_environment),
- config_(),
- transport_(transport) {
- // Store generic cast config and create packetizer config.
- DCHECK(audio_config || video_config) << "Invalid argument";
- if (audio_config) {
- storage_.reset(new PacketStorage(cast_environment->Clock(),
- audio_config->rtp_history_ms));
- config_.audio = true;
- config_.ssrc = audio_config->sender_ssrc;
- config_.payload_type = audio_config->rtp_payload_type;
- config_.frequency = audio_config->frequency;
- config_.audio_codec = audio_config->codec;
- } else {
- storage_.reset(new PacketStorage(cast_environment->Clock(),
- video_config->rtp_history_ms));
- config_.audio = false;
- config_.ssrc = video_config->sender_ssrc;
- config_.payload_type = video_config->rtp_payload_type;
- config_.frequency = kVideoFrequency;
- config_.video_codec = video_config->codec;
- }
- // Randomly set start values.
- config_.sequence_number = base::RandInt(0, 65535);
- config_.rtp_timestamp = base::RandInt(0, 65535);
- config_.rtp_timestamp += base::RandInt(0, 65535) << 16;
- packetizer_.reset(new RtpPacketizer(transport, storage_.get(), config_));
-}
-
-RtpSender::~RtpSender() {}
-
-void RtpSender::IncomingEncodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time) {
- packetizer_->IncomingEncodedVideoFrame(video_frame, capture_time);
-}
-
-void RtpSender::IncomingEncodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time) {
- packetizer_->IncomingEncodedAudioFrame(audio_frame, recorded_time);
-}
-
-void RtpSender::ResendPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets) {
- // Iterate over all frames in the list.
- for (MissingFramesAndPacketsMap::const_iterator it =
- missing_frames_and_packets.begin();
- it != missing_frames_and_packets.end(); ++it) {
- PacketList packets_to_resend;
- uint8 frame_id = it->first;
- const PacketIdSet& packets_set = it->second;
- bool success = false;
-
- if (packets_set.empty()) {
- VLOG(1) << "Missing all packets in frame " << static_cast<int>(frame_id);
-
- uint16 packet_id = 0;
- do {
- // Get packet from storage.
- success = storage_->GetPacket(frame_id, packet_id, &packets_to_resend);
-
- // Resend packet to the network.
- if (success) {
- VLOG(1) << "Resend " << static_cast<int>(frame_id)
- << ":" << packet_id;
- // Set a unique incremental sequence number for every packet.
- Packet& packet = packets_to_resend.back();
- UpdateSequenceNumber(&packet);
- // Set the size as correspond to each frame.
- ++packet_id;
- }
- } while (success);
- } else {
- // Iterate over all of the packets in the frame.
- for (PacketIdSet::const_iterator set_it = packets_set.begin();
- set_it != packets_set.end(); ++set_it) {
- uint16 packet_id = *set_it;
- success = storage_->GetPacket(frame_id, packet_id, &packets_to_resend);
-
- // Resend packet to the network.
- if (success) {
- VLOG(1) << "Resend " << static_cast<int>(frame_id)
- << ":" << packet_id;
- Packet& packet = packets_to_resend.back();
- UpdateSequenceNumber(&packet);
- }
- }
- }
- transport_->ResendPackets(packets_to_resend);
- }
-}
-
-void RtpSender::UpdateSequenceNumber(Packet* packet) {
- uint16 new_sequence_number = packetizer_->NextSequenceNumber();
- int index = 2;
- (*packet)[index] = (static_cast<uint8>(new_sequence_number));
- (*packet)[index + 1] =(static_cast<uint8>(new_sequence_number >> 8));
-}
-
-void RtpSender::RtpStatistics(const base::TimeTicks& now,
- RtcpSenderInfo* sender_info) {
- // The timestamp of this Rtcp packet should be estimated as the timestamp of
- // the frame being captured at this moment. We are calculating that
- // timestamp as the last frame's timestamp + the time since the last frame
- // was captured.
- uint32 ntp_seconds = 0;
- uint32 ntp_fraction = 0;
- ConvertTimeTicksToNtp(now, &ntp_seconds, &ntp_fraction);
- sender_info->ntp_seconds = ntp_seconds;
- sender_info->ntp_fraction = ntp_fraction;
-
- base::TimeTicks time_sent;
- uint32 rtp_timestamp;
- if (packetizer_->LastSentTimestamp(&time_sent, &rtp_timestamp)) {
- base::TimeDelta time_since_last_send = now - time_sent;
- sender_info->rtp_timestamp = rtp_timestamp +
- time_since_last_send.InMilliseconds() * (config_.frequency / 1000);
- } else {
- sender_info->rtp_timestamp = 0;
- }
- sender_info->send_packet_count = packetizer_->send_packets_count();
- sender_info->send_octet_count = packetizer_->send_octet_count();
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/net/rtp_sender/rtp_sender.gyp b/chromium/media/cast/net/rtp_sender/rtp_sender.gyp
deleted file mode 100644
index f689b99b149..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_sender.gyp
+++ /dev/null
@@ -1,26 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_rtp_sender',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc/',
- ],
- 'sources': [
- 'rtp_sender.cc',
- 'rtp_sender.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- 'packet_storage/packet_storage.gyp:*',
- 'rtp_packetizer/rtp_packetizer.gyp:*',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/net/rtp_sender/rtp_sender.h b/chromium/media/cast/net/rtp_sender/rtp_sender.h
deleted file mode 100644
index 038165992db..00000000000
--- a/chromium/media/cast/net/rtp_sender/rtp_sender.h
+++ /dev/null
@@ -1,66 +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.
-
-// This file contains the interface to the cast RTP sender.
-
-#ifndef MEDIA_CAST_NET_RTP_SENDER_RTP_SENDER_H_
-#define MEDIA_CAST_NET_RTP_SENDER_RTP_SENDER_H_
-
-#include <map>
-#include <set>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/net/rtp_sender/packet_storage/packet_storage.h"
-#include "media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer.h"
-#include "media/cast/net/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
-
-namespace media {
-namespace cast {
-
-class PacedPacketSender;
-struct RtcpSenderInfo;
-
-// This object is only called from the main cast thread.
-// This class handles splitting encoded audio and video frames into packets and
-// add an RTP header to each packet. The sent packets are stored until they are
-// acknowledged by the remote peer or timed out.
-class RtpSender {
- public:
- RtpSender(scoped_refptr<CastEnvironment> cast_environment,
- const AudioSenderConfig* audio_config,
- const VideoSenderConfig* video_config,
- PacedPacketSender* transport);
-
- ~RtpSender();
-
- // The video_frame objects ownership is handled by the main cast thread.
- void IncomingEncodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time);
-
- // The audio_frame objects ownership is handled by the main cast thread.
- void IncomingEncodedAudioFrame(const EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time);
-
- void ResendPackets(const MissingFramesAndPacketsMap& missing_packets);
-
- void RtpStatistics(const base::TimeTicks& now, RtcpSenderInfo* sender_info);
-
- private:
- void UpdateSequenceNumber(std::vector<uint8>* packet);
-
- scoped_refptr<CastEnvironment> cast_environment_;
- RtpPacketizerConfig config_;
- scoped_ptr<RtpPacketizer> packetizer_;
- scoped_ptr<PacketStorage> storage_;
- PacedPacketSender* transport_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_RTP_SENDER_RTP_SENDER_H_
diff --git a/chromium/media/cast/receiver/audio_decoder.cc b/chromium/media/cast/receiver/audio_decoder.cc
new file mode 100644
index 00000000000..a4d18968355
--- /dev/null
+++ b/chromium/media/cast/receiver/audio_decoder.cc
@@ -0,0 +1,246 @@
+// 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/audio_decoder.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/sys_byteorder.h"
+#include "media/cast/cast_defines.h"
+#include "third_party/opus/src/include/opus.h"
+
+namespace media {
+namespace cast {
+
+// Base class that handles the common problem of detecting dropped frames, and
+// then invoking the Decode() method implemented by the subclasses to convert
+// the encoded payload data into usable audio data.
+class AudioDecoder::ImplBase
+ : public base::RefCountedThreadSafe<AudioDecoder::ImplBase> {
+ public:
+ ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::AudioCodec codec,
+ int num_channels,
+ int sampling_rate)
+ : cast_environment_(cast_environment),
+ codec_(codec),
+ num_channels_(num_channels),
+ cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
+ seen_first_frame_(false) {
+ if (num_channels_ <= 0 || sampling_rate <= 0 || sampling_rate % 100 != 0)
+ cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
+ }
+
+ CastInitializationStatus InitializationResult() const {
+ return cast_initialization_status_;
+ }
+
+ void DecodeFrame(scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback) {
+ DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED);
+
+ COMPILE_ASSERT(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_),
+ size_of_frame_id_types_do_not_match);
+ bool is_continuous = true;
+ if (seen_first_frame_) {
+ const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_;
+ if (frames_ahead > 1) {
+ RecoverBecauseFramesWereDropped();
+ is_continuous = false;
+ }
+ } else {
+ seen_first_frame_ = true;
+ }
+ last_frame_id_ = encoded_frame->frame_id;
+
+ scoped_ptr<AudioBus> decoded_audio = Decode(
+ encoded_frame->mutable_bytes(),
+ static_cast<int>(encoded_frame->data.size()));
+ cast_environment_->PostTask(CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(callback,
+ base::Passed(&decoded_audio),
+ is_continuous));
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<ImplBase>;
+ virtual ~ImplBase() {}
+
+ virtual void RecoverBecauseFramesWereDropped() {}
+
+ // Note: Implementation of Decode() is allowed to mutate |data|.
+ virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) = 0;
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ const transport::AudioCodec codec_;
+ const int num_channels_;
+
+ // Subclass' ctor is expected to set this to STATUS_AUDIO_INITIALIZED.
+ CastInitializationStatus cast_initialization_status_;
+
+ private:
+ bool seen_first_frame_;
+ uint32 last_frame_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImplBase);
+};
+
+class AudioDecoder::OpusImpl : public AudioDecoder::ImplBase {
+ public:
+ OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment,
+ int num_channels,
+ int sampling_rate)
+ : ImplBase(cast_environment,
+ transport::kOpus,
+ num_channels,
+ sampling_rate),
+ decoder_memory_(new uint8[opus_decoder_get_size(num_channels)]),
+ opus_decoder_(reinterpret_cast<OpusDecoder*>(decoder_memory_.get())),
+ max_samples_per_frame_(
+ kOpusMaxFrameDurationMillis * sampling_rate / 1000),
+ buffer_(new float[max_samples_per_frame_ * num_channels]) {
+ if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
+ return;
+ if (opus_decoder_init(opus_decoder_, sampling_rate, num_channels) !=
+ OPUS_OK) {
+ ImplBase::cast_initialization_status_ =
+ STATUS_INVALID_AUDIO_CONFIGURATION;
+ return;
+ }
+ ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
+ }
+
+ private:
+ virtual ~OpusImpl() {}
+
+ virtual void RecoverBecauseFramesWereDropped() OVERRIDE {
+ // Passing NULL for the input data notifies the decoder of frame loss.
+ const opus_int32 result =
+ opus_decode_float(
+ opus_decoder_, NULL, 0, buffer_.get(), max_samples_per_frame_, 0);
+ DCHECK_GE(result, 0);
+ }
+
+ virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) OVERRIDE {
+ scoped_ptr<AudioBus> audio_bus;
+ const opus_int32 num_samples_decoded = opus_decode_float(
+ opus_decoder_, data, len, buffer_.get(), max_samples_per_frame_, 0);
+ if (num_samples_decoded <= 0)
+ return audio_bus.Pass(); // Decode error.
+
+ // Copy interleaved samples from |buffer_| into a new AudioBus (where
+ // samples are stored in planar format, for each channel).
+ audio_bus = AudioBus::Create(num_channels_, num_samples_decoded).Pass();
+ // TODO(miu): This should be moved into AudioBus::FromInterleaved().
+ for (int ch = 0; ch < num_channels_; ++ch) {
+ const float* src = buffer_.get() + ch;
+ const float* const src_end = src + num_samples_decoded * num_channels_;
+ float* dest = audio_bus->channel(ch);
+ for (; src < src_end; src += num_channels_, ++dest)
+ *dest = *src;
+ }
+ return audio_bus.Pass();
+ }
+
+ const scoped_ptr<uint8[]> decoder_memory_;
+ OpusDecoder* const opus_decoder_;
+ const int max_samples_per_frame_;
+ const scoped_ptr<float[]> buffer_;
+
+ // According to documentation in third_party/opus/src/include/opus.h, we must
+ // provide enough space in |buffer_| to contain 120ms of samples. At 48 kHz,
+ // then, that means 5760 samples times the number of channels.
+ static const int kOpusMaxFrameDurationMillis = 120;
+
+ DISALLOW_COPY_AND_ASSIGN(OpusImpl);
+};
+
+class AudioDecoder::Pcm16Impl : public AudioDecoder::ImplBase {
+ public:
+ Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
+ int num_channels,
+ int sampling_rate)
+ : ImplBase(cast_environment,
+ transport::kPcm16,
+ num_channels,
+ sampling_rate) {
+ if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
+ return;
+ ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
+ }
+
+ private:
+ virtual ~Pcm16Impl() {}
+
+ virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) OVERRIDE {
+ scoped_ptr<AudioBus> audio_bus;
+ const int num_samples = len / sizeof(int16) / num_channels_;
+ if (num_samples <= 0)
+ return audio_bus.Pass();
+
+ int16* const pcm_data = reinterpret_cast<int16*>(data);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ // Convert endianness.
+ const int num_elements = num_samples * num_channels_;
+ for (int i = 0; i < num_elements; ++i)
+ pcm_data[i] = static_cast<int16>(base::NetToHost16(pcm_data[i]));
+#endif
+ audio_bus = AudioBus::Create(num_channels_, num_samples).Pass();
+ audio_bus->FromInterleaved(pcm_data, num_samples, sizeof(int16));
+ return audio_bus.Pass();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(Pcm16Impl);
+};
+
+AudioDecoder::AudioDecoder(
+ const scoped_refptr<CastEnvironment>& cast_environment,
+ int channels,
+ int sampling_rate,
+ transport::AudioCodec codec)
+ : cast_environment_(cast_environment) {
+ switch (codec) {
+ case transport::kOpus:
+ impl_ = new OpusImpl(cast_environment, channels, sampling_rate);
+ break;
+ case transport::kPcm16:
+ impl_ = new Pcm16Impl(cast_environment, channels, sampling_rate);
+ break;
+ default:
+ NOTREACHED() << "Unknown or unspecified codec.";
+ break;
+ }
+}
+
+AudioDecoder::~AudioDecoder() {}
+
+CastInitializationStatus AudioDecoder::InitializationResult() const {
+ if (impl_)
+ return impl_->InitializationResult();
+ return STATUS_UNSUPPORTED_AUDIO_CODEC;
+}
+
+void AudioDecoder::DecodeFrame(
+ scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback) {
+ DCHECK(encoded_frame.get());
+ DCHECK(!callback.is_null());
+ if (!impl_ || impl_->InitializationResult() != STATUS_AUDIO_INITIALIZED) {
+ callback.Run(make_scoped_ptr<AudioBus>(NULL), false);
+ return;
+ }
+ cast_environment_->PostTask(CastEnvironment::AUDIO,
+ FROM_HERE,
+ base::Bind(&AudioDecoder::ImplBase::DecodeFrame,
+ impl_,
+ base::Passed(&encoded_frame),
+ callback));
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/receiver/audio_decoder.h b/chromium/media/cast/receiver/audio_decoder.h
new file mode 100644
index 00000000000..c66735e4e64
--- /dev/null
+++ b/chromium/media/cast/receiver/audio_decoder.h
@@ -0,0 +1,64 @@
+// 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_AUDIO_DECODER_H_
+#define MEDIA_CAST_RECEIVER_AUDIO_DECODER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/audio_bus.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/transport/cast_transport_config.h"
+
+namespace media {
+namespace cast {
+
+class AudioDecoder {
+ public:
+ // Callback passed to DecodeFrame, to deliver decoded audio data from the
+ // decoder. The number of samples in |audio_bus| may vary, and |audio_bus|
+ // can be NULL when errors occur. |is_continuous| is normally true, but will
+ // be false if the decoder has detected a frame skip since the last decode
+ // operation; and the client should take steps to smooth audio discontinuities
+ // in this case.
+ typedef base::Callback<void(scoped_ptr<AudioBus> audio_bus,
+ bool is_continuous)> DecodeFrameCallback;
+
+ AudioDecoder(const scoped_refptr<CastEnvironment>& cast_environment,
+ int channels,
+ int sampling_rate,
+ transport::AudioCodec codec);
+ virtual ~AudioDecoder();
+
+ // Returns STATUS_AUDIO_INITIALIZED if the decoder was successfully
+ // constructed from the given FrameReceiverConfig. If this method returns any
+ // other value, calls to DecodeFrame() will not succeed.
+ CastInitializationStatus InitializationResult() const;
+
+ // Decode the payload in |encoded_frame| asynchronously. |callback| will be
+ // invoked on the CastEnvironment::MAIN thread with the result.
+ //
+ // In the normal case, |encoded_frame->frame_id| will be
+ // monotonically-increasing by 1 for each successive call to this method.
+ // When it is not, the decoder will assume one or more frames have been
+ // dropped (e.g., due to packet loss), and will perform recovery actions.
+ void DecodeFrame(scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback);
+
+ private:
+ class ImplBase;
+ class OpusImpl;
+ class Pcm16Impl;
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ scoped_refptr<ImplBase> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDecoder);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RECEIVER_AUDIO_DECODER_H_
diff --git a/chromium/media/cast/receiver/audio_decoder_unittest.cc b/chromium/media/cast/receiver/audio_decoder_unittest.cc
new file mode 100644
index 00000000000..6985a694232
--- /dev/null
+++ b/chromium/media/cast/receiver/audio_decoder_unittest.cc
@@ -0,0 +1,241 @@
+// 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 "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/sys_byteorder.h"
+#include "base/time/time.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/standalone_cast_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/opus/src/include/opus.h"
+
+namespace media {
+namespace cast {
+
+namespace {
+struct TestScenario {
+ transport::AudioCodec codec;
+ int num_channels;
+ int sampling_rate;
+
+ TestScenario(transport::AudioCodec c, int n, int s)
+ : codec(c), num_channels(n), sampling_rate(s) {}
+};
+} // namespace
+
+class AudioDecoderTest : public ::testing::TestWithParam<TestScenario> {
+ public:
+ AudioDecoderTest()
+ : cast_environment_(new StandaloneCastEnvironment()),
+ cond_(&lock_) {}
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ 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(
+ new TestAudioBusFactory(GetParam().num_channels,
+ GetParam().sampling_rate,
+ TestAudioBusFactory::kMiddleANoteFreq,
+ 0.5f));
+ last_frame_id_ = 0;
+ seen_a_decoded_frame_ = false;
+
+ if (GetParam().codec == transport::kOpus) {
+ opus_encoder_memory_.reset(
+ new uint8[opus_encoder_get_size(GetParam().num_channels)]);
+ OpusEncoder* const opus_encoder =
+ reinterpret_cast<OpusEncoder*>(opus_encoder_memory_.get());
+ CHECK_EQ(OPUS_OK, opus_encoder_init(opus_encoder,
+ GetParam().sampling_rate,
+ GetParam().num_channels,
+ OPUS_APPLICATION_AUDIO));
+ CHECK_EQ(OPUS_OK,
+ opus_encoder_ctl(opus_encoder, OPUS_SET_BITRATE(OPUS_AUTO)));
+ }
+
+ total_audio_feed_in_ = base::TimeDelta();
+ total_audio_decoded_ = base::TimeDelta();
+ }
+
+ // Called from the unit test thread to create another EncodedFrame and push it
+ // into the decoding pipeline.
+ void FeedMoreAudio(const base::TimeDelta& duration,
+ int num_dropped_frames) {
+ // Prepare a simulated EncodedFrame to feed into the AudioDecoder.
+ scoped_ptr<transport::EncodedFrame> encoded_frame(
+ new transport::EncodedFrame());
+ encoded_frame->dependency = transport::EncodedFrame::KEY;
+ encoded_frame->frame_id = last_frame_id_ + 1 + num_dropped_frames;
+ encoded_frame->referenced_frame_id = encoded_frame->frame_id;
+ last_frame_id_ = encoded_frame->frame_id;
+
+ const scoped_ptr<AudioBus> audio_bus(
+ audio_bus_factory_->NextAudioBus(duration).Pass());
+
+ // Encode |audio_bus| into |encoded_frame->data|.
+ const int num_elements = audio_bus->channels() * audio_bus->frames();
+ std::vector<int16> interleaved(num_elements);
+ audio_bus->ToInterleaved(
+ audio_bus->frames(), sizeof(int16), &interleaved.front());
+ if (GetParam().codec == transport::kPcm16) {
+ encoded_frame->data.resize(num_elements * sizeof(int16));
+ int16* const pcm_data =
+ reinterpret_cast<int16*>(encoded_frame->mutable_bytes());
+ for (size_t i = 0; i < interleaved.size(); ++i)
+ pcm_data[i] = static_cast<int16>(base::HostToNet16(interleaved[i]));
+ } else if (GetParam().codec == transport::kOpus) {
+ OpusEncoder* const opus_encoder =
+ reinterpret_cast<OpusEncoder*>(opus_encoder_memory_.get());
+ const int kOpusEncodeBufferSize = 4000;
+ encoded_frame->data.resize(kOpusEncodeBufferSize);
+ const int payload_size =
+ opus_encode(opus_encoder,
+ &interleaved.front(),
+ audio_bus->frames(),
+ encoded_frame->mutable_bytes(),
+ encoded_frame->data.size());
+ CHECK_GT(payload_size, 1);
+ encoded_frame->data.resize(payload_size);
+ } else {
+ ASSERT_TRUE(false); // Not reached.
+ }
+
+ {
+ base::AutoLock auto_lock(lock_);
+ total_audio_feed_in_ += duration;
+ }
+
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&AudioDecoder::DecodeFrame,
+ base::Unretained(audio_decoder_.get()),
+ base::Passed(&encoded_frame),
+ base::Bind(&AudioDecoderTest::OnDecodedFrame,
+ base::Unretained(this),
+ num_dropped_frames == 0)));
+ }
+
+ // Blocks the caller until all audio that has been feed in has been decoded.
+ void WaitForAllAudioToBeDecoded() {
+ DCHECK(!cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ base::AutoLock auto_lock(lock_);
+ while (total_audio_decoded_ < total_audio_feed_in_)
+ cond_.Wait();
+ EXPECT_EQ(total_audio_feed_in_.InMicroseconds(),
+ total_audio_decoded_.InMicroseconds());
+ }
+
+ private:
+ // Called by |audio_decoder_| to deliver each frame of decoded audio.
+ void OnDecodedFrame(bool should_be_continuous,
+ scoped_ptr<AudioBus> audio_bus,
+ bool is_continuous) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+
+ // A NULL |audio_bus| indicates a decode error, which we don't expect.
+ ASSERT_FALSE(!audio_bus);
+
+ // Did the decoder detect whether frames were dropped?
+ EXPECT_EQ(should_be_continuous, is_continuous);
+
+ // Does the audio data seem to be intact? For Opus, we have to ignore the
+ // first frame seen at the start (and immediately after dropped packet
+ // recovery) because it introduces a tiny, significant delay.
+ bool examine_signal = true;
+ if (GetParam().codec == transport::kOpus) {
+ examine_signal = seen_a_decoded_frame_ && should_be_continuous;
+ seen_a_decoded_frame_ = true;
+ }
+ if (examine_signal) {
+ for (int ch = 0; ch < audio_bus->channels(); ++ch) {
+ EXPECT_NEAR(
+ TestAudioBusFactory::kMiddleANoteFreq * 2 * audio_bus->frames() /
+ GetParam().sampling_rate,
+ CountZeroCrossings(audio_bus->channel(ch), audio_bus->frames()),
+ 1);
+ }
+ }
+
+ // Signal the main test thread that more audio was decoded.
+ base::AutoLock auto_lock(lock_);
+ total_audio_decoded_ += base::TimeDelta::FromSeconds(1) *
+ audio_bus->frames() / GetParam().sampling_rate;
+ cond_.Signal();
+ }
+
+ const scoped_refptr<StandaloneCastEnvironment> cast_environment_;
+ scoped_ptr<AudioDecoder> audio_decoder_;
+ scoped_ptr<TestAudioBusFactory> audio_bus_factory_;
+ uint32 last_frame_id_;
+ bool seen_a_decoded_frame_;
+ scoped_ptr<uint8[]> opus_encoder_memory_;
+
+ base::Lock lock_;
+ base::ConditionVariable cond_;
+ base::TimeDelta total_audio_feed_in_;
+ base::TimeDelta total_audio_decoded_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDecoderTest);
+};
+
+TEST_P(AudioDecoderTest, DecodesFramesWithSameDuration) {
+ const base::TimeDelta kTenMilliseconds =
+ base::TimeDelta::FromMilliseconds(10);
+ const int kNumFrames = 10;
+ for (int i = 0; i < kNumFrames; ++i)
+ FeedMoreAudio(kTenMilliseconds, 0);
+ WaitForAllAudioToBeDecoded();
+}
+
+TEST_P(AudioDecoderTest, DecodesFramesWithVaryingDuration) {
+ // These are the set of frame durations supported by the Opus encoder.
+ const int kFrameDurationMs[] = { 5, 10, 20, 40, 60 };
+
+ const int kNumFrames = 10;
+ for (size_t i = 0; i < arraysize(kFrameDurationMs); ++i)
+ for (int j = 0; j < kNumFrames; ++j)
+ FeedMoreAudio(base::TimeDelta::FromMilliseconds(kFrameDurationMs[i]), 0);
+ WaitForAllAudioToBeDecoded();
+}
+
+TEST_P(AudioDecoderTest, RecoversFromDroppedFrames) {
+ const base::TimeDelta kTenMilliseconds =
+ base::TimeDelta::FromMilliseconds(10);
+ const int kNumFrames = 100;
+ int next_drop_at = 3;
+ int next_num_dropped = 1;
+ for (int i = 0; i < kNumFrames; ++i) {
+ if (i == next_drop_at) {
+ const int num_dropped = next_num_dropped++;
+ next_drop_at *= 2;
+ i += num_dropped;
+ FeedMoreAudio(kTenMilliseconds, num_dropped);
+ } else {
+ FeedMoreAudio(kTenMilliseconds, 0);
+ }
+ }
+ WaitForAllAudioToBeDecoded();
+}
+
+INSTANTIATE_TEST_CASE_P(AudioDecoderTestScenarios,
+ AudioDecoderTest,
+ ::testing::Values(
+ TestScenario(transport::kPcm16, 1, 8000),
+ TestScenario(transport::kPcm16, 2, 48000),
+ TestScenario(transport::kOpus, 1, 8000),
+ TestScenario(transport::kOpus, 2, 48000)));
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/receiver/cast_receiver_impl.cc b/chromium/media/cast/receiver/cast_receiver_impl.cc
new file mode 100644
index 00000000000..7cff354c146
--- /dev/null
+++ b/chromium/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/chromium/media/cast/receiver/cast_receiver_impl.h b/chromium/media/cast/receiver/cast_receiver_impl.h
new file mode 100644
index 00000000000..c0dd5f38d10
--- /dev/null
+++ b/chromium/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/chromium/media/cast/receiver/frame_receiver.cc b/chromium/media/cast/receiver/frame_receiver.cc
new file mode 100644
index 00000000000..e189cc99a7f
--- /dev/null
+++ b/chromium/media/cast/receiver/frame_receiver.cc
@@ -0,0 +1,326 @@
+// 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/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/cast_environment.h"
+
+namespace {
+const int kMinSchedulingDelayMs = 1;
+} // namespace
+
+namespace media {
+namespace cast {
+
+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(config.rtp_max_delay_ms)),
+ expected_frame_duration_(
+ base::TimeDelta::FromSeconds(1) / config.max_frame_rate),
+ reports_are_scheduled_(false),
+ framer_(cast_environment->Clock(),
+ this,
+ config.incoming_ssrc,
+ true,
+ config.rtp_max_delay_ms * config.max_frame_rate / 1000),
+ rtcp_(cast_environment_,
+ NULL,
+ NULL,
+ packet_sender,
+ &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(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_));
+}
+
+FrameReceiver::~FrameReceiver() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber_);
+}
+
+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();
+
+ frame_id_to_rtp_timestamp_[rtp_header.frame_id & 0xff] =
+ rtp_header.rtp_timestamp;
+ cast_environment_->Logging()->InsertPacketEvent(
+ 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);
+
+ 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_),
+ rtp_timebase_);
+ }
+ lip_sync_rtp_timestamp_ = fresh_sync_rtp;
+ lip_sync_drift_.Update(
+ now, fresh_sync_reference - lip_sync_reference_time_);
+ }
+
+ // Another frame is complete from a non-duplicate packet. Attempt to emit
+ // more frames to satisfy enqueued requests.
+ if (complete)
+ EmitAvailableEncodedFrames();
+}
+
+void FrameReceiver::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, event_media_type_,
+ rtp_timestamp, cast_message.ack_frame_id_);
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber_.GetRtcpEventsAndReset(&rtcp_events);
+ rtcp_.SendRtcpFromRtpReceiver(&cast_message, &rtcp_events);
+}
+
+void FrameReceiver::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 packets to produce a completed frame.";
+ return; // ProcessParsedPacket() 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(&FrameReceiver::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_data;
+ if (!decryptor_.Decrypt(encoded_frame->frame_id,
+ encoded_frame->data,
+ &decrypted_data)) {
+ // Decryption failed. Give up on this frame.
+ framer_.ReleaseFrame(encoded_frame->frame_id);
+ continue;
+ }
+ encoded_frame->data.swap(decrypted_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);
+ cast_environment_->PostTask(CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(frame_request_queue_.front(),
+ base::Passed(&encoded_frame)));
+ frame_request_queue_.pop_front();
+ }
+}
+
+void FrameReceiver::EmitAvailableEncodedFramesAfterWaiting() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK(is_waiting_for_consecutive_frame_);
+ is_waiting_for_consecutive_frame_ = false;
+ EmitAvailableEncodedFrames();
+}
+
+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_),
+ rtp_timebase_) +
+ target_playout_delay_;
+}
+
+void FrameReceiver::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(&FrameReceiver::SendNextCastMessage,
+ weak_factory_.GetWeakPtr()),
+ time_to_send);
+}
+
+void FrameReceiver::SendNextCastMessage() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ framer_.SendCastMessage(); // Will only send a message if it is time.
+ ScheduleNextCastMessage();
+}
+
+void FrameReceiver::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(&FrameReceiver::SendNextRtcpReport,
+ weak_factory_.GetWeakPtr()),
+ time_to_next);
+}
+
+void FrameReceiver::SendNextRtcpReport() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ rtcp_.SendRtcpFromRtpReceiver(NULL, NULL);
+ ScheduleNextRtcpReport();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/receiver/frame_receiver.h b/chromium/media/cast/receiver/frame_receiver.h
new file mode 100644
index 00000000000..ac14ab1e0fb
--- /dev/null
+++ b/chromium/media/cast/receiver/frame_receiver.h
@@ -0,0 +1,184 @@
+// 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_FRAME_RECEIVER_H_
+#define MEDIA_CAST_RECEIVER_FRAME_RECEIVER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "media/cast/base/clock_drift_smoother.h"
+#include "media/cast/cast_config.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/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 {
+namespace cast {
+
+class CastEnvironment;
+
+// FrameReceiver receives packets out-of-order while clients make requests for
+// complete frames in-order. (A frame consists of one or more packets.)
+//
+// 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
+// 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.
+//
+// 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 FrameReceiver : public RtpPayloadFeedback,
+ public base::SupportsWeakPtr<FrameReceiver> {
+ public:
+ FrameReceiver(const scoped_refptr<CastEnvironment>& cast_environment,
+ const FrameReceiverConfig& config,
+ EventMediaType event_media_type,
+ transport::PacedPacketSender* const packet_sender);
+
+ virtual ~FrameReceiver();
+
+ // Request an encoded frame.
+ //
+ // The given |callback| is guaranteed to be run at some point in the future,
+ // except for those requests still enqueued at destruction time.
+ void RequestEncodedFrame(const ReceiveEncodedFrameCallback& 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);
+
+ // 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 FrameReceiverTest; // Invokes ProcessParsedPacket().
+
+ void ProcessParsedPacket(const RtpCastHeader& rtp_header,
+ const uint8* payload_data,
+ size_t payload_size);
+
+ // 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();
+
+ // 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 timing for the next cast message.
+ void ScheduleNextCastMessage();
+
+ // Schedule timing for the next RTCP report.
+ void ScheduleNextRtcpReport();
+
+ // Actually send the next cast message.
+ void SendNextCastMessage();
+
+ // Actually send the next RTCP report.
+ void SendNextRtcpReport();
+
+ 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 events to be sent over to the cast sender via RTCP.
+ ReceiverRtcpEventSubscriber event_subscriber_;
+
+ // 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
+ // 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.
+ // 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
+ // 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_;
+
+ // 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<ReceiveEncodedFrameCallback> 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 FRAME_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<FrameReceiver> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameReceiver);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RECEIVER_FRAME_RECEIVER_H_
diff --git a/chromium/media/cast/receiver/frame_receiver_unittest.cc b/chromium/media/cast/receiver/frame_receiver_unittest.cc
new file mode 100644
index 00000000000..4d8273e132a
--- /dev/null
+++ b/chromium/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/chromium/media/cast/receiver/video_decoder.cc b/chromium/media/cast/receiver/video_decoder.cc
new file mode 100644
index 00000000000..6db3fd35f39
--- /dev/null
+++ b/chromium/media/cast/receiver/video_decoder.cc
@@ -0,0 +1,259 @@
+// 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/video_decoder.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_reader.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "media/base/video_util.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/cast_environment.h"
+// VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
+// backwards compatibility for legacy applications using the library.
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
+#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
+#include "ui/gfx/size.h"
+
+namespace media {
+namespace cast {
+
+// Base class that handles the common problem of detecting dropped frames, and
+// then invoking the Decode() method implemented by the subclasses to convert
+// the encoded payload data into a usable video frame.
+class VideoDecoder::ImplBase
+ : public base::RefCountedThreadSafe<VideoDecoder::ImplBase> {
+ public:
+ ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::VideoCodec codec)
+ : cast_environment_(cast_environment),
+ codec_(codec),
+ cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED),
+ seen_first_frame_(false) {}
+
+ CastInitializationStatus InitializationResult() const {
+ return cast_initialization_status_;
+ }
+
+ void DecodeFrame(scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback) {
+ DCHECK_EQ(cast_initialization_status_, STATUS_VIDEO_INITIALIZED);
+
+ COMPILE_ASSERT(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_),
+ size_of_frame_id_types_do_not_match);
+ bool is_continuous = true;
+ if (seen_first_frame_) {
+ const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_;
+ if (frames_ahead > 1) {
+ RecoverBecauseFramesWereDropped();
+ is_continuous = false;
+ }
+ } else {
+ seen_first_frame_ = true;
+ }
+ last_frame_id_ = encoded_frame->frame_id;
+
+ const scoped_refptr<VideoFrame> decoded_frame = Decode(
+ encoded_frame->mutable_bytes(),
+ static_cast<int>(encoded_frame->data.size()));
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(callback, decoded_frame, is_continuous));
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<ImplBase>;
+ virtual ~ImplBase() {}
+
+ virtual void RecoverBecauseFramesWereDropped() {}
+
+ // Note: Implementation of Decode() is allowed to mutate |data|.
+ virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) = 0;
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ const transport::VideoCodec codec_;
+
+ // Subclass' ctor is expected to set this to STATUS_VIDEO_INITIALIZED.
+ CastInitializationStatus cast_initialization_status_;
+
+ private:
+ bool seen_first_frame_;
+ uint32 last_frame_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImplBase);
+};
+
+class VideoDecoder::Vp8Impl : public VideoDecoder::ImplBase {
+ public:
+ explicit Vp8Impl(const scoped_refptr<CastEnvironment>& cast_environment)
+ : ImplBase(cast_environment, transport::kVp8) {
+ if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED)
+ return;
+
+ vpx_codec_dec_cfg_t cfg = {0};
+ // TODO(miu): Revisit this for typical multi-core desktop use case. This
+ // feels like it should be 4 or 8.
+ cfg.threads = 1;
+
+ DCHECK(vpx_codec_get_caps(vpx_codec_vp8_dx()) & VPX_CODEC_CAP_POSTPROC);
+ if (vpx_codec_dec_init(&context_,
+ vpx_codec_vp8_dx(),
+ &cfg,
+ VPX_CODEC_USE_POSTPROC) != VPX_CODEC_OK) {
+ ImplBase::cast_initialization_status_ =
+ STATUS_INVALID_VIDEO_CONFIGURATION;
+ return;
+ }
+ ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
+ }
+
+ private:
+ virtual ~Vp8Impl() {
+ if (ImplBase::cast_initialization_status_ == STATUS_VIDEO_INITIALIZED)
+ CHECK_EQ(VPX_CODEC_OK, vpx_codec_destroy(&context_));
+ }
+
+ virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE {
+ if (len <= 0 || vpx_codec_decode(&context_,
+ data,
+ static_cast<unsigned int>(len),
+ NULL,
+ 0) != VPX_CODEC_OK) {
+ return NULL;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t* const image = vpx_codec_get_frame(&context_, &iter);
+ if (!image)
+ return NULL;
+ if (image->fmt != VPX_IMG_FMT_I420 && image->fmt != VPX_IMG_FMT_YV12) {
+ NOTREACHED();
+ return NULL;
+ }
+ DCHECK(vpx_codec_get_frame(&context_, &iter) == NULL)
+ << "Should have only decoded exactly one frame.";
+
+ const gfx::Size frame_size(image->d_w, image->d_h);
+ // Note: Timestamp for the VideoFrame will be set in VideoReceiver.
+ const scoped_refptr<VideoFrame> decoded_frame =
+ VideoFrame::CreateFrame(VideoFrame::YV12,
+ frame_size,
+ gfx::Rect(frame_size),
+ frame_size,
+ base::TimeDelta());
+ CopyYPlane(image->planes[VPX_PLANE_Y],
+ image->stride[VPX_PLANE_Y],
+ image->d_h,
+ decoded_frame);
+ CopyUPlane(image->planes[VPX_PLANE_U],
+ image->stride[VPX_PLANE_U],
+ (image->d_h + 1) / 2,
+ decoded_frame);
+ CopyVPlane(image->planes[VPX_PLANE_V],
+ image->stride[VPX_PLANE_V],
+ (image->d_h + 1) / 2,
+ decoded_frame);
+ return decoded_frame;
+ }
+
+ // VPX decoder context (i.e., an instantiation).
+ vpx_codec_ctx_t context_;
+
+ DISALLOW_COPY_AND_ASSIGN(Vp8Impl);
+};
+
+#ifndef OFFICIAL_BUILD
+// A fake video decoder that always output 2x2 black frames.
+class VideoDecoder::FakeImpl : public VideoDecoder::ImplBase {
+ public:
+ explicit FakeImpl(const scoped_refptr<CastEnvironment>& cast_environment)
+ : ImplBase(cast_environment, transport::kFakeSoftwareVideo),
+ last_decoded_id_(-1) {
+ if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED)
+ return;
+ ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
+ }
+
+ private:
+ virtual ~FakeImpl() {}
+
+ virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE {
+ base::JSONReader reader;
+ scoped_ptr<base::Value> values(
+ reader.Read(base::StringPiece(reinterpret_cast<char*>(data))));
+ base::DictionaryValue* dict = NULL;
+ values->GetAsDictionary(&dict);
+
+ bool key = false;
+ int id = 0;
+ int ref = 0;
+ dict->GetBoolean("key", &key);
+ dict->GetInteger("id", &id);
+ dict->GetInteger("ref", &ref);
+ DCHECK(id == last_decoded_id_ + 1);
+ last_decoded_id_ = id;
+ return media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
+ }
+
+ int last_decoded_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeImpl);
+};
+#endif
+
+VideoDecoder::VideoDecoder(
+ const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::VideoCodec codec)
+ : cast_environment_(cast_environment) {
+ switch (codec) {
+#ifndef OFFICIAL_BUILD
+ case transport::kFakeSoftwareVideo:
+ impl_ = new FakeImpl(cast_environment);
+ break;
+#endif
+ case transport::kVp8:
+ impl_ = new Vp8Impl(cast_environment);
+ break;
+ case transport::kH264:
+ // TODO(miu): Need implementation.
+ NOTIMPLEMENTED();
+ break;
+ default:
+ NOTREACHED() << "Unknown or unspecified codec.";
+ break;
+ }
+}
+
+VideoDecoder::~VideoDecoder() {}
+
+CastInitializationStatus VideoDecoder::InitializationResult() const {
+ if (impl_)
+ return impl_->InitializationResult();
+ return STATUS_UNSUPPORTED_VIDEO_CODEC;
+}
+
+void VideoDecoder::DecodeFrame(
+ scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback) {
+ DCHECK(encoded_frame.get());
+ DCHECK(!callback.is_null());
+ if (!impl_ || impl_->InitializationResult() != STATUS_VIDEO_INITIALIZED) {
+ callback.Run(make_scoped_refptr<VideoFrame>(NULL), false);
+ return;
+ }
+ cast_environment_->PostTask(CastEnvironment::VIDEO,
+ FROM_HERE,
+ base::Bind(&VideoDecoder::ImplBase::DecodeFrame,
+ impl_,
+ base::Passed(&encoded_frame),
+ callback));
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/receiver/video_decoder.h b/chromium/media/cast/receiver/video_decoder.h
new file mode 100644
index 00000000000..66dc36bb2ac
--- /dev/null
+++ b/chromium/media/cast/receiver/video_decoder.h
@@ -0,0 +1,63 @@
+// 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_VIDEO_DECODER_H_
+#define MEDIA_CAST_RECEIVER_VIDEO_DECODER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/video_frame.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/transport/cast_transport_config.h"
+
+namespace media {
+namespace cast {
+
+class CastEnvironment;
+
+class VideoDecoder {
+ public:
+ // Callback passed to DecodeFrame, to deliver a decoded video frame from the
+ // decoder. |frame| can be NULL when errors occur. |is_continuous| is
+ // normally true, but will be false if the decoder has detected a frame skip
+ // since the last decode operation; and the client might choose to take steps
+ // to smooth/interpolate video discontinuities in this case.
+ typedef base::Callback<void(const scoped_refptr<VideoFrame>& frame,
+ bool is_continuous)> DecodeFrameCallback;
+
+ VideoDecoder(const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::VideoCodec codec);
+ virtual ~VideoDecoder();
+
+ // Returns STATUS_VIDEO_INITIALIZED if the decoder was successfully
+ // constructed from the given FrameReceiverConfig. If this method returns any
+ // other value, calls to DecodeFrame() will not succeed.
+ CastInitializationStatus InitializationResult() const;
+
+ // Decode the payload in |encoded_frame| asynchronously. |callback| will be
+ // invoked on the CastEnvironment::MAIN thread with the result.
+ //
+ // In the normal case, |encoded_frame->frame_id| will be
+ // monotonically-increasing by 1 for each successive call to this method.
+ // When it is not, the decoder will assume one or more frames have been
+ // dropped (e.g., due to packet loss), and will perform recovery actions.
+ void DecodeFrame(scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback);
+
+ private:
+ class FakeImpl;
+ class ImplBase;
+ class Vp8Impl;
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ scoped_refptr<ImplBase> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoDecoder);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RECEIVER_VIDEO_DECODER_H_
diff --git a/chromium/media/cast/receiver/video_decoder_unittest.cc b/chromium/media/cast/receiver/video_decoder_unittest.cc
new file mode 100644
index 00000000000..1d16534b968
--- /dev/null
+++ b/chromium/media/cast/receiver/video_decoder_unittest.cc
@@ -0,0 +1,183 @@
+// 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 <cstdlib>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "media/cast/cast_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_sender/codecs/vp8/vp8_encoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+namespace {
+
+const int kWidth = 360;
+const int kHeight = 240;
+const int kFrameRate = 10;
+
+VideoSenderConfig GetVideoSenderConfigForTest() {
+ VideoSenderConfig config;
+ config.width = kWidth;
+ config.height = kHeight;
+ config.max_frame_rate = kFrameRate;
+ return config;
+}
+
+} // namespace
+
+class VideoDecoderTest
+ : public ::testing::TestWithParam<transport::VideoCodec> {
+ public:
+ VideoDecoderTest()
+ : cast_environment_(new StandaloneCastEnvironment()),
+ vp8_encoder_(GetVideoSenderConfigForTest(), 0),
+ cond_(&lock_) {
+ vp8_encoder_.Initialize();
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ video_decoder_.reset(new VideoDecoder(cast_environment_, GetParam()));
+ CHECK_EQ(STATUS_VIDEO_INITIALIZED, video_decoder_->InitializationResult());
+
+ next_frame_timestamp_ = base::TimeDelta();
+ last_frame_id_ = 0;
+ seen_a_decoded_frame_ = false;
+
+ total_video_frames_feed_in_ = 0;
+ total_video_frames_decoded_ = 0;
+ }
+
+ // Called from the unit test thread to create another EncodedFrame and push it
+ // into the decoding pipeline.
+ void FeedMoreVideo(int num_dropped_frames) {
+ // Prepare a simulated EncodedFrame to feed into the VideoDecoder.
+
+ const gfx::Size frame_size(kWidth, kHeight);
+ const scoped_refptr<VideoFrame> video_frame =
+ VideoFrame::CreateFrame(VideoFrame::YV12,
+ frame_size,
+ gfx::Rect(frame_size),
+ frame_size,
+ next_frame_timestamp_);
+ next_frame_timestamp_ += base::TimeDelta::FromSeconds(1) / kFrameRate;
+ PopulateVideoFrame(video_frame, 0);
+
+ // Encode |frame| into |encoded_frame->data|.
+ scoped_ptr<transport::EncodedFrame> encoded_frame(
+ new transport::EncodedFrame());
+ CHECK_EQ(transport::kVp8, GetParam()); // Only support VP8 test currently.
+ vp8_encoder_.Encode(video_frame, encoded_frame.get());
+ encoded_frame->frame_id = last_frame_id_ + 1 + num_dropped_frames;
+ last_frame_id_ = encoded_frame->frame_id;
+
+ {
+ base::AutoLock auto_lock(lock_);
+ ++total_video_frames_feed_in_;
+ }
+
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&VideoDecoder::DecodeFrame,
+ base::Unretained(video_decoder_.get()),
+ base::Passed(&encoded_frame),
+ base::Bind(&VideoDecoderTest::OnDecodedFrame,
+ base::Unretained(this),
+ video_frame,
+ num_dropped_frames == 0)));
+ }
+
+ // Blocks the caller until all video that has been feed in has been decoded.
+ void WaitForAllVideoToBeDecoded() {
+ DCHECK(!cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ base::AutoLock auto_lock(lock_);
+ while (total_video_frames_decoded_ < total_video_frames_feed_in_)
+ cond_.Wait();
+ EXPECT_EQ(total_video_frames_feed_in_, total_video_frames_decoded_);
+ }
+
+ private:
+ // Called by |vp8_decoder_| to deliver each frame of decoded video.
+ void OnDecodedFrame(const scoped_refptr<VideoFrame>& expected_video_frame,
+ bool should_be_continuous,
+ const scoped_refptr<VideoFrame>& video_frame,
+ bool is_continuous) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+
+ // A NULL |video_frame| indicates a decode error, which we don't expect.
+ ASSERT_FALSE(!video_frame);
+
+ // Did the decoder detect whether frames were dropped?
+ EXPECT_EQ(should_be_continuous, is_continuous);
+
+ // Does the video data seem to be intact?
+ EXPECT_EQ(expected_video_frame->coded_size().width(),
+ video_frame->coded_size().width());
+ EXPECT_EQ(expected_video_frame->coded_size().height(),
+ video_frame->coded_size().height());
+ EXPECT_LT(40.0, I420PSNR(expected_video_frame, video_frame));
+ // TODO(miu): Once we start using VideoFrame::timestamp_, check that here.
+
+ // Signal the main test thread that more video was decoded.
+ base::AutoLock auto_lock(lock_);
+ ++total_video_frames_decoded_;
+ cond_.Signal();
+ }
+
+ const scoped_refptr<StandaloneCastEnvironment> cast_environment_;
+ scoped_ptr<VideoDecoder> video_decoder_;
+ base::TimeDelta next_frame_timestamp_;
+ uint32 last_frame_id_;
+ bool seen_a_decoded_frame_;
+
+ Vp8Encoder vp8_encoder_;
+
+ base::Lock lock_;
+ base::ConditionVariable cond_;
+ int total_video_frames_feed_in_;
+ int total_video_frames_decoded_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoDecoderTest);
+};
+
+TEST_P(VideoDecoderTest, DecodesFrames) {
+ const int kNumFrames = 10;
+ for (int i = 0; i < kNumFrames; ++i)
+ FeedMoreVideo(0);
+ WaitForAllVideoToBeDecoded();
+}
+
+TEST_P(VideoDecoderTest, RecoversFromDroppedFrames) {
+ const int kNumFrames = 100;
+ int next_drop_at = 3;
+ int next_num_dropped = 1;
+ for (int i = 0; i < kNumFrames; ++i) {
+ if (i == next_drop_at) {
+ const int num_dropped = next_num_dropped++;
+ next_drop_at *= 2;
+ i += num_dropped;
+ FeedMoreVideo(num_dropped);
+ } else {
+ FeedMoreVideo(0);
+ }
+ }
+ WaitForAllVideoToBeDecoded();
+}
+
+INSTANTIATE_TEST_CASE_P(VideoDecoderTestScenarios,
+ VideoDecoderTest,
+ ::testing::Values(transport::kVp8));
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.cc b/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.cc
index daaa1ad0883..9ff2d48f03c 100644
--- a/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.cc
+++ b/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.cc
@@ -7,17 +7,13 @@
namespace media {
namespace cast {
-MockRtcpReceiverFeedback::MockRtcpReceiverFeedback() {
-}
+MockRtcpReceiverFeedback::MockRtcpReceiverFeedback() {}
-MockRtcpReceiverFeedback::~MockRtcpReceiverFeedback() {
-}
+MockRtcpReceiverFeedback::~MockRtcpReceiverFeedback() {}
-MockRtcpRttFeedback::MockRtcpRttFeedback() {
-}
+MockRtcpRttFeedback::MockRtcpRttFeedback() {}
-MockRtcpRttFeedback::~MockRtcpRttFeedback() {
-}
+MockRtcpRttFeedback::~MockRtcpRttFeedback() {}
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.h b/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.h
index 0316d9819f2..56fe1ca6995 100644
--- a/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.h
+++ b/chromium/media/cast/rtcp/mock_rtcp_receiver_feedback.h
@@ -7,7 +7,9 @@
#include <vector>
+#include "media/cast/rtcp/rtcp_defines.h"
#include "media/cast/rtcp/rtcp_receiver.h"
+#include "media/cast/transport/cast_transport_defines.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
@@ -19,7 +21,7 @@ class MockRtcpReceiverFeedback : public RtcpReceiverFeedback {
virtual ~MockRtcpReceiverFeedback();
MOCK_METHOD1(OnReceivedSenderReport,
- void(const RtcpSenderInfo& remote_sender_info));
+ void(const transport::RtcpSenderInfo& remote_sender_info));
MOCK_METHOD1(OnReceiverReferenceTimeReport,
void(const RtcpReceiverReferenceTimeReport& remote_time_report));
@@ -28,8 +30,6 @@ class MockRtcpReceiverFeedback : public RtcpReceiverFeedback {
MOCK_METHOD1(OnReceivedReceiverLog,
void(const RtcpReceiverLogMessage& receiver_log));
- MOCK_METHOD1(OnReceivedSenderLog,
- void(const RtcpSenderLogMessage& sender_log));
};
class MockRtcpRttFeedback : public RtcpRttFeedback {
diff --git a/chromium/media/cast/rtcp/mock_rtcp_sender_feedback.cc b/chromium/media/cast/rtcp/mock_rtcp_sender_feedback.cc
index 65c630148c2..e44e0bfdef4 100644
--- a/chromium/media/cast/rtcp/mock_rtcp_sender_feedback.cc
+++ b/chromium/media/cast/rtcp/mock_rtcp_sender_feedback.cc
@@ -7,11 +7,9 @@
namespace media {
namespace cast {
-MockRtcpSenderFeedback::MockRtcpSenderFeedback() {
-}
+MockRtcpSenderFeedback::MockRtcpSenderFeedback() {}
-MockRtcpSenderFeedback::~MockRtcpSenderFeedback() {
-}
+MockRtcpSenderFeedback::~MockRtcpSenderFeedback() {}
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.cc b/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.cc
new file mode 100644
index 00000000000..9a9c0aeeb74
--- /dev/null
+++ b/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.cc
@@ -0,0 +1,96 @@
+// 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/rtcp/receiver_rtcp_event_subscriber.h"
+
+#include <utility>
+
+namespace media {
+namespace cast {
+
+ReceiverRtcpEventSubscriber::ReceiverRtcpEventSubscriber(
+ const size_t max_size_to_retain, EventMediaType type)
+ : max_size_to_retain_(max_size_to_retain), type_(type) {
+ DCHECK(max_size_to_retain_ > 0u);
+ DCHECK(type_ == AUDIO_EVENT || type_ == VIDEO_EVENT);
+}
+
+ReceiverRtcpEventSubscriber::~ReceiverRtcpEventSubscriber() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void ReceiverRtcpEventSubscriber::OnReceiveFrameEvent(
+ const FrameEvent& frame_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (ShouldProcessEvent(frame_event.type, frame_event.media_type)) {
+ RtcpEvent rtcp_event;
+ switch (frame_event.type) {
+ case FRAME_PLAYOUT:
+ rtcp_event.delay_delta = frame_event.delay_delta;
+ case FRAME_ACK_SENT:
+ case FRAME_DECODED:
+ rtcp_event.type = frame_event.type;
+ rtcp_event.timestamp = frame_event.timestamp;
+ rtcp_events_.insert(
+ std::make_pair(frame_event.rtp_timestamp, rtcp_event));
+ break;
+ default:
+ break;
+ }
+ }
+
+ TruncateMapIfNeeded();
+
+ DCHECK(rtcp_events_.size() <= max_size_to_retain_);
+}
+
+void ReceiverRtcpEventSubscriber::OnReceivePacketEvent(
+ const PacketEvent& packet_event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (ShouldProcessEvent(packet_event.type, packet_event.media_type)) {
+ RtcpEvent rtcp_event;
+ if (packet_event.type == PACKET_RECEIVED) {
+ rtcp_event.type = packet_event.type;
+ rtcp_event.timestamp = packet_event.timestamp;
+ rtcp_event.packet_id = packet_event.packet_id;
+ rtcp_events_.insert(
+ std::make_pair(packet_event.rtp_timestamp, rtcp_event));
+ }
+ }
+
+ TruncateMapIfNeeded();
+
+ DCHECK(rtcp_events_.size() <= max_size_to_retain_);
+}
+
+void ReceiverRtcpEventSubscriber::GetRtcpEventsAndReset(
+ RtcpEventMultiMap* rtcp_events) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(rtcp_events);
+ rtcp_events->swap(rtcp_events_);
+ rtcp_events_.clear();
+}
+
+void ReceiverRtcpEventSubscriber::TruncateMapIfNeeded() {
+ // If map size has exceeded |max_size_to_retain_|, remove entry with
+ // the smallest RTP timestamp.
+ if (rtcp_events_.size() > max_size_to_retain_) {
+ DVLOG(3) << "RTCP event map exceeded size limit; "
+ << "removing oldest entry";
+ // This is fine since we only insert elements one at a time.
+ rtcp_events_.erase(rtcp_events_.begin());
+ }
+}
+
+bool ReceiverRtcpEventSubscriber::ShouldProcessEvent(
+ CastLoggingEvent event_type, EventMediaType event_media_type) {
+ return type_ == event_media_type &&
+ (event_type == FRAME_ACK_SENT || event_type == FRAME_DECODED ||
+ event_type == FRAME_PLAYOUT || event_type == PACKET_RECEIVED);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.h b/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.h
new file mode 100644
index 00000000000..84af7cbaf3f
--- /dev/null
+++ b/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber.h
@@ -0,0 +1,79 @@
+// 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_RTCP_RECEIVER_RTCP_EVENT_SUBSCRIBER_H_
+#define MEDIA_CAST_RTCP_RECEIVER_RTCP_EVENT_SUBSCRIBER_H_
+
+#include <map>
+
+#include "base/threading/thread_checker.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/raw_event_subscriber.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+namespace media {
+namespace cast {
+
+// A RawEventSubscriber implementation with the following properties:
+// - Only processes raw event types that are relevant for sending from cast
+// receiver to cast sender via RTCP.
+// - Captures information to be sent over to RTCP from raw event logs into the
+// more compact RtcpEvent struct.
+// - Orders events by RTP timestamp with a multimap.
+// - Internally, the map is capped at a maximum size configurable by the caller.
+// The subscriber only keeps the most recent events (determined by RTP
+// timestamp) up to the size limit.
+class ReceiverRtcpEventSubscriber : public RawEventSubscriber {
+ public:
+ typedef std::multimap<RtpTimestamp, RtcpEvent> RtcpEventMultiMap;
+
+ // |max_size_to_retain|: The object will keep up to |max_size_to_retain|
+ // events
+ // in the map. Once threshold has been reached, an event with the smallest
+ // RTP timestamp will be removed.
+ // |type|: Determines whether the subscriber will process only audio or video
+ // events.
+ ReceiverRtcpEventSubscriber(const size_t max_size_to_retain,
+ EventMediaType type);
+
+ virtual ~ReceiverRtcpEventSubscriber();
+
+ // RawEventSubscriber implementation.
+ virtual void OnReceiveFrameEvent(const FrameEvent& frame_event) OVERRIDE;
+ virtual void OnReceivePacketEvent(const PacketEvent& packet_event) OVERRIDE;
+
+ // Assigns events collected to |rtcp_events| and clears them from this
+ // object.
+ void GetRtcpEventsAndReset(RtcpEventMultiMap* rtcp_events);
+
+ private:
+ // If |rtcp_events_.size()| exceeds |max_size_to_retain_|, remove an oldest
+ // entry (determined by RTP timestamp) so its size no greater than
+ // |max_size_to_retain_|.
+ void TruncateMapIfNeeded();
+
+ // Returns |true| if events of |event_type| and |media_type|
+ // should be processed.
+ bool ShouldProcessEvent(CastLoggingEvent event_type,
+ EventMediaType media_type);
+
+ const size_t max_size_to_retain_;
+ EventMediaType type_;
+
+ // The key should really be something more than just a RTP timestamp in order
+ // to differentiate between video and audio frames, but since the
+ // implementation doesn't mix audio and video frame events, RTP timestamp
+ // only as key is fine.
+ RtcpEventMultiMap rtcp_events_;
+
+ // Ensures methods are only called on the main thread.
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceiverRtcpEventSubscriber);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTCP_RECEIVER_RTCP_EVENT_SUBSCRIBER_H_
diff --git a/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber_unittest.cc b/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber_unittest.cc
new file mode 100644
index 00000000000..e0d0f172160
--- /dev/null
+++ b/chromium/media/cast/rtcp/receiver_rtcp_event_subscriber_unittest.cc
@@ -0,0 +1,131 @@
+// 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/rtcp/receiver_rtcp_event_subscriber.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+namespace {
+
+const size_t kMaxEventEntries = 10u;
+const int64 kDelayMs = 20L;
+
+} // namespace
+
+class ReceiverRtcpEventSubscriberTest : public ::testing::Test {
+ protected:
+ ReceiverRtcpEventSubscriberTest()
+ : testing_clock_(new base::SimpleTestTickClock()),
+ 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 ~ReceiverRtcpEventSubscriberTest() {}
+
+ virtual void TearDown() OVERRIDE {
+ if (event_subscriber_) {
+ cast_environment_->Logging()->RemoveRawEventSubscriber(
+ event_subscriber_.get());
+ }
+ }
+
+ void Init(EventMediaType type) {
+ event_subscriber_.reset(
+ new ReceiverRtcpEventSubscriber(kMaxEventEntries, type));
+ cast_environment_->Logging()->AddRawEventSubscriber(
+ event_subscriber_.get());
+ }
+
+ void InsertEvents() {
+ // Video events
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ testing_clock_->NowTicks(), FRAME_PLAYOUT, VIDEO_EVENT,
+ /*rtp_timestamp*/ 100u, /*frame_id*/ 2u,
+ base::TimeDelta::FromMilliseconds(kDelayMs));
+ cast_environment_->Logging()->InsertFrameEvent(
+ testing_clock_->NowTicks(), FRAME_DECODED, VIDEO_EVENT,
+ /*rtp_timestamp*/ 200u, /*frame_id*/ 1u);
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(), PACKET_RECEIVED, VIDEO_EVENT,
+ /*rtp_timestamp */ 200u, /*frame_id*/ 2u, /*packet_id*/ 1u,
+ /*max_packet_id*/ 10u, /*size*/ 1024u);
+
+ // Audio events
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ testing_clock_->NowTicks(), FRAME_PLAYOUT, AUDIO_EVENT,
+ /*rtp_timestamp*/ 300u, /*frame_id*/ 4u,
+ base::TimeDelta::FromMilliseconds(kDelayMs));
+ cast_environment_->Logging()->InsertFrameEvent(
+ testing_clock_->NowTicks(), FRAME_DECODED, AUDIO_EVENT,
+ /*rtp_timestamp*/ 400u, /*frame_id*/ 3u);
+ cast_environment_->Logging()->InsertPacketEvent(
+ testing_clock_->NowTicks(), PACKET_RECEIVED, AUDIO_EVENT,
+ /*rtp_timestamp */ 400u, /*frame_id*/ 5u, /*packet_id*/ 1u,
+ /*max_packet_id*/ 10u, /*size*/ 128u);
+
+ // Unrelated events
+ cast_environment_->Logging()->InsertFrameEvent(testing_clock_->NowTicks(),
+ FRAME_CAPTURE_END,
+ VIDEO_EVENT,
+ /*rtp_timestamp*/ 100u,
+ /*frame_id*/ 1u);
+ cast_environment_->Logging()->InsertFrameEvent(testing_clock_->NowTicks(),
+ FRAME_CAPTURE_END,
+ AUDIO_EVENT,
+ /*rtp_timestamp*/ 100u,
+ /*frame_id*/ 1u);
+ }
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ scoped_ptr<ReceiverRtcpEventSubscriber> event_subscriber_;
+};
+
+TEST_F(ReceiverRtcpEventSubscriberTest, LogVideoEvents) {
+ Init(VIDEO_EVENT);
+
+ InsertEvents();
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber_->GetRtcpEventsAndReset(&rtcp_events);
+ EXPECT_EQ(3u, rtcp_events.size());
+}
+
+TEST_F(ReceiverRtcpEventSubscriberTest, LogAudioEvents) {
+ Init(AUDIO_EVENT);
+
+ InsertEvents();
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber_->GetRtcpEventsAndReset(&rtcp_events);
+ EXPECT_EQ(3u, rtcp_events.size());
+}
+
+TEST_F(ReceiverRtcpEventSubscriberTest, DropEventsWhenSizeExceeded) {
+ Init(VIDEO_EVENT);
+
+ for (uint32 i = 1u; i <= 10u; ++i) {
+ cast_environment_->Logging()->InsertFrameEvent(
+ testing_clock_->NowTicks(), FRAME_DECODED, VIDEO_EVENT,
+ /*rtp_timestamp*/ i * 10, /*frame_id*/ i);
+ }
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber_->GetRtcpEventsAndReset(&rtcp_events);
+ EXPECT_EQ(10u, rtcp_events.size());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/rtcp/rtcp.cc b/chromium/media/cast/rtcp/rtcp.cc
index 4ea4bc99ba9..480b2ac3990 100644
--- a/chromium/media/cast/rtcp/rtcp.cc
+++ b/chromium/media/cast/rtcp/rtcp.cc
@@ -4,6 +4,7 @@
#include "media/cast/rtcp/rtcp.h"
+#include "base/big_endian.h"
#include "base/rand_util.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
@@ -12,28 +13,22 @@
#include "media/cast/rtcp/rtcp_receiver.h"
#include "media/cast/rtcp/rtcp_sender.h"
#include "media/cast/rtcp/rtcp_utility.h"
-#include "net/base/big_endian.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace media {
namespace cast {
static const int kMaxRttMs = 10000; // 10 seconds.
-
-// Time limit for received RTCP messages when we stop using it for lip-sync.
-static const int64 kMaxDiffSinceReceivedRtcpMs = 100000; // 100 seconds.
+static const int kMaxDelay = 2000;
class LocalRtcpRttFeedback : public RtcpRttFeedback {
public:
- explicit LocalRtcpRttFeedback(Rtcp* rtcp)
- : rtcp_(rtcp) {
- }
+ explicit LocalRtcpRttFeedback(Rtcp* rtcp) : rtcp_(rtcp) {}
virtual void OnReceivedDelaySinceLastReport(
- uint32 receivers_ssrc,
- uint32 last_report,
+ uint32 receivers_ssrc, uint32 last_report,
uint32 delay_since_last_report) OVERRIDE {
- rtcp_->OnReceivedDelaySinceLastReport(receivers_ssrc,
- last_report,
+ rtcp_->OnReceivedDelaySinceLastReport(receivers_ssrc, last_report,
delay_since_last_report);
}
@@ -41,31 +36,14 @@ class LocalRtcpRttFeedback : public RtcpRttFeedback {
Rtcp* rtcp_;
};
-RtcpCastMessage::RtcpCastMessage(uint32 media_ssrc)
- : media_ssrc_(media_ssrc) {}
-
-RtcpCastMessage::~RtcpCastMessage() {}
-
-RtcpNackMessage::RtcpNackMessage() {}
-RtcpNackMessage::~RtcpNackMessage() {}
-
-RtcpRembMessage::RtcpRembMessage() {}
-RtcpRembMessage::~RtcpRembMessage() {}
-
-RtcpReceiverFrameLogMessage::RtcpReceiverFrameLogMessage(uint32 timestamp)
- : rtp_timestamp_(timestamp) {}
-
-RtcpReceiverFrameLogMessage::~RtcpReceiverFrameLogMessage() {}
-
class LocalRtcpReceiverFeedback : public RtcpReceiverFeedback {
public:
LocalRtcpReceiverFeedback(Rtcp* rtcp,
- scoped_refptr<CastEnvironment> cast_environment)
- : rtcp_(rtcp), cast_environment_(cast_environment) {
- }
+ scoped_refptr<CastEnvironment> cast_environment)
+ : rtcp_(rtcp), cast_environment_(cast_environment) {}
virtual void OnReceivedSenderReport(
- const RtcpSenderInfo& remote_sender_info) OVERRIDE {
+ const transport::RtcpSenderInfo& remote_sender_info) OVERRIDE {
rtcp_->OnReceivedNtp(remote_sender_info.ntp_seconds,
remote_sender_info.ntp_fraction);
if (remote_sender_info.send_packet_count != 0) {
@@ -85,80 +63,9 @@ class LocalRtcpReceiverFeedback : public RtcpReceiverFeedback {
rtcp_->OnReceivedSendReportRequest();
}
- virtual void OnReceivedReceiverLog(
- const RtcpReceiverLogMessage& receiver_log) OVERRIDE {
- // Add received log messages into our log system.
- RtcpReceiverLogMessage::const_iterator it = receiver_log.begin();
-
- for (; it != receiver_log.end(); ++it) {
- uint32 rtp_timestamp = it->rtp_timestamp_;
-
- RtcpReceiverEventLogMessages::const_iterator event_it =
- it->event_log_messages_.begin();
- for (; event_it != it->event_log_messages_.end(); ++event_it) {
- // TODO(pwestin): we need to send in the event_it->event_timestamp to
- // the log system too.
- switch (event_it->type) {
- case kPacketReceived:
- cast_environment_->Logging()->InsertPacketEvent(kPacketReceived,
- rtp_timestamp, kFrameIdUnknown, event_it->packet_id, 0, 0);
- break;
- case kAckSent:
- case kAudioFrameDecoded:
- case kVideoFrameDecoded:
- cast_environment_->Logging()->InsertFrameEvent(event_it->type,
- rtp_timestamp, kFrameIdUnknown);
- break;
- case kAudioPlayoutDelay:
- case kVideoRenderDelay:
- cast_environment_->Logging()->InsertFrameEventWithDelay(
- event_it->type, rtp_timestamp, kFrameIdUnknown,
- event_it->delay_delta);
- break;
- default:
- VLOG(2) << "Received log message via RTCP that we did not expect: "
- << static_cast<int>(event_it->type);
- break;
- }
- }
- }
- }
-
- virtual void OnReceivedSenderLog(
- const RtcpSenderLogMessage& sender_log) OVERRIDE {
- RtcpSenderLogMessage::const_iterator it = sender_log.begin();
-
- for (; it != sender_log.end(); ++it) {
- uint32 rtp_timestamp = it->rtp_timestamp;
- CastLoggingEvent log_event = kUnknown;
-
- // These events are provided to know the status of frames that never
- // reached the receiver. The timing information for these events are not
- // relevant and is not sent over the wire.
- switch (it->frame_status) {
- case kRtcpSenderFrameStatusDroppedByFlowControl:
- // A frame that have been dropped by the flow control would have
- // kVideoFrameCaptured as its last event in the log.
- log_event = kVideoFrameCaptured;
- break;
- case kRtcpSenderFrameStatusDroppedByEncoder:
- // A frame that have been dropped by the encoder would have
- // kVideoFrameSentToEncoder as its last event in the log.
- log_event = kVideoFrameSentToEncoder;
- break;
- case kRtcpSenderFrameStatusSentToNetwork:
- // A frame that have be encoded is always sent to the network. We
- // do not add a new log entry for this.
- log_event = kVideoFrameEncoded;
- break;
- default:
- continue;
- }
- // TODO(pwestin): how do we handle the truncated rtp_timestamp?
- // Add received log messages into our log system.
- cast_environment_->Logging()->InsertFrameEvent(log_event, rtp_timestamp,
- kFrameIdUnknown);
- }
+ virtual void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log)
+ OVERRIDE {
+ rtcp_->OnReceivedReceiverLog(receiver_log);
}
private:
@@ -168,36 +75,34 @@ class LocalRtcpReceiverFeedback : public RtcpReceiverFeedback {
Rtcp::Rtcp(scoped_refptr<CastEnvironment> cast_environment,
RtcpSenderFeedback* sender_feedback,
- PacedPacketSender* paced_packet_sender,
- RtpSenderStatistics* rtp_sender_statistics,
- RtpReceiverStatistics* rtp_receiver_statistics,
- RtcpMode rtcp_mode,
- const base::TimeDelta& rtcp_interval,
- uint32 local_ssrc,
- uint32 remote_ssrc,
- const std::string& c_name)
- : rtcp_interval_(rtcp_interval),
+ transport::CastTransportSender* const transport_sender,
+ 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,
+ EventMediaType event_media_type)
+ : cast_environment_(cast_environment),
+ transport_sender_(transport_sender),
+ rtcp_interval_(rtcp_interval),
rtcp_mode_(rtcp_mode),
local_ssrc_(local_ssrc),
remote_ssrc_(remote_ssrc),
- rtp_sender_statistics_(rtp_sender_statistics),
+ c_name_(c_name),
+ event_media_type_(event_media_type),
rtp_receiver_statistics_(rtp_receiver_statistics),
- receiver_feedback_(new LocalRtcpReceiverFeedback(this, cast_environment)),
rtt_feedback_(new LocalRtcpRttFeedback(this)),
+ receiver_feedback_(new LocalRtcpReceiverFeedback(this, cast_environment)),
rtcp_sender_(new RtcpSender(cast_environment, paced_packet_sender,
local_ssrc, c_name)),
- last_report_received_(0),
- last_received_rtp_timestamp_(0),
- last_received_ntp_seconds_(0),
- last_received_ntp_fraction_(0),
+ last_report_truncated_ntp_(0),
+ local_clock_ahead_by_(ClockDriftSmoother::GetDefaultTimeConstant()),
+ lip_sync_rtp_timestamp_(0),
+ lip_sync_ntp_timestamp_(0),
min_rtt_(base::TimeDelta::FromMilliseconds(kMaxRttMs)),
- number_of_rtt_in_avg_(0),
- cast_environment_(cast_environment) {
- rtcp_receiver_.reset(new RtcpReceiver(cast_environment,
- sender_feedback,
+ number_of_rtt_in_avg_(0) {
+ rtcp_receiver_.reset(new RtcpReceiver(cast_environment, sender_feedback,
receiver_feedback_.get(),
- rtt_feedback_.get(),
- local_ssrc));
+ rtt_feedback_.get(), local_ssrc));
rtcp_receiver_->SetRemoteSSRC(remote_ssrc);
}
@@ -209,7 +114,8 @@ bool Rtcp::IsRtcpPacket(const uint8* packet, size_t length) {
if (length < kMinLengthOfRtcp) return false;
uint8 packet_type = packet[1];
- if (packet_type >= kPacketTypeLow && packet_type <= kPacketTypeHigh) {
+ if (packet_type >= transport::kPacketTypeLow &&
+ packet_type <= transport::kPacketTypeHigh) {
return true;
}
return false;
@@ -219,7 +125,8 @@ bool Rtcp::IsRtcpPacket(const uint8* packet, size_t length) {
uint32 Rtcp::GetSsrcOfSender(const uint8* rtcp_buffer, size_t length) {
DCHECK_GE(length, kMinLengthOfRtcp) << "Invalid RTCP packet";
uint32 ssrc_of_sender;
- net::BigEndianReader big_endian_reader(rtcp_buffer, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_buffer), length);
big_endian_reader.Skip(4); // Skip header
big_endian_reader.ReadU32(&ssrc_of_sender);
return ssrc_of_sender;
@@ -242,117 +149,145 @@ void Rtcp::IncomingRtcpPacket(const uint8* rtcp_buffer, size_t length) {
rtcp_receiver_->IncomingRtcpPacket(&rtcp_parser);
}
-void Rtcp::SendRtcpFromRtpReceiver(const RtcpCastMessage* cast_message,
- RtcpReceiverLogMessage* receiver_log) {
+void Rtcp::SendRtcpFromRtpReceiver(
+ const RtcpCastMessage* cast_message,
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
uint32 packet_type_flags = 0;
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
- RtcpReportBlock report_block;
+ transport::RtcpReportBlock report_block;
RtcpReceiverReferenceTimeReport rrtr;
+ // Attach our NTP to all RTCP packets; with this information a "smart" sender
+ // can make decisions based on how old the RTCP message is.
+ packet_type_flags |= transport::kRtcpRrtr;
+ ConvertTimeTicksToNtp(now, &rrtr.ntp_seconds, &rrtr.ntp_fraction);
+ SaveLastSentNtpTime(now, rrtr.ntp_seconds, rrtr.ntp_fraction);
+
if (cast_message) {
- packet_type_flags |= RtcpSender::kRtcpCast;
- cast_environment_->Logging()->InsertGenericEvent(kAckSent,
- cast_message->ack_frame_id_);
+ packet_type_flags |= transport::kRtcpCast;
}
- if (receiver_log) {
- packet_type_flags |= RtcpSender::kRtcpReceiverLog;
+ if (rtcp_events) {
+ packet_type_flags |= transport::kRtcpReceiverLog;
}
if (rtcp_mode_ == kRtcpCompound || now >= next_time_to_send_rtcp_) {
- packet_type_flags |= RtcpSender::kRtcpRr;
+ packet_type_flags |= transport::kRtcpRr;
- report_block.remote_ssrc = 0; // Not needed to set send side.
+ report_block.remote_ssrc = 0; // Not needed to set send side.
report_block.media_ssrc = remote_ssrc_; // SSRC of the RTP packet sender.
if (rtp_receiver_statistics_) {
rtp_receiver_statistics_->GetStatistics(
- &report_block.fraction_lost,
- &report_block.cumulative_lost,
- &report_block.extended_high_sequence_number,
- &report_block.jitter);
- cast_environment_->Logging()->InsertGenericEvent(kJitterMs,
- report_block.jitter);
- cast_environment_->Logging()->InsertGenericEvent(kPacketLoss,
- report_block.fraction_lost);
-
+ &report_block.fraction_lost, &report_block.cumulative_lost,
+ &report_block.extended_high_sequence_number, &report_block.jitter);
}
- report_block.last_sr = last_report_received_;
+ report_block.last_sr = last_report_truncated_ntp_;
if (!time_last_report_received_.is_null()) {
uint32 delay_seconds = 0;
uint32 delay_fraction = 0;
base::TimeDelta delta = now - time_last_report_received_;
- ConvertTimeToFractions(delta.InMicroseconds(),
- &delay_seconds,
+ ConvertTimeToFractions(delta.InMicroseconds(), &delay_seconds,
&delay_fraction);
report_block.delay_since_last_sr =
ConvertToNtpDiff(delay_seconds, delay_fraction);
} else {
report_block.delay_since_last_sr = 0;
}
-
- packet_type_flags |= RtcpSender::kRtcpRrtr;
- ConvertTimeTicksToNtp(now, &rrtr.ntp_seconds, &rrtr.ntp_fraction);
- SaveLastSentNtpTime(now, rrtr.ntp_seconds, rrtr.ntp_fraction);
UpdateNextTimeToSendRtcp();
}
rtcp_sender_->SendRtcpFromRtpReceiver(packet_type_flags,
&report_block,
&rrtr,
cast_message,
- receiver_log);
+ rtcp_events,
+ target_delay_ms_);
}
-void Rtcp::SendRtcpFromRtpSender(
- RtcpSenderLogMessage* sender_log_message) {
- uint32 packet_type_flags = RtcpSender::kRtcpSr;
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
-
- if (sender_log_message) {
- packet_type_flags |= RtcpSender::kRtcpSenderLog;
- }
-
- RtcpSenderInfo sender_info;
- if (rtp_sender_statistics_) {
- rtp_sender_statistics_->GetStatistics(now, &sender_info);
- } else {
- memset(&sender_info, 0, sizeof(sender_info));
- }
- SaveLastSentNtpTime(now, sender_info.ntp_seconds, sender_info.ntp_fraction);
-
- RtcpDlrrReportBlock dlrr;
+void Rtcp::SendRtcpFromRtpSender(base::TimeTicks current_time,
+ uint32 current_time_as_rtp_timestamp) {
+ DCHECK(transport_sender_);
+ uint32 packet_type_flags = transport::kRtcpSr;
+ uint32 current_ntp_seconds = 0;
+ uint32 current_ntp_fractions = 0;
+ ConvertTimeTicksToNtp(current_time, &current_ntp_seconds,
+ &current_ntp_fractions);
+ SaveLastSentNtpTime(current_time, current_ntp_seconds,
+ current_ntp_fractions);
+
+ transport::RtcpDlrrReportBlock dlrr;
if (!time_last_report_received_.is_null()) {
- packet_type_flags |= RtcpSender::kRtcpDlrr;
- dlrr.last_rr = last_report_received_;
+ packet_type_flags |= transport::kRtcpDlrr;
+ dlrr.last_rr = last_report_truncated_ntp_;
uint32 delay_seconds = 0;
uint32 delay_fraction = 0;
- base::TimeDelta delta = now - time_last_report_received_;
- ConvertTimeToFractions(delta.InMicroseconds(),
- &delay_seconds,
+ base::TimeDelta delta = current_time - time_last_report_received_;
+ ConvertTimeToFractions(delta.InMicroseconds(), &delay_seconds,
&delay_fraction);
dlrr.delay_since_last_rr = ConvertToNtpDiff(delay_seconds, delay_fraction);
}
- rtcp_sender_->SendRtcpFromRtpSender(packet_type_flags,
- &sender_info,
- &dlrr,
- sender_log_message);
+ transport_sender_->SendRtcpFromRtpSender(
+ packet_type_flags, current_ntp_seconds, current_ntp_fractions,
+ current_time_as_rtp_timestamp, dlrr, local_ssrc_, c_name_);
UpdateNextTimeToSendRtcp();
}
void Rtcp::OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction) {
- last_report_received_ = (ntp_seconds << 16) + (ntp_fraction >> 16);
+ last_report_truncated_ntp_ = ConvertToNtpDiff(ntp_seconds, ntp_fraction);
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
+ const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
time_last_report_received_ = now;
+
+ // TODO(miu): This clock offset calculation does not account for packet
+ // transit time over the network. End2EndTest.EvilNetwork confirms that this
+ // contributes a very significant source of error here. Fix this along with
+ // the RTT clean-up.
+ const base::TimeDelta measured_offset =
+ now - ConvertNtpToTimeTicks(ntp_seconds, ntp_fraction);
+ local_clock_ahead_by_.Update(now, measured_offset);
+ if (measured_offset < local_clock_ahead_by_.Current()) {
+ // Logically, the minimum offset between the clocks has to be the correct
+ // one. For example, the time it took to transmit the current report may
+ // have been lower than usual, and so some of the error introduced by the
+ // transmission time can be eliminated.
+ local_clock_ahead_by_.Reset(now, measured_offset);
+ }
+ VLOG(1) << "Local clock is ahead of the remote clock by: "
+ << "measured=" << measured_offset.InMicroseconds() << " usec, "
+ << "filtered=" << local_clock_ahead_by_.Current().InMicroseconds()
+ << " usec.";
}
-void Rtcp::OnReceivedLipSyncInfo(uint32 rtp_timestamp,
- uint32 ntp_seconds,
+void Rtcp::OnReceivedLipSyncInfo(uint32 rtp_timestamp, uint32 ntp_seconds,
uint32 ntp_fraction) {
- last_received_rtp_timestamp_ = rtp_timestamp;
- last_received_ntp_seconds_ = ntp_seconds;
- last_received_ntp_fraction_ = ntp_fraction;
+ if (ntp_seconds == 0) {
+ NOTREACHED();
+ return;
+ }
+ lip_sync_rtp_timestamp_ = rtp_timestamp;
+ lip_sync_ntp_timestamp_ =
+ (static_cast<uint64>(ntp_seconds) << 32) | ntp_fraction;
+}
+
+bool Rtcp::GetLatestLipSyncTimes(uint32* rtp_timestamp,
+ base::TimeTicks* reference_time) const {
+ if (!lip_sync_ntp_timestamp_)
+ return false;
+
+ const base::TimeTicks local_reference_time =
+ ConvertNtpToTimeTicks(static_cast<uint32>(lip_sync_ntp_timestamp_ >> 32),
+ static_cast<uint32>(lip_sync_ntp_timestamp_)) +
+ local_clock_ahead_by_.Current();
+
+ // Sanity-check: Getting regular lip sync updates?
+ DCHECK((cast_environment_->Clock()->NowTicks() - local_reference_time) <
+ base::TimeDelta::FromMinutes(1));
+
+ *rtp_timestamp = lip_sync_rtp_timestamp_;
+ *reference_time = local_reference_time;
+ return true;
}
void Rtcp::OnReceivedSendReportRequest() {
@@ -362,35 +297,13 @@ void Rtcp::OnReceivedSendReportRequest() {
next_time_to_send_rtcp_ = now;
}
-bool Rtcp::RtpTimestampInSenderTime(int frequency, uint32 rtp_timestamp,
- base::TimeTicks* rtp_timestamp_in_ticks) const {
- if (last_received_ntp_seconds_ == 0) return false;
-
- int wrap = CheckForWrapAround(rtp_timestamp, last_received_rtp_timestamp_);
- int64 rtp_timestamp_int64 = rtp_timestamp;
- int64 last_received_rtp_timestamp_int64 = last_received_rtp_timestamp_;
-
- if (wrap == 1) {
- rtp_timestamp_int64 += (1LL << 32);
- } else if (wrap == -1) {
- last_received_rtp_timestamp_int64 += (1LL << 32);
- }
- // Time since the last RTCP message.
- // Note that this can be negative since we can compare a rtp timestamp from
- // a frame older than the last received RTCP message.
- int64 rtp_timestamp_diff =
- rtp_timestamp_int64 - last_received_rtp_timestamp_int64;
-
- int frequency_khz = frequency / 1000;
- int64 rtp_time_diff_ms = rtp_timestamp_diff / frequency_khz;
-
- // Sanity check.
- if (abs(rtp_time_diff_ms) > kMaxDiffSinceReceivedRtcpMs) return false;
+void Rtcp::SetCastReceiverEventHistorySize(size_t size) {
+ rtcp_receiver_->SetCastReceiverEventHistorySize(size);
+}
- *rtp_timestamp_in_ticks = ConvertNtpToTimeTicks(last_received_ntp_seconds_,
- last_received_ntp_fraction_) +
- base::TimeDelta::FromMilliseconds(rtp_time_diff_ms);
- return true;
+void Rtcp::SetTargetDelay(base::TimeDelta target_delay) {
+ DCHECK(target_delay.InMilliseconds() < kMaxDelay);
+ target_delay_ms_ = static_cast<uint16>(target_delay.InMilliseconds());
}
void Rtcp::OnReceivedDelaySinceLastReport(uint32 receivers_ssrc,
@@ -401,8 +314,8 @@ void Rtcp::OnReceivedDelaySinceLastReport(uint32 receivers_ssrc,
return; // Feedback on another report.
}
- base::TimeDelta sender_delay = cast_environment_->Clock()->NowTicks()
- - it->second;
+ base::TimeDelta sender_delay =
+ cast_environment_->Clock()->NowTicks() - it->second;
UpdateRtt(sender_delay, ConvertFromNtpDiff(delay_since_last_report));
}
@@ -411,9 +324,8 @@ void Rtcp::SaveLastSentNtpTime(const base::TimeTicks& now,
uint32 last_ntp_fraction) {
// Make sure |now| is always greater than the last element in
// |last_reports_sent_queue_|.
- if (!last_reports_sent_queue_.empty()) {
+ if (!last_reports_sent_queue_.empty())
DCHECK(now >= last_reports_sent_queue_.back().second);
- }
uint32 last_report = ConvertToNtpDiff(last_ntp_seconds, last_ntp_fraction);
last_reports_sent_map_[last_report] = now;
@@ -436,66 +348,85 @@ void Rtcp::SaveLastSentNtpTime(const base::TimeTicks& now,
void Rtcp::UpdateRtt(const base::TimeDelta& sender_delay,
const base::TimeDelta& receiver_delay) {
base::TimeDelta rtt = sender_delay - receiver_delay;
+ // TODO(miu): Find out why this must be >= 1 ms, and remove the fudge if it's
+ // bogus.
rtt = std::max(rtt, base::TimeDelta::FromMilliseconds(1));
rtt_ = rtt;
min_rtt_ = std::min(min_rtt_, rtt);
max_rtt_ = std::max(max_rtt_, rtt);
+ // TODO(miu): Replace "average for all time" with an EWMA, or suitable
+ // "average over recent past" mechanism.
if (number_of_rtt_in_avg_ != 0) {
- float ac = static_cast<float>(number_of_rtt_in_avg_);
- avg_rtt_ms_= ((ac / (ac + 1.0)) * avg_rtt_ms_) +
- ((1.0 / (ac + 1.0)) * rtt.InMilliseconds());
+ const double ac = static_cast<double>(number_of_rtt_in_avg_);
+ avg_rtt_ms_ = ((ac / (ac + 1.0)) * avg_rtt_ms_) +
+ ((1.0 / (ac + 1.0)) * rtt.InMillisecondsF());
} else {
- avg_rtt_ms_ = rtt.InMilliseconds();
+ avg_rtt_ms_ = rtt.InMillisecondsF();
}
number_of_rtt_in_avg_++;
}
-bool Rtcp::Rtt(base::TimeDelta* rtt,
- base::TimeDelta* avg_rtt,
- base::TimeDelta* min_rtt,
- base::TimeDelta* max_rtt) const {
+bool Rtcp::Rtt(base::TimeDelta* rtt, base::TimeDelta* avg_rtt,
+ base::TimeDelta* min_rtt, base::TimeDelta* max_rtt) const {
DCHECK(rtt) << "Invalid argument";
DCHECK(avg_rtt) << "Invalid argument";
DCHECK(min_rtt) << "Invalid argument";
DCHECK(max_rtt) << "Invalid argument";
if (number_of_rtt_in_avg_ == 0) return false;
- cast_environment_->Logging()->InsertGenericEvent(kRttMs,
- rtt->InMilliseconds());
*rtt = rtt_;
- *avg_rtt = base::TimeDelta::FromMilliseconds(avg_rtt_ms_);
+ *avg_rtt = base::TimeDelta::FromMillisecondsD(avg_rtt_ms_);
*min_rtt = min_rtt_;
*max_rtt = max_rtt_;
return true;
}
-int Rtcp::CheckForWrapAround(uint32 new_timestamp,
- uint32 old_timestamp) const {
- if (new_timestamp < old_timestamp) {
- // This difference should be less than -2^31 if we have had a wrap around
- // (e.g. |new_timestamp| = 1, |rtcp_rtp_timestamp| = 2^32 - 1). Since it is
- // cast to a int32_t, it should be positive.
- if (static_cast<int32>(new_timestamp - old_timestamp) > 0) {
- return 1; // Forward wrap around.
- }
- } else if (static_cast<int32>(old_timestamp - new_timestamp) > 0) {
- // This difference should be less than -2^31 if we have had a backward wrap
- // around. Since it is cast to a int32, it should be positive.
- return -1;
- }
- return 0;
-}
-
void Rtcp::UpdateNextTimeToSendRtcp() {
int random = base::RandInt(0, 999);
- base::TimeDelta time_to_next = (rtcp_interval_ / 2) +
- (rtcp_interval_ * random / 1000);
+ base::TimeDelta time_to_next =
+ (rtcp_interval_ / 2) + (rtcp_interval_ * random / 1000);
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
next_time_to_send_rtcp_ = now + time_to_next;
}
+void Rtcp::OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log) {
+ // Add received log messages into our log system.
+ RtcpReceiverLogMessage::const_iterator it = receiver_log.begin();
+ for (; it != receiver_log.end(); ++it) {
+ uint32 rtp_timestamp = it->rtp_timestamp_;
+
+ RtcpReceiverEventLogMessages::const_iterator event_it =
+ it->event_log_messages_.begin();
+ for (; event_it != it->event_log_messages_.end(); ++event_it) {
+ switch (event_it->type) {
+ case PACKET_RECEIVED:
+ cast_environment_->Logging()->InsertPacketEvent(
+ event_it->event_timestamp, event_it->type,
+ 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, event_media_type_,
+ rtp_timestamp, kFrameIdUnknown);
+ break;
+ case FRAME_PLAYOUT:
+ cast_environment_->Logging()->InsertFrameEventWithDelay(
+ event_it->event_timestamp, event_it->type, event_media_type_,
+ rtp_timestamp, kFrameIdUnknown, event_it->delay_delta);
+ break;
+ default:
+ VLOG(2) << "Received log message via RTCP that we did not expect: "
+ << static_cast<int>(event_it->type);
+ break;
+ }
+ }
+ }
+}
+
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/rtcp.gyp b/chromium/media/cast/rtcp/rtcp.gyp
deleted file mode 100644
index 14119988c8e..00000000000
--- a/chromium/media/cast/rtcp/rtcp.gyp
+++ /dev/null
@@ -1,46 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_rtcp',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'sources': [
- 'rtcp_defines.h',
- 'rtcp.h',
- 'rtcp.cc',
- 'rtcp_receiver.cc',
- 'rtcp_receiver.h',
- 'rtcp_sender.cc',
- 'rtcp_sender.h',
- 'rtcp_utility.cc',
- 'rtcp_utility.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- '<(DEPTH)/net/net.gyp:net',
- ],
- },
- {
- 'target_name': 'cast_rtcp_test',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'sources': [
- 'test_rtcp_packet_builder.cc',
- 'test_rtcp_packet_builder.h',
- ], # source
- 'dependencies': [
- 'cast_rtcp',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- ],
- },
- ],
-}
-
diff --git a/chromium/media/cast/rtcp/rtcp.h b/chromium/media/cast/rtcp/rtcp.h
index aa083a5a4dd..9d0184f9033 100644
--- a/chromium/media/cast/rtcp/rtcp.h
+++ b/chromium/media/cast/rtcp/rtcp.h
@@ -5,20 +5,23 @@
#ifndef MEDIA_CAST_RTCP_RTCP_H_
#define MEDIA_CAST_RTCP_RTCP_H_
-#include <list>
#include <map>
#include <queue>
-#include <set>
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.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_defines.h"
#include "media/cast/cast_environment.h"
+#include "media/cast/rtcp/receiver_rtcp_event_subscriber.h"
#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/cast_transport_sender.h"
+#include "media/cast/transport/pacing/paced_sender.h"
namespace media {
namespace cast {
@@ -40,14 +43,6 @@ class RtcpSenderFeedback {
virtual ~RtcpSenderFeedback() {}
};
-class RtpSenderStatistics {
- public:
- virtual void GetStatistics(const base::TimeTicks& now,
- RtcpSenderInfo* sender_info) = 0;
-
- virtual ~RtpSenderStatistics() {}
-};
-
class RtpReceiverStatistics {
public:
virtual void GetStatistics(uint8* fraction_lost,
@@ -60,16 +55,20 @@ class RtpReceiverStatistics {
class Rtcp {
public:
+ // Rtcp accepts two transports, one to be used by Cast senders
+ // (CastTransportSender) only, and the other (PacedPacketSender) should only
+ // be used by the Cast receivers and test applications.
Rtcp(scoped_refptr<CastEnvironment> cast_environment,
RtcpSenderFeedback* sender_feedback,
- PacedPacketSender* paced_packet_sender,
- RtpSenderStatistics* rtp_sender_statistics,
+ transport::CastTransportSender* const transport_sender, // Send-side.
+ transport::PacedPacketSender* paced_packet_sender, // Receive side.
RtpReceiverStatistics* rtp_receiver_statistics,
RtcpMode rtcp_mode,
const base::TimeDelta& rtcp_interval,
uint32 local_ssrc,
uint32 remote_ssrc,
- const std::string& c_name);
+ const std::string& c_name,
+ EventMediaType event_media_type);
virtual ~Rtcp();
@@ -78,35 +77,55 @@ class Rtcp {
static uint32 GetSsrcOfSender(const uint8* rtcp_buffer, size_t length);
base::TimeTicks TimeToSendNextRtcpReport();
- // |sender_log_message| is optional; without it no log messages will be
- // attached to the RTCP report; instead a normal RTCP send report will be
- // sent.
- // Additionally if all messages in |sender_log_message| does
- // not fit in the packet the |sender_log_message| will contain the remaining
- // unsent messages.
- void SendRtcpFromRtpSender(RtcpSenderLogMessage* sender_log_message);
-
- // |cast_message| and |receiver_log| is optional; if |cast_message| is
+
+ // Send a RTCP sender report.
+ // |current_time| is the current time reported by a tick clock.
+ // |current_time_as_rtp_timestamp| is the corresponding RTP timestamp.
+ void SendRtcpFromRtpSender(base::TimeTicks current_time,
+ uint32 current_time_as_rtp_timestamp);
+
+ // |cast_message| and |rtcp_events| is optional; if |cast_message| is
// provided the RTCP receiver report will append a Cast message containing
- // Acks and Nacks; if |receiver_log| is provided the RTCP receiver report will
- // append the log messages. If no argument is set a normal RTCP receiver
- // report will be sent. Additionally if all messages in |receiver_log| does
- // not fit in the packet the |receiver_log| will contain the remaining unsent
- // messages.
- void SendRtcpFromRtpReceiver(const RtcpCastMessage* cast_message,
- RtcpReceiverLogMessage* receiver_log);
+ // Acks and Nacks; if |rtcp_events| is provided the RTCP receiver report
+ // will append the log messages.
+ void SendRtcpFromRtpReceiver(
+ const RtcpCastMessage* cast_message,
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events);
void IncomingRtcpPacket(const uint8* rtcp_buffer, size_t length);
- bool Rtt(base::TimeDelta* rtt, base::TimeDelta* avg_rtt,
- base::TimeDelta* min_rtt, base::TimeDelta* max_rtt) const;
- bool RtpTimestampInSenderTime(int frequency,
- uint32 rtp_timestamp,
- base::TimeTicks* rtp_timestamp_in_ticks) const;
- protected:
- int CheckForWrapAround(uint32 new_timestamp,
- uint32 old_timestamp) const;
+ // TODO(miu): Clean up this method and downstream code: Only VideoSender uses
+ // this (for congestion control), and only the |rtt| and |avg_rtt| values, and
+ // it's not clear that any of the downstream code is doing the right thing
+ // with this data.
+ bool Rtt(base::TimeDelta* rtt,
+ base::TimeDelta* avg_rtt,
+ base::TimeDelta* min_rtt,
+ base::TimeDelta* max_rtt) const;
+
+ bool is_rtt_available() const { return number_of_rtt_in_avg_ > 0; }
+
+ // If available, returns true and sets the output arguments to the latest
+ // lip-sync timestamps gleaned from the sender reports. While the sender
+ // provides reference NTP times relative to its own wall clock, the
+ // |reference_time| returned here has been translated to the local
+ // CastEnvironment clock.
+ bool GetLatestLipSyncTimes(uint32* rtp_timestamp,
+ base::TimeTicks* reference_time) const;
+
+ // Set the history size to record Cast receiver events. The event history is
+ // used to remove duplicates. The history will store at most |size| events.
+ void SetCastReceiverEventHistorySize(size_t size);
+ // Update the target delay. Will be added to every report sent back to the
+ // sender.
+ // TODO(miu): Remove this deprecated functionality. The sender ignores this.
+ void SetTargetDelay(base::TimeDelta target_delay);
+
+ void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log);
+
+ protected:
+ void OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction);
void OnReceivedLipSyncInfo(uint32 rtp_timestamp,
uint32 ntp_seconds,
uint32 ntp_fraction);
@@ -115,13 +134,6 @@ class Rtcp {
friend class LocalRtcpRttFeedback;
friend class LocalRtcpReceiverFeedback;
- void SendRtcp(const base::TimeTicks& now,
- uint32 packet_type_flags,
- uint32 media_ssrc,
- const RtcpCastMessage* cast_message);
-
- void OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction);
-
void OnReceivedDelaySinceLastReport(uint32 receivers_ssrc,
uint32 last_report,
uint32 delay_since_last_report);
@@ -133,17 +145,20 @@ class Rtcp {
void UpdateNextTimeToSendRtcp();
- void SaveLastSentNtpTime(const base::TimeTicks& now, uint32 last_ntp_seconds,
+ void SaveLastSentNtpTime(const base::TimeTicks& now,
+ uint32 last_ntp_seconds,
uint32 last_ntp_fraction);
scoped_refptr<CastEnvironment> cast_environment_;
+ transport::CastTransportSender* const transport_sender_;
const base::TimeDelta rtcp_interval_;
const RtcpMode rtcp_mode_;
const uint32 local_ssrc_;
const uint32 remote_ssrc_;
+ const std::string c_name_;
+ const EventMediaType event_media_type_;
// Not owned by this class.
- RtpSenderStatistics* const rtp_sender_statistics_;
RtpReceiverStatistics* const rtp_receiver_statistics_;
scoped_ptr<LocalRtcpRttFeedback> rtt_feedback_;
@@ -154,18 +169,33 @@ class Rtcp {
base::TimeTicks next_time_to_send_rtcp_;
RtcpSendTimeMap last_reports_sent_map_;
RtcpSendTimeQueue last_reports_sent_queue_;
+
+ // The truncated (i.e., 64-->32-bit) NTP timestamp provided in the last report
+ // from the remote peer, along with the local time at which the report was
+ // received. These values are used for ping-pong'ing NTP timestamps between
+ // the peers so that they can estimate the network's round-trip time.
+ uint32 last_report_truncated_ntp_;
base::TimeTicks time_last_report_received_;
- uint32 last_report_received_;
- uint32 last_received_rtp_timestamp_;
- uint32 last_received_ntp_seconds_;
- uint32 last_received_ntp_fraction_;
+ // Maintains a smoothed offset between the local clock and the remote clock.
+ // Calling this member's Current() method is only valid if
+ // |time_last_report_received_| is not "null."
+ ClockDriftSmoother local_clock_ahead_by_;
+
+ // Latest "lip sync" info from the sender. The sender provides the RTP
+ // timestamp of some frame of its choosing and also a corresponding reference
+ // NTP timestamp sampled from a clock common to all media streams. It is
+ // expected that the sender will update this data regularly and in a timely
+ // manner (e.g., about once per second).
+ uint32 lip_sync_rtp_timestamp_;
+ uint64 lip_sync_ntp_timestamp_;
base::TimeDelta rtt_;
base::TimeDelta min_rtt_;
base::TimeDelta max_rtt_;
int number_of_rtt_in_avg_;
- float avg_rtt_ms_;
+ double avg_rtt_ms_;
+ uint16 target_delay_ms_;
DISALLOW_COPY_AND_ASSIGN(Rtcp);
};
diff --git a/chromium/media/cast/rtcp/rtcp_defines.cc b/chromium/media/cast/rtcp/rtcp_defines.cc
new file mode 100644
index 00000000000..214100d4d9e
--- /dev/null
+++ b/chromium/media/cast/rtcp/rtcp_defines.cc
@@ -0,0 +1,49 @@
+// 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/rtcp/rtcp_defines.h"
+
+#include "media/cast/logging/logging_defines.h"
+
+namespace media {
+namespace cast {
+
+RtcpCastMessage::RtcpCastMessage(uint32 media_ssrc)
+ : media_ssrc_(media_ssrc), ack_frame_id_(0u), target_delay_ms_(0) {}
+RtcpCastMessage::~RtcpCastMessage() {}
+
+void RtcpCastMessage::Copy(const RtcpCastMessage& cast_message) {
+ media_ssrc_ = cast_message.media_ssrc_;
+ ack_frame_id_ = cast_message.ack_frame_id_;
+ target_delay_ms_ = cast_message.target_delay_ms_;
+ missing_frames_and_packets_ = cast_message.missing_frames_and_packets_;
+}
+
+RtcpReceiverEventLogMessage::RtcpReceiverEventLogMessage()
+ : type(UNKNOWN), packet_id(0u) {}
+RtcpReceiverEventLogMessage::~RtcpReceiverEventLogMessage() {}
+
+RtcpReceiverFrameLogMessage::RtcpReceiverFrameLogMessage(uint32 timestamp)
+ : rtp_timestamp_(timestamp) {}
+RtcpReceiverFrameLogMessage::~RtcpReceiverFrameLogMessage() {}
+
+RtcpRpsiMessage::RtcpRpsiMessage()
+ : remote_ssrc(0u), payload_type(0u), picture_id(0u) {}
+RtcpRpsiMessage::~RtcpRpsiMessage() {}
+
+RtcpNackMessage::RtcpNackMessage() : remote_ssrc(0u) {}
+RtcpNackMessage::~RtcpNackMessage() {}
+
+RtcpRembMessage::RtcpRembMessage() : remb_bitrate(0u) {}
+RtcpRembMessage::~RtcpRembMessage() {}
+
+RtcpReceiverReferenceTimeReport::RtcpReceiverReferenceTimeReport()
+ : remote_ssrc(0u), ntp_seconds(0u), ntp_fraction(0u) {}
+RtcpReceiverReferenceTimeReport::~RtcpReceiverReferenceTimeReport() {}
+
+RtcpEvent::RtcpEvent() : type(UNKNOWN), packet_id(0u) {}
+RtcpEvent::~RtcpEvent() {}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/rtcp/rtcp_defines.h b/chromium/media/cast/rtcp/rtcp_defines.h
index 0277bd1feaf..31795648c64 100644
--- a/chromium/media/cast/rtcp/rtcp_defines.h
+++ b/chromium/media/cast/rtcp/rtcp_defines.h
@@ -5,45 +5,42 @@
#ifndef MEDIA_CAST_RTCP_RTCP_DEFINES_H_
#define MEDIA_CAST_RTCP_RTCP_DEFINES_H_
-#include <list>
#include <map>
#include <set>
#include "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
#include "media/cast/logging/logging_defines.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace media {
namespace cast {
+static const size_t kRtcpCastLogHeaderSize = 12;
+static const size_t kRtcpReceiverFrameLogSize = 8;
+static const size_t kRtcpReceiverEventLogSize = 4;
+
// Handle the per frame ACK and NACK messages.
class RtcpCastMessage {
public:
explicit RtcpCastMessage(uint32 media_ssrc);
~RtcpCastMessage();
+ void Copy(const RtcpCastMessage& cast_message);
+
uint32 media_ssrc_;
uint32 ack_frame_id_;
+ uint16 target_delay_ms_;
MissingFramesAndPacketsMap missing_frames_and_packets_;
-};
-
-// Log messages form sender to receiver.
-enum RtcpSenderFrameStatus {
- kRtcpSenderFrameStatusUnknown = 0,
- kRtcpSenderFrameStatusDroppedByEncoder = 1,
- kRtcpSenderFrameStatusDroppedByFlowControl = 2,
- kRtcpSenderFrameStatusSentToNetwork = 3,
-};
-struct RtcpSenderFrameLogMessage {
- RtcpSenderFrameStatus frame_status;
- uint32 rtp_timestamp;
+ DISALLOW_COPY_AND_ASSIGN(RtcpCastMessage);
};
-typedef std::list<RtcpSenderFrameLogMessage> RtcpSenderLogMessage;
-
// Log messages from receiver to sender.
struct RtcpReceiverEventLogMessage {
+ RtcpReceiverEventLogMessage();
+ ~RtcpReceiverEventLogMessage();
+
CastLoggingEvent type;
base::TimeTicks event_timestamp;
base::TimeDelta delay_delta;
@@ -52,99 +49,84 @@ struct RtcpReceiverEventLogMessage {
typedef std::list<RtcpReceiverEventLogMessage> RtcpReceiverEventLogMessages;
-class RtcpReceiverFrameLogMessage {
- public:
+struct RtcpReceiverFrameLogMessage {
explicit RtcpReceiverFrameLogMessage(uint32 rtp_timestamp);
~RtcpReceiverFrameLogMessage();
uint32 rtp_timestamp_;
RtcpReceiverEventLogMessages event_log_messages_;
-};
-typedef std::list<RtcpReceiverFrameLogMessage> RtcpReceiverLogMessage;
-
-struct RtcpSenderInfo {
- // First three members are used for lipsync.
- // First two members are used for rtt.
- uint32 ntp_seconds;
- uint32 ntp_fraction;
- uint32 rtp_timestamp;
- uint32 send_packet_count;
- size_t send_octet_count;
+ // TODO(mikhal): Investigate what's the best way to allow adding
+ // DISALLOW_COPY_AND_ASSIGN, as currently it contradicts the implementation
+ // and possible changes have a big impact on design.
};
-struct RtcpReportBlock {
- uint32 remote_ssrc; // SSRC of sender of this report.
- uint32 media_ssrc; // SSRC of the RTP packet sender.
- uint8 fraction_lost;
- uint32 cumulative_lost; // 24 bits valid.
- uint32 extended_high_sequence_number;
- uint32 jitter;
- uint32 last_sr;
- uint32 delay_since_last_sr;
-};
+typedef std::list<RtcpReceiverFrameLogMessage> RtcpReceiverLogMessage;
struct RtcpRpsiMessage {
+ RtcpRpsiMessage();
+ ~RtcpRpsiMessage();
+
uint32 remote_ssrc;
uint8 payload_type;
uint64 picture_id;
};
-class RtcpNackMessage {
- public:
+struct RtcpNackMessage {
RtcpNackMessage();
~RtcpNackMessage();
uint32 remote_ssrc;
std::list<uint16> nack_list;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpNackMessage);
};
-class RtcpRembMessage {
- public:
+struct RtcpRembMessage {
RtcpRembMessage();
~RtcpRembMessage();
uint32 remb_bitrate;
std::list<uint32> remb_ssrcs;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpRembMessage);
};
struct RtcpReceiverReferenceTimeReport {
+ RtcpReceiverReferenceTimeReport();
+ ~RtcpReceiverReferenceTimeReport();
+
uint32 remote_ssrc;
uint32 ntp_seconds;
uint32 ntp_fraction;
};
-struct RtcpDlrrReportBlock {
- uint32 last_rr;
- uint32 delay_since_last_rr;
-};
-
-inline bool operator==(RtcpReportBlock lhs, RtcpReportBlock rhs) {
- return lhs.remote_ssrc == rhs.remote_ssrc &&
- lhs.media_ssrc == rhs.media_ssrc &&
- lhs.fraction_lost == rhs.fraction_lost &&
- lhs.cumulative_lost == rhs.cumulative_lost &&
- lhs.extended_high_sequence_number == rhs.extended_high_sequence_number &&
- lhs.jitter == rhs.jitter &&
- lhs.last_sr == rhs.last_sr &&
- lhs.delay_since_last_sr == rhs.delay_since_last_sr;
-}
-
-inline bool operator==(RtcpSenderInfo lhs, RtcpSenderInfo rhs) {
- return lhs.ntp_seconds == rhs.ntp_seconds &&
- lhs.ntp_fraction == rhs.ntp_fraction &&
- lhs.rtp_timestamp == rhs.rtp_timestamp &&
- lhs.send_packet_count == rhs.send_packet_count &&
- lhs.send_octet_count == rhs.send_octet_count;
-}
-
inline bool operator==(RtcpReceiverReferenceTimeReport lhs,
RtcpReceiverReferenceTimeReport rhs) {
return lhs.remote_ssrc == rhs.remote_ssrc &&
- lhs.ntp_seconds == rhs.ntp_seconds &&
- lhs.ntp_fraction == rhs.ntp_fraction;
+ lhs.ntp_seconds == rhs.ntp_seconds &&
+ lhs.ntp_fraction == rhs.ntp_fraction;
}
+// Struct used by raw event subscribers as an intermediate format before
+// sending off to the other side via RTCP.
+// (i.e., {Sender,Receiver}RtcpEventSubscriber)
+struct RtcpEvent {
+ RtcpEvent();
+ ~RtcpEvent();
+
+ CastLoggingEvent type;
+
+ // Time of event logged.
+ base::TimeTicks timestamp;
+
+ // Render/playout delay. Only set for FRAME_PLAYOUT events.
+ base::TimeDelta delay_delta;
+
+ // Only set for packet events.
+ uint16 packet_id;
+};
+
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/rtcp_receiver.cc b/chromium/media/cast/rtcp/rtcp_receiver.cc
index 152ebc00d7b..3be8e921c46 100644
--- a/chromium/media/cast/rtcp/rtcp_receiver.cc
+++ b/chromium/media/cast/rtcp/rtcp_receiver.cc
@@ -6,50 +6,31 @@
#include "base/logging.h"
#include "media/cast/rtcp/rtcp_utility.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace {
-media::cast::CastLoggingEvent TranslateToLogEventFromWireFormat(uint8 event) {
- switch (event) {
- case 1:
- return media::cast::kAckSent;
- case 2:
- return media::cast::kAudioPlayoutDelay;
- case 3:
- return media::cast::kAudioFrameDecoded;
- case 4:
- return media::cast::kVideoFrameDecoded;
- case 5:
- return media::cast::kVideoRenderDelay;
- case 6:
- return media::cast::kPacketReceived;
- default:
- // If the sender adds new log messages we will end up here until we add
- // the new messages in the receiver.
- VLOG(1) << "Unexpected log message received: " << static_cast<int>(event);
- NOTREACHED();
- return media::cast::kUnknown;
- }
-}
-
-media::cast::RtcpSenderFrameStatus TranslateToFrameStatusFromWireFormat(
- uint8 status) {
- switch (status) {
- case 0:
- return media::cast::kRtcpSenderFrameStatusUnknown;
- case 1:
- return media::cast::kRtcpSenderFrameStatusDroppedByEncoder;
- case 2:
- return media::cast::kRtcpSenderFrameStatusDroppedByFlowControl;
- case 3:
- return media::cast::kRtcpSenderFrameStatusSentToNetwork;
- default:
- // If the sender adds new log messages we will end up here until we add
- // the new messages in the receiver.
- NOTREACHED();
- VLOG(1) << "Unexpected status received: " << static_cast<int>(status);
- return media::cast::kRtcpSenderFrameStatusUnknown;
- }
+// A receiver frame event is identified by frame RTP timestamp, event timestamp
+// and event type.
+// A receiver packet event is identified by all of the above plus packet id.
+// The key format is as follows:
+// First uint64:
+// bits 0-11: zeroes (unused).
+// bits 12-15: event type ID.
+// bits 16-31: packet ID if packet event, 0 otherwise.
+// bits 32-63: RTP timestamp.
+// Second uint64:
+// bits 0-63: event TimeTicks internal value.
+std::pair<uint64, uint64> GetReceiverEventKey(
+ uint32 frame_rtp_timestamp, const base::TimeTicks& event_timestamp,
+ uint8 event_type, uint16 packet_id_or_zero) {
+ uint64 value1 = event_type;
+ value1 <<= 16;
+ value1 |= packet_id_or_zero;
+ value1 <<= 32;
+ value1 |= frame_rtp_timestamp;
+ return std::make_pair(
+ value1, static_cast<uint64>(event_timestamp.ToInternalValue()));
}
} // namespace
@@ -67,12 +48,15 @@ RtcpReceiver::RtcpReceiver(scoped_refptr<CastEnvironment> cast_environment,
sender_feedback_(sender_feedback),
receiver_feedback_(receiver_feedback),
rtt_feedback_(rtt_feedback),
- cast_environment_(cast_environment) {}
+ cast_environment_(cast_environment),
+ receiver_event_history_size_(0) {}
RtcpReceiver::~RtcpReceiver() {}
-void RtcpReceiver::SetRemoteSSRC(uint32 ssrc) {
- remote_ssrc_ = ssrc;
+void RtcpReceiver::SetRemoteSSRC(uint32 ssrc) { remote_ssrc_ = ssrc; }
+
+void RtcpReceiver::SetCastReceiverEventHistorySize(size_t size) {
+ receiver_event_history_size_ = size;
}
void RtcpReceiver::IncomingRtcpPacket(RtcpParser* rtcp_parser) {
@@ -117,9 +101,6 @@ void RtcpReceiver::IncomingRtcpPacket(RtcpParser* rtcp_parser) {
case kRtcpApplicationSpecificCastReceiverLogCode:
HandleApplicationSpecificCastReceiverLog(rtcp_parser);
break;
- case kRtcpApplicationSpecificCastSenderLogCode:
- HandleApplicationSpecificCastSenderLog(rtcp_parser);
- break;
case kRtcpPayloadSpecificRembCode:
case kRtcpPayloadSpecificRembItemCode:
case kRtcpPayloadSpecificCastCode:
@@ -151,16 +132,15 @@ void RtcpReceiver::HandleSenderReport(RtcpParser* rtcp_parser) {
// Synchronization source identifier for the originator of this SR packet.
uint32 remote_ssrc = rtcp_field.sender_report.sender_ssrc;
- VLOG(1) << "Cast RTCP received SR from SSRC " << remote_ssrc;
+ VLOG(2) << "Cast RTCP received SR from SSRC " << remote_ssrc;
if (remote_ssrc_ == remote_ssrc) {
- RtcpSenderInfo remote_sender_info;
+ transport::RtcpSenderInfo remote_sender_info;
remote_sender_info.ntp_seconds =
rtcp_field.sender_report.ntp_most_significant;
remote_sender_info.ntp_fraction =
rtcp_field.sender_report.ntp_least_significant;
- remote_sender_info.rtp_timestamp =
- rtcp_field.sender_report.rtp_timestamp;
+ remote_sender_info.rtp_timestamp = rtcp_field.sender_report.rtp_timestamp;
remote_sender_info.send_packet_count =
rtcp_field.sender_report.sender_packet_count;
remote_sender_info.send_octet_count =
@@ -184,7 +164,7 @@ void RtcpReceiver::HandleReceiverReport(RtcpParser* rtcp_parser) {
uint32 remote_ssrc = rtcp_field.receiver_report.sender_ssrc;
- VLOG(1) << "Cast RTCP received RR from SSRC " << remote_ssrc;
+ VLOG(2) << "Cast RTCP received RR from SSRC " << remote_ssrc;
rtcp_field_type = rtcp_parser->Iterate();
while (rtcp_field_type == kRtcpReportBlockItemCode) {
@@ -211,13 +191,9 @@ void RtcpReceiver::HandleReportBlock(const RtcpField* rtcp_field,
// This block is not for us ignore it.
return;
}
- VLOG(1) << "Cast RTCP received RB from SSRC " << remote_ssrc;
- cast_environment_->Logging()->InsertGenericEvent(kPacketLoss,
- rb.fraction_lost);
- cast_environment_->Logging()->InsertGenericEvent(kJitterMs,
- rb.jitter);
+ VLOG(2) << "Cast RTCP received RB from SSRC " << remote_ssrc;
- RtcpReportBlock report_block;
+ transport::RtcpReportBlock report_block;
report_block.remote_ssrc = remote_ssrc;
report_block.media_ssrc = rb.ssrc;
report_block.fraction_lost = rb.fraction_lost;
@@ -229,9 +205,8 @@ void RtcpReceiver::HandleReportBlock(const RtcpField* rtcp_field,
report_block.delay_since_last_sr = rb.delay_last_sender_report;
if (rtt_feedback_) {
- rtt_feedback_->OnReceivedDelaySinceLastReport(rb.ssrc,
- rb.last_sender_report,
- rb.delay_last_sender_report);
+ rtt_feedback_->OnReceivedDelaySinceLastReport(
+ rb.ssrc, rb.last_sender_report, rb.delay_last_sender_report);
}
}
@@ -245,7 +220,7 @@ void RtcpReceiver::HandleSDES(RtcpParser* rtcp_parser) {
void RtcpReceiver::HandleSDESChunk(RtcpParser* rtcp_parser) {
const RtcpField& rtcp_field = rtcp_parser->Field();
- VLOG(1) << "Cast RTCP received SDES with cname " << rtcp_field.c_name.name;
+ VLOG(2) << "Cast RTCP received SDES with cname " << rtcp_field.c_name.name;
}
void RtcpReceiver::HandleXr(RtcpParser* rtcp_parser) {
@@ -337,7 +312,7 @@ void RtcpReceiver::HandleBYE(RtcpParser* rtcp_parser) {
const RtcpField& rtcp_field = rtcp_parser->Field();
uint32 remote_ssrc = rtcp_field.bye.sender_ssrc;
if (remote_ssrc_ == remote_ssrc) {
- VLOG(1) << "Cast RTCP received BYE from SSRC " << remote_ssrc;
+ VLOG(2) << "Cast RTCP received BYE from SSRC " << remote_ssrc;
}
rtcp_parser->Iterate();
}
@@ -346,7 +321,7 @@ void RtcpReceiver::HandlePLI(RtcpParser* rtcp_parser) {
const RtcpField& rtcp_field = rtcp_parser->Field();
if (ssrc_ == rtcp_field.pli.media_ssrc) {
// Received a signal that we need to send a new key frame.
- VLOG(1) << "Cast RTCP received PLI on our SSRC " << ssrc_;
+ VLOG(2) << "Cast RTCP received PLI on our SSRC " << ssrc_;
}
rtcp_parser->Iterate();
}
@@ -377,7 +352,7 @@ void RtcpReceiver::HandleRpsi(RtcpParser* rtcp_parser) {
}
rpsi_picture_id += (rtcp_field.rpsi.native_bit_string[bytes - 1] & 0x7f);
- VLOG(1) << "Cast RTCP received RPSI with picture_id " << rpsi_picture_id;
+ VLOG(2) << "Cast RTCP received RPSI with picture_id " << rpsi_picture_id;
}
void RtcpReceiver::HandlePayloadSpecificApp(RtcpParser* rtcp_parser) {
@@ -421,7 +396,7 @@ void RtcpReceiver::HandlePayloadSpecificRembItem(RtcpParser* rtcp_parser) {
for (int i = 0; i < rtcp_field.remb_item.number_of_ssrcs; ++i) {
if (rtcp_field.remb_item.ssrcs[i] == ssrc_) {
// Found matching ssrc.
- VLOG(1) << "Cast RTCP received REMB with received_bitrate "
+ VLOG(2) << "Cast RTCP received REMB with received_bitrate "
<< rtcp_field.remb_item.bitrate;
return;
}
@@ -450,11 +425,15 @@ void RtcpReceiver::HandleApplicationSpecificCastReceiverLog(
field_type = rtcp_parser->Iterate();
while (field_type == kRtcpApplicationSpecificCastReceiverLogEventCode) {
- HandleApplicationSpecificCastReceiverEventLog(rtcp_parser,
+ HandleApplicationSpecificCastReceiverEventLog(
+ rtcp_field.cast_receiver_log.rtp_timestamp,
+ rtcp_parser,
&frame_log.event_log_messages_);
field_type = rtcp_parser->Iterate();
}
- receiver_log.push_back(frame_log);
+
+ if (!frame_log.event_log_messages_.empty())
+ receiver_log.push_back(frame_log);
}
if (receiver_feedback_ && !receiver_log.empty()) {
@@ -463,52 +442,50 @@ void RtcpReceiver::HandleApplicationSpecificCastReceiverLog(
}
void RtcpReceiver::HandleApplicationSpecificCastReceiverEventLog(
+ uint32 frame_rtp_timestamp,
RtcpParser* rtcp_parser,
RtcpReceiverEventLogMessages* event_log_messages) {
const RtcpField& rtcp_field = rtcp_parser->Field();
- RtcpReceiverEventLogMessage event_log;
- event_log.type = TranslateToLogEventFromWireFormat(
- rtcp_field.cast_receiver_log.event);
- event_log.event_timestamp = base::TimeTicks() +
+ const uint8 event = rtcp_field.cast_receiver_log.event;
+ const CastLoggingEvent event_type = TranslateToLogEventFromWireFormat(event);
+ uint16 packet_id = event_type == PACKET_RECEIVED ?
+ rtcp_field.cast_receiver_log.delay_delta_or_packet_id.packet_id : 0;
+ const base::TimeTicks event_timestamp =
+ base::TimeTicks() +
base::TimeDelta::FromMilliseconds(
rtcp_field.cast_receiver_log.event_timestamp_base +
rtcp_field.cast_receiver_log.event_timestamp_delta);
- event_log.delay_delta = base::TimeDelta::FromMilliseconds(
- rtcp_field.cast_receiver_log.delay_delta_or_packet_id);
- event_log.packet_id =
- rtcp_field.cast_receiver_log.delay_delta_or_packet_id;
- event_log_messages->push_back(event_log);
-}
-
-void RtcpReceiver::HandleApplicationSpecificCastSenderLog(
- RtcpParser* rtcp_parser) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
- uint32 remote_ssrc = rtcp_field.cast_sender_log.sender_ssrc;
- if (remote_ssrc_ != remote_ssrc) {
- RtcpFieldTypes field_type;
- // Message not to us. Iterate until we have passed this message.
- do {
- field_type = rtcp_parser->Iterate();
- } while (field_type == kRtcpApplicationSpecificCastSenderLogCode);
+ // The following code checks to see if we have already seen this event.
+ // The algorithm works by maintaining a sliding window of events. We have
+ // a queue and a set of events. We enqueue every new event and insert it
+ // into the set. When the queue becomes too big we remove the oldest event
+ // from both the queue and the set.
+ ReceiverEventKey key =
+ GetReceiverEventKey(
+ frame_rtp_timestamp, event_timestamp, event, packet_id);
+ if (receiver_event_key_set_.find(key) != receiver_event_key_set_.end()) {
return;
+ } else {
+ receiver_event_key_set_.insert(key);
+ receiver_event_key_queue_.push(key);
+
+ if (receiver_event_key_queue_.size() > receiver_event_history_size_) {
+ const ReceiverEventKey oldest_key = receiver_event_key_queue_.front();
+ receiver_event_key_queue_.pop();
+ receiver_event_key_set_.erase(oldest_key);
+ }
}
- RtcpSenderLogMessage sender_log;
- RtcpFieldTypes field_type = rtcp_parser->Iterate();
- while (field_type == kRtcpApplicationSpecificCastSenderLogCode) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
- RtcpSenderFrameLogMessage frame_log;
- frame_log.frame_status =
- TranslateToFrameStatusFromWireFormat(rtcp_field.cast_sender_log.status);
- frame_log.rtp_timestamp = rtcp_field.cast_sender_log.rtp_timestamp;
- sender_log.push_back(frame_log);
- field_type = rtcp_parser->Iterate();
- }
- if (receiver_feedback_) {
- receiver_feedback_->OnReceivedSenderLog(sender_log);
- }
+ RtcpReceiverEventLogMessage event_log;
+ event_log.type = event_type;
+ event_log.event_timestamp = event_timestamp;
+ event_log.delay_delta = base::TimeDelta::FromMilliseconds(
+ rtcp_field.cast_receiver_log.delay_delta_or_packet_id.delay_delta);
+ event_log.packet_id =
+ rtcp_field.cast_receiver_log.delay_delta_or_packet_id.packet_id;
+ event_log_messages->push_back(event_log);
}
void RtcpReceiver::HandlePayloadSpecificCastItem(RtcpParser* rtcp_parser) {
@@ -516,6 +493,7 @@ void RtcpReceiver::HandlePayloadSpecificCastItem(RtcpParser* rtcp_parser) {
RtcpCastMessage cast_message(remote_ssrc_);
cast_message.ack_frame_id_ = ack_frame_id_wrap_helper_.MapTo32bitsFrameId(
rtcp_field.cast_item.last_frame_id);
+ cast_message.target_delay_ms_ = rtcp_field.cast_item.target_delay_ms;
RtcpFieldTypes packet_type = rtcp_parser->Iterate();
while (packet_type == kRtcpPayloadSpecificCastNackItemCode) {
@@ -545,15 +523,15 @@ void RtcpReceiver::HandlePayloadSpecificCastNackItem(
frame_it = ret.first;
DCHECK(frame_it != missing_frames_and_packets->end()) << "Invalid state";
}
- if (rtcp_field->cast_nack_item.packet_id == kRtcpCastAllPacketsLost) {
+ uint16 packet_id = rtcp_field->cast_nack_item.packet_id;
+ frame_it->second.insert(packet_id);
+
+ if (packet_id == kRtcpCastAllPacketsLost) {
// Special case all packets in a frame is missing.
return;
}
- uint16 packet_id = rtcp_field->cast_nack_item.packet_id;
uint8 bitmask = rtcp_field->cast_nack_item.bitmask;
- frame_it->second.insert(packet_id);
-
if (bitmask) {
for (int i = 1; i <= 8; ++i) {
if (bitmask & 1) {
@@ -576,9 +554,10 @@ void RtcpReceiver::HandleFIR(RtcpParser* rtcp_parser) {
void RtcpReceiver::HandleFIRItem(const RtcpField* rtcp_field) {
// Is it our sender that is requested to generate a new keyframe.
- if (ssrc_ != rtcp_field->fir_item.ssrc) return;
+ if (ssrc_ != rtcp_field->fir_item.ssrc)
+ return;
- VLOG(1) << "Cast RTCP received FIR on our SSRC " << ssrc_;
+ VLOG(2) << "Cast RTCP received FIR on our SSRC " << ssrc_;
}
} // namespace cast
diff --git a/chromium/media/cast/rtcp/rtcp_receiver.h b/chromium/media/cast/rtcp/rtcp_receiver.h
index 81383c4ec10..d3cef9e57b4 100644
--- a/chromium/media/cast/rtcp/rtcp_receiver.h
+++ b/chromium/media/cast/rtcp/rtcp_receiver.h
@@ -5,10 +5,13 @@
#ifndef MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
#define MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
-#include "media/cast/net/cast_net_defines.h"
+#include <queue>
+
+#include "base/containers/hash_tables.h"
#include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtcp/rtcp_defines.h"
#include "media/cast/rtcp/rtcp_utility.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace media {
namespace cast {
@@ -16,7 +19,7 @@ namespace cast {
class RtcpReceiverFeedback {
public:
virtual void OnReceivedSenderReport(
- const RtcpSenderInfo& remote_sender_info) = 0;
+ const transport::RtcpSenderInfo& remote_sender_info) = 0;
virtual void OnReceiverReferenceTimeReport(
const RtcpReceiverReferenceTimeReport& remote_time_report) = 0;
@@ -26,9 +29,6 @@ class RtcpReceiverFeedback {
virtual void OnReceivedReceiverLog(
const RtcpReceiverLogMessage& receiver_log) = 0;
- virtual void OnReceivedSenderLog(
- const RtcpSenderLogMessage& sender_log) = 0;
-
virtual ~RtcpReceiverFeedback() {}
};
@@ -53,6 +53,10 @@ class RtcpReceiver {
void SetRemoteSSRC(uint32 ssrc);
+ // Set the history size to record Cast receiver events. Event history is
+ // used to remove duplicates. The history has no more than |size| events.
+ void SetCastReceiverEventHistorySize(size_t size);
+
void IncomingRtcpPacket(RtcpParser* rtcp_parser);
private:
@@ -60,8 +64,7 @@ class RtcpReceiver {
void HandleReceiverReport(RtcpParser* rtcp_parser);
- void HandleReportBlock(const RtcpField* rtcp_field,
- uint32 remote_ssrc);
+ void HandleReportBlock(const RtcpField* rtcp_field, uint32 remote_ssrc);
void HandleSDES(RtcpParser* rtcp_parser);
void HandleSDESChunk(RtcpParser* rtcp_parser);
@@ -100,6 +103,7 @@ class RtcpReceiver {
void HandleApplicationSpecificCastReceiverLog(RtcpParser* rtcp_parser);
void HandleApplicationSpecificCastSenderLog(RtcpParser* rtcp_parser);
void HandleApplicationSpecificCastReceiverEventLog(
+ uint32 frame_rtp_timestamp,
RtcpParser* rtcp_parser,
RtcpReceiverEventLogMessages* event_log_messages);
@@ -112,7 +116,13 @@ class RtcpReceiver {
RtcpRttFeedback* const rtt_feedback_;
scoped_refptr<CastEnvironment> cast_environment_;
- FrameIdWrapHelper ack_frame_id_wrap_helper_;
+ transport::FrameIdWrapHelper ack_frame_id_wrap_helper_;
+
+ // Maintains a history of receiver events.
+ size_t receiver_event_history_size_;
+ typedef std::pair<uint64, uint64> ReceiverEventKey;
+ base::hash_set<ReceiverEventKey> receiver_event_key_set_;
+ std::queue<ReceiverEventKey> receiver_event_key_queue_;
DISALLOW_COPY_AND_ASSIGN(RtcpReceiver);
};
diff --git a/chromium/media/cast/rtcp/rtcp_receiver_unittest.cc b/chromium/media/cast/rtcp/rtcp_receiver_unittest.cc
index b5c5d2d3889..51026d1554b 100644
--- a/chromium/media/cast/rtcp/rtcp_receiver_unittest.cc
+++ b/chromium/media/cast/rtcp/rtcp_receiver_unittest.cc
@@ -10,7 +10,8 @@
#include "media/cast/rtcp/rtcp_receiver.h"
#include "media/cast/rtcp/rtcp_utility.h"
#include "media/cast/rtcp/test_rtcp_packet_builder.h"
-#include "media/cast/test/fake_task_runner.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/cast_transport_defines.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
@@ -21,6 +22,7 @@ using testing::_;
static const uint32 kSenderSsrc = 0x10203;
static const uint32 kSourceSsrc = 0x40506;
static const uint32 kUnknownSsrc = 0xDEAD;
+static const uint16 kTargetDelayMs = 100;
static const std::string kCName("test@10.1.1.1");
namespace {
@@ -28,8 +30,8 @@ class SenderFeedbackCastVerification : public RtcpSenderFeedback {
public:
SenderFeedbackCastVerification() : called_(false) {}
- virtual void OnReceivedCastFeedback(
- const RtcpCastMessage& cast_feedback) OVERRIDE {
+ virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback)
+ OVERRIDE {
EXPECT_EQ(cast_feedback.media_ssrc_, kSenderSsrc);
EXPECT_EQ(cast_feedback.ack_frame_id_, kAckFrameId);
@@ -38,7 +40,8 @@ class SenderFeedbackCastVerification : public RtcpSenderFeedback {
EXPECT_TRUE(frame_it != cast_feedback.missing_frames_and_packets_.end());
EXPECT_EQ(kLostFrameId, frame_it->first);
- EXPECT_TRUE(frame_it->second.empty());
+ EXPECT_EQ(frame_it->second.size(), 1UL);
+ EXPECT_EQ(*frame_it->second.begin(), kRtcpCastAllPacketsLost);
++frame_it;
EXPECT_TRUE(frame_it != cast_feedback.missing_frames_and_packets_.end());
EXPECT_EQ(kFrameIdWithLostPackets, frame_it->first);
@@ -58,6 +61,8 @@ class SenderFeedbackCastVerification : public RtcpSenderFeedback {
private:
bool called_;
+
+ DISALLOW_COPY_AND_ASSIGN(SenderFeedbackCastVerification);
};
class RtcpReceiverCastLogVerification : public RtcpReceiverFeedback {
@@ -67,18 +72,18 @@ class RtcpReceiverCastLogVerification : public RtcpReceiverFeedback {
called_on_received_receiver_log_(false) {}
virtual void OnReceivedSenderReport(
- const RtcpSenderInfo& remote_sender_info) OVERRIDE {};
+ const transport::RtcpSenderInfo& remote_sender_info) OVERRIDE{};
virtual void OnReceiverReferenceTimeReport(
- const RtcpReceiverReferenceTimeReport& remote_time_report) OVERRIDE {};
+ const RtcpReceiverReferenceTimeReport& remote_time_report) OVERRIDE{};
- virtual void OnReceivedSendReportRequest() OVERRIDE {};
+ virtual void OnReceivedSendReportRequest() OVERRIDE{};
- virtual void OnReceivedReceiverLog(
- const RtcpReceiverLogMessage& receiver_log) OVERRIDE {
+ virtual void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log)
+ OVERRIDE {
EXPECT_EQ(expected_receiver_log_.size(), receiver_log.size());
RtcpReceiverLogMessage::const_iterator expected_it =
- expected_receiver_log_.begin();
+ expected_receiver_log_.begin();
RtcpReceiverLogMessage::const_iterator incoming_it = receiver_log.begin();
for (; incoming_it != receiver_log.end(); ++incoming_it) {
EXPECT_EQ(expected_it->rtp_timestamp_, incoming_it->rtp_timestamp_);
@@ -94,7 +99,7 @@ class RtcpReceiverCastLogVerification : public RtcpReceiverFeedback {
EXPECT_EQ(event_expected_it->type, event_incoming_it->type);
EXPECT_EQ(event_expected_it->event_timestamp,
event_incoming_it->event_timestamp);
- if (event_expected_it->type == kPacketReceived) {
+ if (event_expected_it->type == PACKET_RECEIVED) {
EXPECT_EQ(event_expected_it->packet_id, event_incoming_it->packet_id);
} else {
EXPECT_EQ(event_expected_it->delay_delta,
@@ -107,26 +112,6 @@ class RtcpReceiverCastLogVerification : public RtcpReceiverFeedback {
called_on_received_receiver_log_ = true;
}
- virtual void OnReceivedSenderLog(
- const RtcpSenderLogMessage& sender_log) OVERRIDE {
- EXPECT_EQ(expected_sender_log_.size(), sender_log.size());
-
- RtcpSenderLogMessage::const_iterator expected_it =
- expected_sender_log_.begin();
- RtcpSenderLogMessage::const_iterator incoming_it = sender_log.begin();
- for (; expected_it != expected_sender_log_.end();
- ++expected_it, ++incoming_it) {
- EXPECT_EQ(expected_it->frame_status, incoming_it->frame_status);
- EXPECT_EQ(0xffffff & expected_it->rtp_timestamp,
- incoming_it->rtp_timestamp);
- }
- called_on_received_sender_log_ = true;
- }
-
- bool OnReceivedSenderLogCalled() {
- return called_on_received_sender_log_;
- }
-
bool OnReceivedReceiverLogCalled() {
return called_on_received_receiver_log_ && expected_receiver_log_.empty();
}
@@ -135,15 +120,12 @@ class RtcpReceiverCastLogVerification : public RtcpReceiverFeedback {
expected_receiver_log_ = receiver_log;
}
- void SetExpectedSenderLog(const RtcpSenderLogMessage& sender_log) {
- expected_sender_log_ = sender_log;
- }
-
private:
RtcpReceiverLogMessage expected_receiver_log_;
- RtcpSenderLogMessage expected_sender_log_;
bool called_on_received_sender_log_;
bool called_on_received_receiver_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpReceiverCastLogVerification);
};
} // namespace
@@ -151,28 +133,26 @@ class RtcpReceiverCastLogVerification : public RtcpReceiverFeedback {
class RtcpReceiverTest : public ::testing::Test {
protected:
RtcpReceiverTest()
- : task_runner_(new test::FakeTaskRunner(&testing_clock_)),
- cast_environment_(new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig())),
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)),
rtcp_receiver_(new RtcpReceiver(cast_environment_,
&mock_sender_feedback_,
&mock_receiver_feedback_,
&mock_rtt_feedback_,
kSourceSsrc)) {
- }
-
- virtual ~RtcpReceiverTest() {}
-
- virtual void SetUp() OVERRIDE {
EXPECT_CALL(mock_receiver_feedback_, OnReceivedSenderReport(_)).Times(0);
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceiverReferenceTimeReport(_)).Times(0);
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedSendReportRequest()).Times(0);
+ EXPECT_CALL(mock_receiver_feedback_, OnReceiverReferenceTimeReport(_))
+ .Times(0);
+ EXPECT_CALL(mock_receiver_feedback_, OnReceivedSendReportRequest())
+ .Times(0);
EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(0);
- EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(_, _, _)).Times(0);
+ EXPECT_CALL(mock_rtt_feedback_, OnReceivedDelaySinceLastReport(_, _, _))
+ .Times(0);
expected_sender_info_.ntp_seconds = kNtpHigh;
expected_sender_info_.ntp_fraction = kNtpLow;
@@ -193,22 +173,26 @@ class RtcpReceiverTest : public ::testing::Test {
expected_receiver_reference_report_.ntp_fraction = kNtpLow;
}
+ virtual ~RtcpReceiverTest() {}
+
// Injects an RTCP packet into the receiver.
void InjectRtcpPacket(const uint8* packet, uint16 length) {
RtcpParser rtcp_parser(packet, length);
rtcp_receiver_->IncomingRtcpPacket(&rtcp_parser);
}
- base::SimpleTestTickClock testing_clock_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_refptr<CastEnvironment> cast_environment_;
MockRtcpReceiverFeedback mock_receiver_feedback_;
MockRtcpRttFeedback mock_rtt_feedback_;
MockRtcpSenderFeedback mock_sender_feedback_;
scoped_ptr<RtcpReceiver> rtcp_receiver_;
- RtcpSenderInfo expected_sender_info_;
- RtcpReportBlock expected_report_block_;
+ transport::RtcpSenderInfo expected_sender_info_;
+ transport::RtcpReportBlock expected_report_block_;
RtcpReceiverReferenceTimeReport expected_receiver_reference_report_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpReceiverTest);
};
TEST_F(RtcpReceiverTest, BrokenPacketIsIgnored) {
@@ -222,14 +206,14 @@ TEST_F(RtcpReceiverTest, InjectSenderReportPacket) {
// Expected to be ignored since the sender ssrc does not match our
// remote ssrc.
- InjectRtcpPacket(p.Packet(), p.Length());
+ InjectRtcpPacket(p.Data(), p.Length());
EXPECT_CALL(mock_receiver_feedback_,
OnReceivedSenderReport(expected_sender_info_)).Times(1);
rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
// Expected to be pass through since the sender ssrc match our remote ssrc.
- InjectRtcpPacket(p.Packet(), p.Length());
+ InjectRtcpPacket(p.Data(), p.Length());
}
TEST_F(RtcpReceiverTest, InjectReceiveReportPacket) {
@@ -239,19 +223,18 @@ TEST_F(RtcpReceiverTest, InjectReceiveReportPacket) {
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
- InjectRtcpPacket(p1.Packet(), p1.Length());
+ InjectRtcpPacket(p1.Data(), p1.Length());
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
TestRtcpPacketBuilder p2;
p2.AddRr(kSenderSsrc, 1);
p2.AddRb(kSourceSsrc);
// Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p2.Packet(), p2.Length());
+ InjectRtcpPacket(p2.Data(), p2.Length());
}
TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
@@ -263,7 +246,7 @@ TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
// our remote ssrc.
// Report block expected to be ignored since the source ssrc does not match
// our local ssrc.
- InjectRtcpPacket(p1.Packet(), p1.Length());
+ InjectRtcpPacket(p1.Data(), p1.Length());
EXPECT_CALL(mock_receiver_feedback_,
OnReceivedSenderReport(expected_sender_info_)).Times(1);
@@ -273,13 +256,12 @@ TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
// remote ssrc.
// Report block expected to be ignored since the source ssrc does not match
// our local ssrc.
- InjectRtcpPacket(p1.Packet(), p1.Length());
+ InjectRtcpPacket(p1.Data(), p1.Length());
EXPECT_CALL(mock_receiver_feedback_, OnReceivedSenderReport(_)).Times(0);
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
rtcp_receiver_->SetRemoteSSRC(0);
@@ -291,14 +273,13 @@ TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
// our remote ssrc.
// Receiver report expected to be pass through since the sender ssrc match
// our local ssrc.
- InjectRtcpPacket(p2.Packet(), p2.Length());
+ InjectRtcpPacket(p2.Data(), p2.Length());
EXPECT_CALL(mock_receiver_feedback_,
OnReceivedSenderReport(expected_sender_info_)).Times(1);
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
@@ -306,7 +287,7 @@ TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
// remote ssrc.
// Receiver report expected to be pass through since the sender ssrc match
// our local ssrc.
- InjectRtcpPacket(p2.Packet(), p2.Length());
+ InjectRtcpPacket(p2.Data(), p2.Length());
}
TEST_F(RtcpReceiverTest, InjectSenderReportPacketWithDlrr) {
@@ -320,20 +301,19 @@ TEST_F(RtcpReceiverTest, InjectSenderReportPacketWithDlrr) {
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
- InjectRtcpPacket(p.Packet(), p.Length());
+ InjectRtcpPacket(p.Data(), p.Length());
EXPECT_CALL(mock_receiver_feedback_,
OnReceivedSenderReport(expected_sender_info_)).Times(1);
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSenderSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSenderSsrc, kLastSr, kDelayLastSr)).Times(1);
// Enable receiving sender report.
rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
// Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p.Packet(), p.Length());
+ InjectRtcpPacket(p.Data(), p.Length());
}
TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithRrtr) {
@@ -345,14 +325,14 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithRrtr) {
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
- InjectRtcpPacket(p1.Packet(), p1.Length());
+ InjectRtcpPacket(p1.Data(), p1.Length());
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
- EXPECT_CALL(mock_receiver_feedback_, OnReceiverReferenceTimeReport(
- expected_receiver_reference_report_)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
+ EXPECT_CALL(mock_receiver_feedback_,
+ OnReceiverReferenceTimeReport(
+ expected_receiver_reference_report_)).Times(1);
// Enable receiving reference time report.
rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
@@ -364,7 +344,7 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithRrtr) {
p2.AddXrRrtrBlock();
// Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p2.Packet(), p2.Length());
+ InjectRtcpPacket(p2.Data(), p2.Length());
}
TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithIntraFrameRequest) {
@@ -375,12 +355,11 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithIntraFrameRequest) {
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
- InjectRtcpPacket(p1.Packet(), p1.Length());
+ InjectRtcpPacket(p1.Data(), p1.Length());
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
TestRtcpPacketBuilder p2;
p2.AddRr(kSenderSsrc, 1);
@@ -388,23 +367,22 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithIntraFrameRequest) {
p2.AddPli(kSenderSsrc, kSourceSsrc);
// Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p2.Packet(), p2.Length());
+ InjectRtcpPacket(p2.Data(), p2.Length());
}
TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastFeedback) {
TestRtcpPacketBuilder p1;
p1.AddRr(kSenderSsrc, 1);
p1.AddRb(kUnknownSsrc);
- p1.AddCast(kSenderSsrc, kUnknownSsrc);
+ p1.AddCast(kSenderSsrc, kUnknownSsrc, kTargetDelayMs);
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
- InjectRtcpPacket(p1.Packet(), p1.Length());
+ InjectRtcpPacket(p1.Data(), p1.Length());
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(1);
// Enable receiving the cast feedback.
@@ -413,10 +391,10 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastFeedback) {
TestRtcpPacketBuilder p2;
p2.AddRr(kSenderSsrc, 1);
p2.AddRb(kSourceSsrc);
- p2.AddCast(kSenderSsrc, kSourceSsrc);
+ p2.AddCast(kSenderSsrc, kSourceSsrc, kTargetDelayMs);
// Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p2.Packet(), p2.Length());
+ InjectRtcpPacket(p2.Data(), p2.Length());
}
TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastVerification) {
@@ -428,9 +406,8 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastVerification) {
kSourceSsrc);
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc,
- kLastSr,
- kDelayLastSr)).Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
// Enable receiving the cast feedback.
rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
@@ -438,48 +415,15 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastVerification) {
TestRtcpPacketBuilder p;
p.AddRr(kSenderSsrc, 1);
p.AddRb(kSourceSsrc);
- p.AddCast(kSenderSsrc, kSourceSsrc);
+ p.AddCast(kSenderSsrc, kSourceSsrc, kTargetDelayMs);
// Expected to be pass through since the sender ssrc match our local ssrc.
- RtcpParser rtcp_parser(p.Packet(), p.Length());
+ RtcpParser rtcp_parser(p.Data(), p.Length());
rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
EXPECT_TRUE(sender_feedback_cast_verification.called());
}
-TEST_F(RtcpReceiverTest, InjectSenderReportWithCastSenderLogVerification) {
- RtcpReceiverCastLogVerification cast_log_verification;
- RtcpReceiver rtcp_receiver(cast_environment_,
- &mock_sender_feedback_,
- &cast_log_verification,
- &mock_rtt_feedback_,
- kSourceSsrc);
- rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
-
- RtcpSenderLogMessage sender_log;
- for (int j = 0; j < 359; ++j) {
- RtcpSenderFrameLogMessage sender_frame_log;
- sender_frame_log.frame_status = kRtcpSenderFrameStatusSentToNetwork;
- sender_frame_log.rtp_timestamp = kRtpTimestamp + j * 90;
- sender_log.push_back(sender_frame_log);
- }
- cast_log_verification.SetExpectedSenderLog(sender_log);
-
- TestRtcpPacketBuilder p;
- p.AddSr(kSenderSsrc, 0);
- p.AddSdesCname(kSenderSsrc, kCName);
- p.AddSenderLog(kSenderSsrc);
-
- for (int i = 0; i < 359; ++i) {
- p.AddSenderFrameLog(kRtcpSenderFrameStatusSentToNetwork,
- kRtpTimestamp + i * 90);
- }
- RtcpParser rtcp_parser(p.Packet(), p.Length());
- rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
-
- EXPECT_TRUE(cast_log_verification.OnReceivedSenderLogCalled());
-}
-
TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationBase) {
static const uint32 kTimeBaseMs = 12345678;
static const uint32 kTimeDelayMs = 10;
@@ -494,21 +438,28 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationBase) {
&mock_rtt_feedback_,
kSourceSsrc);
rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
+ rtcp_receiver.SetCastReceiverEventHistorySize(100);
RtcpReceiverLogMessage receiver_log;
RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
RtcpReceiverEventLogMessage event_log;
- event_log.type = kAckSent;
+ event_log.type = FRAME_ACK_SENT;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.delay_delta = base::TimeDelta::FromMilliseconds(kDelayDeltaMs);
frame_log.event_log_messages_.push_back(event_log);
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs));
- event_log.type = kPacketReceived;
+ event_log.type = PACKET_RECEIVED;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.packet_id = kLostPacketId1;
frame_log.event_log_messages_.push_back(event_log);
+
+ event_log.type = PACKET_RECEIVED;
+ event_log.event_timestamp = testing_clock.NowTicks();
+ event_log.packet_id = kLostPacketId2;
+ frame_log.event_log_messages_.push_back(event_log);
+
receiver_log.push_back(frame_log);
cast_log_verification.SetExpectedReceiverLog(receiver_log);
@@ -517,15 +468,22 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationBase) {
p.AddRr(kSenderSsrc, 1);
p.AddRb(kSourceSsrc);
p.AddReceiverLog(kSenderSsrc);
- p.AddReceiverFrameLog(kRtpTimestamp, 2, kTimeBaseMs);
- p.AddReceiverEventLog(kDelayDeltaMs, 1, 0);
- p.AddReceiverEventLog(kLostPacketId1, 6, kTimeDelayMs);
+ p.AddReceiverFrameLog(kRtpTimestamp, 3, kTimeBaseMs);
+ p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
+ p.AddReceiverEventLog(kLostPacketId1, PACKET_RECEIVED, kTimeDelayMs);
+ p.AddReceiverEventLog(kLostPacketId2, PACKET_RECEIVED, kTimeDelayMs);
+
+ // Adds duplicated receiver event.
+ p.AddReceiverFrameLog(kRtpTimestamp, 3, kTimeBaseMs);
+ p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
+ p.AddReceiverEventLog(kLostPacketId1, PACKET_RECEIVED, kTimeDelayMs);
+ p.AddReceiverEventLog(kLostPacketId2, PACKET_RECEIVED, kTimeDelayMs);
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc, kLastSr, kDelayLastSr)).
- Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
- RtcpParser rtcp_parser(p.Packet(), p.Length());
+ RtcpParser rtcp_parser(p.Data(), p.Length());
rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
EXPECT_TRUE(cast_log_verification.OnReceivedReceiverLogCalled());
@@ -551,7 +509,7 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationMulti) {
for (int j = 0; j < 100; ++j) {
RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
RtcpReceiverEventLogMessage event_log;
- event_log.type = kAckSent;
+ event_log.type = FRAME_ACK_SENT;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.delay_delta = base::TimeDelta::FromMilliseconds(kDelayDeltaMs);
frame_log.event_log_messages_.push_back(event_log);
@@ -566,21 +524,19 @@ TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationMulti) {
p.AddRb(kSourceSsrc);
p.AddReceiverLog(kSenderSsrc);
for (int i = 0; i < 100; ++i) {
- p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs);
- p.AddReceiverEventLog(kDelayDeltaMs, 1, 0);
+ p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs);
+ p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
}
EXPECT_CALL(mock_rtt_feedback_,
- OnReceivedDelaySinceLastReport(kSourceSsrc, kLastSr, kDelayLastSr)).
- Times(1);
+ OnReceivedDelaySinceLastReport(
+ kSourceSsrc, kLastSr, kDelayLastSr)).Times(1);
- RtcpParser rtcp_parser(p.Packet(), p.Length());
+ RtcpParser rtcp_parser(p.Data(), p.Length());
rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
EXPECT_TRUE(cast_log_verification.OnReceivedReceiverLogCalled());
}
-
-
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/rtcp_sender.cc b/chromium/media/cast/rtcp/rtcp_sender.cc
index b5cf4ce4ced..bf7d30c84c8 100644
--- a/chromium/media/cast/rtcp/rtcp_sender.cc
+++ b/chromium/media/cast/rtcp/rtcp_sender.cc
@@ -4,263 +4,226 @@
#include "media/cast/rtcp/rtcp_sender.h"
+#include <stdint.h>
+
#include <algorithm>
#include <vector>
+#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/pacing/paced_sender.h"
+#include "media/cast/rtcp/rtcp_defines.h"
#include "media/cast/rtcp/rtcp_utility.h"
-#include "net/base/big_endian.h"
-
-static const size_t kRtcpCastLogHeaderSize = 12;
-static const size_t kRtcpSenderFrameLogSize = 4;
-static const size_t kRtcpReceiverFrameLogSize = 8;
-static const size_t kRtcpReceiverEventLogSize = 4;
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+namespace media {
+namespace cast {
namespace {
+
+// Max delta is 4095 milliseconds because we need to be able to encode it in
+// 12 bits.
+const int64 kMaxWireFormatTimeDeltaMs = INT64_C(0xfff);
+
uint16 MergeEventTypeAndTimestampForWireFormat(
- const media::cast::CastLoggingEvent& event,
+ const CastLoggingEvent& event,
const base::TimeDelta& time_delta) {
int64 time_delta_ms = time_delta.InMilliseconds();
- // Max delta is 4096 milliseconds.
- DCHECK_GE(GG_INT64_C(0xfff), time_delta_ms);
-
- uint16 event_type_and_timestamp_delta =
- static_cast<uint16>(time_delta_ms & 0xfff);
-
- uint16 event_type = 0;
- switch (event) {
- case media::cast::kAckSent:
- event_type = 1;
- break;
- case media::cast::kAudioPlayoutDelay:
- event_type = 2;
- break;
- case media::cast::kAudioFrameDecoded:
- event_type = 3;
- break;
- case media::cast::kVideoFrameDecoded:
- event_type = 4;
- break;
- case media::cast::kVideoRenderDelay:
- event_type = 5;
- break;
- case media::cast::kPacketReceived:
- event_type = 6;
- break;
- default:
- NOTREACHED();
- }
- DCHECK(!(event_type & 0xfff0));
- return (event_type << 12) + event_type_and_timestamp_delta;
-}
-bool ScanRtcpReceiverLogMessage(
- const media::cast::RtcpReceiverLogMessage& receiver_log_message,
- size_t start_size,
- size_t* number_of_frames,
- size_t* total_number_of_messages_to_send,
- size_t* rtcp_log_size) {
- if (receiver_log_message.empty()) return false;
+ DCHECK_GE(time_delta_ms, 0);
+ DCHECK_LE(time_delta_ms, kMaxWireFormatTimeDeltaMs);
- size_t remaining_space = media::cast::kIpPacketSize - start_size;
+ uint16 time_delta_12_bits =
+ static_cast<uint16>(time_delta_ms & kMaxWireFormatTimeDeltaMs);
- // We must have space for at least one message
- DCHECK_GE(remaining_space, kRtcpCastLogHeaderSize +
- kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize)
- << "Not enough buffer space";
+ uint16 event_type_4_bits = ConvertEventTypeToWireFormat(event);
+ DCHECK(event_type_4_bits);
+ DCHECK(~(event_type_4_bits & 0xfff0));
+ return (event_type_4_bits << 12) | time_delta_12_bits;
+}
- if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
- kRtcpReceiverEventLogSize) {
- return false;
- }
- // Account for the RTCP header for an application-defined packet.
- remaining_space -= kRtcpCastLogHeaderSize;
+bool EventTimestampLessThan(const RtcpReceiverEventLogMessage& lhs,
+ const RtcpReceiverEventLogMessage& rhs) {
+ return lhs.event_timestamp < rhs.event_timestamp;
+}
- media::cast::RtcpReceiverLogMessage::const_iterator frame_it =
- receiver_log_message.begin();
- for (; frame_it != receiver_log_message.end(); ++frame_it) {
- (*number_of_frames)++;
+void AddReceiverLog(
+ const RtcpReceiverLogMessage& redundancy_receiver_log_message,
+ RtcpReceiverLogMessage* receiver_log_message,
+ size_t* remaining_space,
+ size_t* number_of_frames,
+ size_t* total_number_of_messages_to_send) {
+ RtcpReceiverLogMessage::const_iterator it =
+ redundancy_receiver_log_message.begin();
+ while (it != redundancy_receiver_log_message.end() &&
+ *remaining_space >=
+ kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
+ receiver_log_message->push_front(*it);
+ size_t num_event_logs = (*remaining_space - kRtcpReceiverFrameLogSize) /
+ kRtcpReceiverEventLogSize;
+ RtcpReceiverEventLogMessages& event_log_messages =
+ receiver_log_message->front().event_log_messages_;
+ if (num_event_logs < event_log_messages.size())
+ event_log_messages.resize(num_event_logs);
+
+ *remaining_space -= kRtcpReceiverFrameLogSize +
+ event_log_messages.size() * kRtcpReceiverEventLogSize;
+ ++*number_of_frames;
+ *total_number_of_messages_to_send += event_log_messages.size();
+ ++it;
+ }
+}
- remaining_space -= kRtcpReceiverFrameLogSize;
+// A class to build a string representing the NACK list in Cast message.
+//
+// The string will look like "23:3-6 25:1,5-6", meaning packets 3 to 6 in frame
+// 23 are being NACK'ed (i.e. they are missing from the receiver's point of
+// view) and packets 1, 5 and 6 are missing in frame 25. A frame that is
+// completely missing will show as "26:65535".
+class NackStringBuilder {
+ public:
+ NackStringBuilder()
+ : frame_count_(0),
+ packet_count_(0),
+ last_frame_id_(-1),
+ last_packet_id_(-1),
+ contiguous_sequence_(false) {}
+ ~NackStringBuilder() {}
+
+ bool Empty() const { return frame_count_ == 0; }
+
+ void PushFrame(int frame_id) {
+ DCHECK_GE(frame_id, 0);
+ if (frame_count_ > 0) {
+ if (frame_id == last_frame_id_) {
+ return;
+ }
+ if (contiguous_sequence_) {
+ stream_ << "-" << last_packet_id_;
+ }
+ stream_ << ", ";
+ }
+ stream_ << frame_id;
+ last_frame_id_ = frame_id;
+ packet_count_ = 0;
+ contiguous_sequence_ = false;
+ ++frame_count_;
+ }
- size_t messages_in_frame = frame_it->event_log_messages_.size();
- size_t remaining_space_in_messages =
- remaining_space / kRtcpReceiverEventLogSize;
- size_t messages_to_send = std::min(messages_in_frame,
- remaining_space_in_messages);
- if (messages_to_send > media::cast::kRtcpMaxReceiverLogMessages) {
- // We can't send more than 256 messages.
- remaining_space -= media::cast::kRtcpMaxReceiverLogMessages *
- kRtcpReceiverEventLogSize;
- *total_number_of_messages_to_send +=
- media::cast::kRtcpMaxReceiverLogMessages;
- break;
+ void PushPacket(int packet_id) {
+ DCHECK_GE(last_frame_id_, 0);
+ DCHECK_GE(packet_id, 0);
+ if (packet_count_ == 0) {
+ stream_ << ":" << packet_id;
+ } else if (packet_id == last_packet_id_ + 1) {
+ contiguous_sequence_ = true;
+ } else {
+ if (contiguous_sequence_) {
+ stream_ << "-" << last_packet_id_;
+ contiguous_sequence_ = false;
+ }
+ stream_ << "," << packet_id;
}
- remaining_space -= messages_to_send * kRtcpReceiverEventLogSize;
- *total_number_of_messages_to_send += messages_to_send;
+ ++packet_count_;
+ last_packet_id_ = packet_id;
+ }
- if (remaining_space <
- kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
- // Make sure that we have room for at least one more message.
- break;
+ std::string GetString() {
+ if (contiguous_sequence_) {
+ stream_ << "-" << last_packet_id_;
+ contiguous_sequence_ = false;
}
+ return stream_.str();
}
- *rtcp_log_size = kRtcpCastLogHeaderSize +
- *number_of_frames * kRtcpReceiverFrameLogSize +
- *total_number_of_messages_to_send * kRtcpReceiverEventLogSize;
- DCHECK_GE(media::cast::kIpPacketSize,
- start_size + *rtcp_log_size) << "Not enough buffer space";
- VLOG(1) << "number of frames " << *number_of_frames;
- VLOG(1) << "total messages to send " << *total_number_of_messages_to_send;
- VLOG(1) << "rtcp log size " << *rtcp_log_size;
- return true;
-}
+ private:
+ std::ostringstream stream_;
+ int frame_count_;
+ int packet_count_;
+ int last_frame_id_;
+ int last_packet_id_;
+ bool contiguous_sequence_;
+};
} // namespace
-namespace media {
-namespace cast {
-
+// TODO(mikhal): This is only used by the receiver. Consider renaming.
RtcpSender::RtcpSender(scoped_refptr<CastEnvironment> cast_environment,
- PacedPacketSender* outgoing_transport,
+ transport::PacedPacketSender* outgoing_transport,
uint32 sending_ssrc,
const std::string& c_name)
- : ssrc_(sending_ssrc),
- c_name_(c_name),
- transport_(outgoing_transport),
- cast_environment_(cast_environment) {
+ : ssrc_(sending_ssrc),
+ c_name_(c_name),
+ transport_(outgoing_transport),
+ cast_environment_(cast_environment) {
DCHECK_LT(c_name_.length(), kRtcpCnameSize) << "Invalid config";
}
RtcpSender::~RtcpSender() {}
-void RtcpSender::SendRtcpFromRtpSender(uint32 packet_type_flags,
- const RtcpSenderInfo* sender_info,
- const RtcpDlrrReportBlock* dlrr,
- RtcpSenderLogMessage* sender_log) {
- if (packet_type_flags & kRtcpRr ||
- packet_type_flags & kRtcpPli ||
- packet_type_flags & kRtcpRrtr ||
- packet_type_flags & kRtcpCast ||
- packet_type_flags & kRtcpReceiverLog ||
- packet_type_flags & kRtcpRpsi ||
- packet_type_flags & kRtcpRemb ||
- packet_type_flags & kRtcpNack) {
- NOTREACHED() << "Invalid argument";
- }
-
- std::vector<uint8> packet;
- packet.reserve(kIpPacketSize);
- if (packet_type_flags & kRtcpSr) {
- DCHECK(sender_info) << "Invalid argument";
- BuildSR(*sender_info, NULL, &packet);
- BuildSdec(&packet);
- }
- if (packet_type_flags & kRtcpBye) {
- BuildBye(&packet);
- }
- if (packet_type_flags & kRtcpDlrr) {
- DCHECK(dlrr) << "Invalid argument";
- BuildDlrrRb(dlrr, &packet);
- }
- if (packet_type_flags & kRtcpSenderLog) {
- DCHECK(sender_log) << "Invalid argument";
- BuildSenderLog(sender_log, &packet);
- }
- if (packet.empty())
- return; // Sanity don't send empty packets.
-
- transport_->SendRtcpPacket(packet);
-}
-
void RtcpSender::SendRtcpFromRtpReceiver(
uint32 packet_type_flags,
- const RtcpReportBlock* report_block,
+ const transport::RtcpReportBlock* report_block,
const RtcpReceiverReferenceTimeReport* rrtr,
const RtcpCastMessage* cast_message,
- RtcpReceiverLogMessage* receiver_log) {
- if (packet_type_flags & kRtcpSr ||
- packet_type_flags & kRtcpDlrr ||
- packet_type_flags & kRtcpSenderLog) {
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
+ uint16 target_delay_ms) {
+ if (packet_type_flags & transport::kRtcpSr ||
+ packet_type_flags & transport::kRtcpDlrr ||
+ packet_type_flags & transport::kRtcpSenderLog) {
NOTREACHED() << "Invalid argument";
}
- if (packet_type_flags & kRtcpPli ||
- packet_type_flags & kRtcpRpsi ||
- packet_type_flags & kRtcpRemb ||
- packet_type_flags & kRtcpNack) {
+ if (packet_type_flags & transport::kRtcpPli ||
+ packet_type_flags & transport::kRtcpRpsi ||
+ packet_type_flags & transport::kRtcpRemb ||
+ packet_type_flags & transport::kRtcpNack) {
// Implement these for webrtc interop.
NOTIMPLEMENTED();
}
- std::vector<uint8> packet;
- packet.reserve(kIpPacketSize);
+ transport::PacketRef packet(new base::RefCountedData<Packet>);
+ packet->data.reserve(kMaxIpPacketSize);
- if (packet_type_flags & kRtcpRr) {
- BuildRR(report_block, &packet);
+ if (packet_type_flags & transport::kRtcpRr) {
+ BuildRR(report_block, &packet->data);
if (!c_name_.empty()) {
- BuildSdec(&packet);
+ BuildSdec(&packet->data);
}
}
- if (packet_type_flags & kRtcpBye) {
- BuildBye(&packet);
+ if (packet_type_flags & transport::kRtcpBye) {
+ BuildBye(&packet->data);
}
- if (packet_type_flags & kRtcpRrtr) {
+ if (packet_type_flags & transport::kRtcpRrtr) {
DCHECK(rrtr) << "Invalid argument";
- BuildRrtr(rrtr, &packet);
+ BuildRrtr(rrtr, &packet->data);
}
- if (packet_type_flags & kRtcpCast) {
+ if (packet_type_flags & transport::kRtcpCast) {
DCHECK(cast_message) << "Invalid argument";
- BuildCast(cast_message, &packet);
+ BuildCast(cast_message, target_delay_ms, &packet->data);
}
- if (packet_type_flags & kRtcpReceiverLog) {
- DCHECK(receiver_log) << "Invalid argument";
- BuildReceiverLog(receiver_log, &packet);
+ if (packet_type_flags & transport::kRtcpReceiverLog) {
+ DCHECK(rtcp_events) << "Invalid argument";
+ BuildReceiverLog(*rtcp_events, &packet->data);
}
- if (packet.empty()) return; // Sanity don't send empty packets.
- transport_->SendRtcpPacket(packet);
-}
-
-void RtcpSender::BuildSR(const RtcpSenderInfo& sender_info,
- const RtcpReportBlock* report_block,
- std::vector<uint8>* packet) const {
- // Sender report.
- size_t start_size = packet->size();
- DCHECK_LT(start_size + 52, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 52 > kIpPacketSize) return;
-
- uint16 number_of_rows = (report_block) ? 12 : 6;
- packet->resize(start_size + 28);
-
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 28);
- big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
- big_endian_writer.WriteU8(kPacketTypeSenderReport);
- big_endian_writer.WriteU16(number_of_rows);
- big_endian_writer.WriteU32(ssrc_);
- big_endian_writer.WriteU32(sender_info.ntp_seconds);
- big_endian_writer.WriteU32(sender_info.ntp_fraction);
- big_endian_writer.WriteU32(sender_info.rtp_timestamp);
- big_endian_writer.WriteU32(sender_info.send_packet_count);
- big_endian_writer.WriteU32(static_cast<uint32>(sender_info.send_octet_count));
+ if (packet->data.empty())
+ return; // Sanity don't send empty packets.
- if (report_block) {
- AddReportBlocks(*report_block, packet); // Adds 24 bytes.
- }
+ transport_->SendRtcpPacket(ssrc_, packet);
}
-void RtcpSender::BuildRR(const RtcpReportBlock* report_block,
- std::vector<uint8>* packet) const {
+void RtcpSender::BuildRR(const transport::RtcpReportBlock* report_block,
+ Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 32, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 32 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 32, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 32 > kMaxIpPacketSize)
+ return;
uint16 number_of_rows = (report_block) ? 7 : 1;
packet->resize(start_size + 8);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 8);
big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
- big_endian_writer.WriteU8(kPacketTypeReceiverReport);
+ big_endian_writer.WriteU8(transport::kPacketTypeReceiverReport);
big_endian_writer.WriteU16(number_of_rows);
big_endian_writer.WriteU32(ssrc_);
@@ -269,15 +232,17 @@ void RtcpSender::BuildRR(const RtcpReportBlock* report_block,
}
}
-void RtcpSender::AddReportBlocks(const RtcpReportBlock& report_block,
- std::vector<uint8>* packet) const {
+void RtcpSender::AddReportBlocks(const transport::RtcpReportBlock& report_block,
+ Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 24 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 24, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 24 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 24);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 24);
big_endian_writer.WriteU32(report_block.media_ssrc);
big_endian_writer.WriteU8(report_block.fraction_lost);
big_endian_writer.WriteU8(report_block.cumulative_lost >> 16);
@@ -297,30 +262,32 @@ void RtcpSender::AddReportBlocks(const RtcpReportBlock& report_block,
big_endian_writer.WriteU32(report_block.delay_since_last_sr);
}
-void RtcpSender::BuildSdec(std::vector<uint8>* packet) const {
+void RtcpSender::BuildSdec(Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 12 + c_name_.length(), kIpPacketSize)
+ DCHECK_LT(start_size + 12 + c_name_.length(), kMaxIpPacketSize)
<< "Not enough buffer space";
- if (start_size + 12 > kIpPacketSize) return;
+ if (start_size + 12 > kMaxIpPacketSize)
+ return;
// SDES Source Description.
packet->resize(start_size + 10);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 10);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 10);
// We always need to add one SDES CNAME.
big_endian_writer.WriteU8(0x80 + 1);
- big_endian_writer.WriteU8(kPacketTypeSdes);
+ big_endian_writer.WriteU8(transport::kPacketTypeSdes);
// Handle SDES length later on.
uint32 sdes_length_position = static_cast<uint32>(start_size) + 3;
big_endian_writer.WriteU16(0);
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
- big_endian_writer.WriteU8(1); // CNAME = 1
+ big_endian_writer.WriteU8(1); // CNAME = 1
big_endian_writer.WriteU8(static_cast<uint8>(c_name_.length()));
size_t sdes_length = 10 + c_name_.length();
- packet->insert(packet->end(), c_name_.c_str(),
- c_name_.c_str() + c_name_.length());
+ packet->insert(
+ packet->end(), c_name_.c_str(), c_name_.c_str() + c_name_.length());
size_t padding = 0;
@@ -340,20 +307,21 @@ void RtcpSender::BuildSdec(std::vector<uint8>* packet) const {
(*packet)[sdes_length_position] = buffer_length;
}
-void RtcpSender::BuildPli(uint32 remote_ssrc,
- std::vector<uint8>* packet) const {
+void RtcpSender::BuildPli(uint32 remote_ssrc, Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 12, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 12 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 12, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 12 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 12);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 12);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 12);
uint8 FMT = 1; // Picture loss indicator.
big_endian_writer.WriteU8(0x80 + FMT);
- big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
- big_endian_writer.WriteU16(2); // Used fixed length of 2.
- big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
+ big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
+ big_endian_writer.WriteU16(2); // Used fixed length of 2.
+ big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(remote_ssrc); // Add the remote SSRC.
}
@@ -366,18 +334,19 @@ void RtcpSender::BuildPli(uint32 remote_ssrc,
| defined per codec ... | Padding (0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
-void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi,
- std::vector<uint8>* packet) const {
+void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi, Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 24 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 24, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 24 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 24);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 24);
uint8 FMT = 3; // Reference Picture Selection Indication.
big_endian_writer.WriteU8(0x80 + FMT);
- big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
+ big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
// Calculate length.
uint32 bits_required = 7;
@@ -407,8 +376,8 @@ void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi,
// Add picture ID.
for (int i = bytes_required - 1; i > 0; i--) {
- big_endian_writer.WriteU8(
- 0x80 | static_cast<uint8>(rpsi->picture_id >> (i * 7)));
+ big_endian_writer.WriteU8(0x80 |
+ static_cast<uint8>(rpsi->picture_id >> (i * 7)));
}
// Add last byte of picture ID.
big_endian_writer.WriteU8(static_cast<uint8>(rpsi->picture_id & 0x7f));
@@ -419,38 +388,38 @@ void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi,
}
}
-void RtcpSender::BuildRemb(const RtcpRembMessage* remb,
- std::vector<uint8>* packet) const {
+void RtcpSender::BuildRemb(const RtcpRembMessage* remb, Packet* packet) const {
size_t start_size = packet->size();
size_t remb_size = 20 + 4 * remb->remb_ssrcs.size();
- DCHECK_LT(start_size + remb_size, kIpPacketSize)
+ DCHECK_LT(start_size + remb_size, kMaxIpPacketSize)
<< "Not enough buffer space";
- if (start_size + remb_size > kIpPacketSize) return;
+ if (start_size + remb_size > kMaxIpPacketSize)
+ return;
packet->resize(start_size + remb_size);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), remb_size);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), remb_size);
// Add application layer feedback.
uint8 FMT = 15;
big_endian_writer.WriteU8(0x80 + FMT);
- big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
+ big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
big_endian_writer.WriteU8(0);
big_endian_writer.WriteU8(static_cast<uint8>(remb->remb_ssrcs.size() + 4));
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
- big_endian_writer.WriteU32(0); // Remote SSRC must be 0.
+ big_endian_writer.WriteU32(0); // Remote SSRC must be 0.
big_endian_writer.WriteU32(kRemb);
big_endian_writer.WriteU8(static_cast<uint8>(remb->remb_ssrcs.size()));
// 6 bit exponent and a 18 bit mantissa.
uint8 bitrate_exponent;
uint32 bitrate_mantissa;
- BitrateToRembExponentBitrate(remb->remb_bitrate,
- &bitrate_exponent,
- &bitrate_mantissa);
+ BitrateToRembExponentBitrate(
+ remb->remb_bitrate, &bitrate_exponent, &bitrate_mantissa);
- big_endian_writer.WriteU8(static_cast<uint8>((bitrate_exponent << 2) +
- ((bitrate_mantissa >> 16) & 0x03)));
+ big_endian_writer.WriteU8(static_cast<uint8>(
+ (bitrate_exponent << 2) + ((bitrate_mantissa >> 16) & 0x03)));
big_endian_writer.WriteU8(static_cast<uint8>(bitrate_mantissa >> 8));
big_endian_writer.WriteU8(static_cast<uint8>(bitrate_mantissa));
@@ -458,34 +427,33 @@ void RtcpSender::BuildRemb(const RtcpRembMessage* remb,
for (; it != remb->remb_ssrcs.end(); ++it) {
big_endian_writer.WriteU32(*it);
}
- cast_environment_->Logging()->InsertGenericEvent(kRembBitrate,
- remb->remb_bitrate);
}
-void RtcpSender::BuildNack(const RtcpNackMessage* nack,
- std::vector<uint8>* packet) const {
+void RtcpSender::BuildNack(const RtcpNackMessage* nack, Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 16, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 16 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 16, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 16 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 16);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 16);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 16);
uint8 FMT = 1;
big_endian_writer.WriteU8(0x80 + FMT);
- big_endian_writer.WriteU8(kPacketTypeGenericRtpFeedback);
+ big_endian_writer.WriteU8(transport::kPacketTypeGenericRtpFeedback);
big_endian_writer.WriteU8(0);
size_t nack_size_pos = start_size + 3;
big_endian_writer.WriteU8(3);
- big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
+ big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(nack->remote_ssrc); // Add the remote SSRC.
// Build NACK bitmasks and write them to the Rtcp message.
// The nack list should be sorted and not contain duplicates.
size_t number_of_nack_fields = 0;
- size_t max_number_of_nack_fields = std::min<size_t>(kRtcpMaxNackFields,
- (kIpPacketSize - packet->size()) / 4);
+ size_t max_number_of_nack_fields = std::min<size_t>(
+ kRtcpMaxNackFields, (kMaxIpPacketSize - packet->size()) / 4);
std::list<uint16>::const_iterator it = nack->nack_list.begin();
while (it != nack->nack_list.end() &&
@@ -504,11 +472,13 @@ void RtcpSender::BuildNack(const RtcpNackMessage* nack,
}
// Write the sequence number and the bitmask to the packet.
start_size = packet->size();
- DCHECK_LT(start_size + 4, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 4 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 4, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 4 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 4);
- net::BigEndianWriter big_endian_nack_writer(&((*packet)[start_size]), 4);
+ base::BigEndianWriter big_endian_nack_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 4);
big_endian_nack_writer.WriteU16(nack_sequence_number);
big_endian_nack_writer.WriteU16(bitmask);
number_of_nack_fields++;
@@ -517,75 +487,41 @@ void RtcpSender::BuildNack(const RtcpNackMessage* nack,
(*packet)[nack_size_pos] = static_cast<uint8>(2 + number_of_nack_fields);
}
-void RtcpSender::BuildBye(std::vector<uint8>* packet) const {
+void RtcpSender::BuildBye(Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 8, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 8 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 8, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 8 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 8);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 8);
big_endian_writer.WriteU8(0x80 + 1);
- big_endian_writer.WriteU8(kPacketTypeBye);
- big_endian_writer.WriteU16(1); // Length.
- big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
-}
-
-/*
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |V=2|P|reserved | PT=XR=207 | length |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | SSRC |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | BT=5 | reserved | block length |
- +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- | SSRC_1 (SSRC of first receiver) | sub-
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
- | last RR (LRR) | 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | delay since last RR (DLRR) |
- +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-*/
-void RtcpSender::BuildDlrrRb(const RtcpDlrrReportBlock* dlrr,
- std::vector<uint8>* packet) const {
- size_t start_size = packet->size();
- DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 24 > kIpPacketSize) return;
-
- packet->resize(start_size + 24);
-
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
- big_endian_writer.WriteU8(0x80);
- big_endian_writer.WriteU8(kPacketTypeXr);
- big_endian_writer.WriteU16(5); // Length.
+ big_endian_writer.WriteU8(transport::kPacketTypeBye);
+ big_endian_writer.WriteU16(1); // Length.
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
- big_endian_writer.WriteU8(5); // Add block type.
- big_endian_writer.WriteU8(0); // Add reserved.
- big_endian_writer.WriteU16(3); // Block length.
- big_endian_writer.WriteU32(ssrc_); // Add the media (received RTP) SSRC.
- big_endian_writer.WriteU32(dlrr->last_rr);
- big_endian_writer.WriteU32(dlrr->delay_since_last_rr);
}
void RtcpSender::BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
- std::vector<uint8>* packet) const {
+ Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 20, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 20 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 20, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 20 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 20);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 20);
big_endian_writer.WriteU8(0x80);
- big_endian_writer.WriteU8(kPacketTypeXr);
- big_endian_writer.WriteU16(4); // Length.
+ big_endian_writer.WriteU8(transport::kPacketTypeXr);
+ big_endian_writer.WriteU16(4); // Length.
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
- big_endian_writer.WriteU8(4); // Add block type.
- big_endian_writer.WriteU8(0); // Add reserved.
- big_endian_writer.WriteU16(2); // Block length.
+ big_endian_writer.WriteU8(4); // Add block type.
+ big_endian_writer.WriteU8(0); // Add reserved.
+ big_endian_writer.WriteU16(2); // Block length.
// Add the media (received RTP) SSRC.
big_endian_writer.WriteU32(rrtr->ntp_seconds);
@@ -593,47 +529,54 @@ void RtcpSender::BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
}
void RtcpSender::BuildCast(const RtcpCastMessage* cast,
- std::vector<uint8>* packet) const {
+ uint16 target_delay_ms,
+ Packet* packet) const {
size_t start_size = packet->size();
- DCHECK_LT(start_size + 20, kIpPacketSize) << "Not enough buffer space";
- if (start_size + 20 > kIpPacketSize) return;
+ DCHECK_LT(start_size + 20, kMaxIpPacketSize) << "Not enough buffer space";
+ if (start_size + 20 > kMaxIpPacketSize)
+ return;
packet->resize(start_size + 20);
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 20);
uint8 FMT = 15; // Application layer feedback.
big_endian_writer.WriteU8(0x80 + FMT);
- big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
+ big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
big_endian_writer.WriteU8(0);
- size_t cast_size_pos = start_size + 3; // Save length position.
+ size_t cast_size_pos = start_size + 3; // Save length position.
big_endian_writer.WriteU8(4);
- big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
+ big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(cast->media_ssrc_); // Remote SSRC.
big_endian_writer.WriteU32(kCast);
big_endian_writer.WriteU8(static_cast<uint8>(cast->ack_frame_id_));
size_t cast_loss_field_pos = start_size + 17; // Save loss field position.
big_endian_writer.WriteU8(0); // Overwritten with number_of_loss_fields.
- big_endian_writer.WriteU8(0); // Reserved.
- big_endian_writer.WriteU8(0); // Reserved.
+ big_endian_writer.WriteU16(target_delay_ms);
size_t number_of_loss_fields = 0;
- size_t max_number_of_loss_fields = std::min<size_t>(kRtcpMaxCastLossFields,
- (kIpPacketSize - packet->size()) / 4);
+ size_t max_number_of_loss_fields = std::min<size_t>(
+ kRtcpMaxCastLossFields, (kMaxIpPacketSize - packet->size()) / 4);
MissingFramesAndPacketsMap::const_iterator frame_it =
cast->missing_frames_and_packets_.begin();
+ NackStringBuilder nack_string_builder;
for (; frame_it != cast->missing_frames_and_packets_.end() &&
- number_of_loss_fields < max_number_of_loss_fields; ++frame_it) {
+ number_of_loss_fields < max_number_of_loss_fields;
+ ++frame_it) {
+ nack_string_builder.PushFrame(frame_it->first);
// Iterate through all frames with missing packets.
if (frame_it->second.empty()) {
// Special case all packets in a frame is missing.
start_size = packet->size();
packet->resize(start_size + 4);
- net::BigEndianWriter big_endian_nack_writer(&((*packet)[start_size]), 4);
+ base::BigEndianWriter big_endian_nack_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 4);
big_endian_nack_writer.WriteU8(static_cast<uint8>(frame_it->first));
big_endian_nack_writer.WriteU16(kRtcpCastAllPacketsLost);
big_endian_nack_writer.WriteU8(0);
+ nack_string_builder.PushPacket(kRtcpCastAllPacketsLost);
++number_of_loss_fields;
} else {
PacketIdSet::const_iterator packet_it = frame_it->second.begin();
@@ -642,18 +585,20 @@ void RtcpSender::BuildCast(const RtcpCastMessage* cast,
start_size = packet->size();
packet->resize(start_size + 4);
- net::BigEndianWriter big_endian_nack_writer(
- &((*packet)[start_size]), 4);
+ base::BigEndianWriter big_endian_nack_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 4);
// Write frame and packet id to buffer before calculating bitmask.
big_endian_nack_writer.WriteU8(static_cast<uint8>(frame_it->first));
big_endian_nack_writer.WriteU16(packet_id);
+ nack_string_builder.PushPacket(packet_id);
uint8 bitmask = 0;
++packet_it;
while (packet_it != frame_it->second.end()) {
int shift = static_cast<uint8>(*packet_it - packet_id) - 1;
if (shift >= 0 && shift <= 7) {
+ nack_string_builder.PushPacket(*packet_it);
bitmask |= (1 << shift);
++packet_it;
} else {
@@ -665,80 +610,48 @@ void RtcpSender::BuildCast(const RtcpCastMessage* cast,
}
}
}
+ VLOG_IF(1, !nack_string_builder.Empty())
+ << "SSRC: " << cast->media_ssrc_
+ << ", ACK: " << cast->ack_frame_id_
+ << ", NACK: " << nack_string_builder.GetString();
DCHECK_LE(number_of_loss_fields, kRtcpMaxCastLossFields);
(*packet)[cast_size_pos] = static_cast<uint8>(4 + number_of_loss_fields);
(*packet)[cast_loss_field_pos] = static_cast<uint8>(number_of_loss_fields);
}
-void RtcpSender::BuildSenderLog(RtcpSenderLogMessage* sender_log_message,
- std::vector<uint8>* packet) const {
- DCHECK(sender_log_message);
- DCHECK(packet);
- size_t start_size = packet->size();
- size_t remaining_space = kIpPacketSize - start_size;
- DCHECK_GE(remaining_space, kRtcpCastLogHeaderSize + kRtcpSenderFrameLogSize)
- << "Not enough buffer space";
- if (remaining_space < kRtcpCastLogHeaderSize + kRtcpSenderFrameLogSize)
- return;
-
- size_t space_for_x_messages =
- (remaining_space - kRtcpCastLogHeaderSize) / kRtcpSenderFrameLogSize;
- size_t number_of_messages = std::min(space_for_x_messages,
- sender_log_message->size());
-
- size_t log_size = kRtcpCastLogHeaderSize +
- number_of_messages * kRtcpSenderFrameLogSize;
- packet->resize(start_size + log_size);
-
- net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), log_size);
- big_endian_writer.WriteU8(0x80 + kSenderLogSubtype);
- big_endian_writer.WriteU8(kPacketTypeApplicationDefined);
- big_endian_writer.WriteU16(static_cast<uint16>(2 + number_of_messages));
- big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
- big_endian_writer.WriteU32(kCast);
-
- for (; number_of_messages > 0; --number_of_messages) {
- DCHECK(!sender_log_message->empty());
- const RtcpSenderFrameLogMessage& message = sender_log_message->front();
- big_endian_writer.WriteU8(static_cast<uint8>(message.frame_status));
- // We send the 24 east significant bits of the RTP timestamp.
- big_endian_writer.WriteU8(static_cast<uint8>(message.rtp_timestamp >> 16));
- big_endian_writer.WriteU8(static_cast<uint8>(message.rtp_timestamp >> 8));
- big_endian_writer.WriteU8(static_cast<uint8>(message.rtp_timestamp));
- sender_log_message->pop_front();
- }
-}
-
-void RtcpSender::BuildReceiverLog(RtcpReceiverLogMessage* receiver_log_message,
- std::vector<uint8>* packet) const {
- DCHECK(receiver_log_message);
+void RtcpSender::BuildReceiverLog(
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
+ Packet* packet) {
const size_t packet_start_size = packet->size();
size_t number_of_frames = 0;
size_t total_number_of_messages_to_send = 0;
size_t rtcp_log_size = 0;
-
- if (!ScanRtcpReceiverLogMessage(*receiver_log_message,
- packet_start_size,
- &number_of_frames,
- &total_number_of_messages_to_send,
- &rtcp_log_size)) {
+ RtcpReceiverLogMessage receiver_log_message;
+
+ if (!BuildRtcpReceiverLogMessage(rtcp_events,
+ packet_start_size,
+ &receiver_log_message,
+ &number_of_frames,
+ &total_number_of_messages_to_send,
+ &rtcp_log_size)) {
return;
}
packet->resize(packet_start_size + rtcp_log_size);
- net::BigEndianWriter big_endian_writer(&((*packet)[packet_start_size]),
- rtcp_log_size);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[packet_start_size])), rtcp_log_size);
big_endian_writer.WriteU8(0x80 + kReceiverLogSubtype);
- big_endian_writer.WriteU8(kPacketTypeApplicationDefined);
- big_endian_writer.WriteU16(static_cast<uint16>(2 + 2 * number_of_frames +
- total_number_of_messages_to_send));
+ big_endian_writer.WriteU8(transport::kPacketTypeApplicationDefined);
+ big_endian_writer.WriteU16(static_cast<uint16>(
+ 2 + 2 * number_of_frames + total_number_of_messages_to_send));
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(kCast);
- while (!receiver_log_message->empty() &&
+ while (!receiver_log_message.empty() &&
total_number_of_messages_to_send > 0) {
- RtcpReceiverFrameLogMessage& frame_log_messages =
- receiver_log_message->front();
+ RtcpReceiverFrameLogMessage& frame_log_messages(
+ receiver_log_message.front());
+
// Add our frame header.
big_endian_writer.WriteU32(frame_log_messages.rtp_timestamp_);
size_t messages_in_frame = frame_log_messages.event_log_messages_.size();
@@ -765,19 +678,18 @@ void RtcpSender::BuildReceiverLog(RtcpReceiverLogMessage* receiver_log_message,
const RtcpReceiverEventLogMessage& event_message =
frame_log_messages.event_log_messages_.front();
uint16 event_type_and_timestamp_delta =
- MergeEventTypeAndTimestampForWireFormat(event_message.type,
- event_message.event_timestamp - event_timestamp_base);
+ MergeEventTypeAndTimestampForWireFormat(
+ event_message.type,
+ event_message.event_timestamp - event_timestamp_base);
switch (event_message.type) {
- case kAckSent:
- case kAudioPlayoutDelay:
- case kAudioFrameDecoded:
- case kVideoFrameDecoded:
- case kVideoRenderDelay:
- big_endian_writer.WriteU16(static_cast<uint16>(
- event_message.delay_delta.InMilliseconds()));
+ case FRAME_ACK_SENT:
+ case FRAME_PLAYOUT:
+ case FRAME_DECODED:
+ big_endian_writer.WriteU16(
+ static_cast<uint16>(event_message.delay_delta.InMilliseconds()));
big_endian_writer.WriteU16(event_type_and_timestamp_delta);
break;
- case kPacketReceived:
+ case PACKET_RECEIVED:
big_endian_writer.WriteU16(event_message.packet_id);
big_endian_writer.WriteU16(event_type_and_timestamp_delta);
break;
@@ -789,10 +701,124 @@ void RtcpSender::BuildReceiverLog(RtcpReceiverLogMessage* receiver_log_message,
}
if (frame_log_messages.event_log_messages_.empty()) {
// We sent all messages on this frame; pop the frame header.
- receiver_log_message->pop_front();
+ receiver_log_message.pop_front();
}
}
- DCHECK_EQ(total_number_of_messages_to_send, 0);
+ DCHECK_EQ(total_number_of_messages_to_send, 0u);
+}
+
+bool RtcpSender::BuildRtcpReceiverLogMessage(
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
+ size_t start_size,
+ RtcpReceiverLogMessage* receiver_log_message,
+ size_t* number_of_frames,
+ size_t* total_number_of_messages_to_send,
+ size_t* rtcp_log_size) {
+ size_t remaining_space =
+ std::min(kMaxReceiverLogBytes, kMaxIpPacketSize - start_size);
+ if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
+ kRtcpReceiverEventLogSize) {
+ return false;
+ }
+
+ // We use this to do event timestamp sorting and truncating for events of
+ // a single frame.
+ std::vector<RtcpReceiverEventLogMessage> sorted_log_messages;
+
+ // Account for the RTCP header for an application-defined packet.
+ remaining_space -= kRtcpCastLogHeaderSize;
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap::const_reverse_iterator rit =
+ rtcp_events.rbegin();
+
+ while (rit != rtcp_events.rend() &&
+ remaining_space >=
+ kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
+ const RtpTimestamp rtp_timestamp = rit->first;
+ RtcpReceiverFrameLogMessage frame_log(rtp_timestamp);
+ remaining_space -= kRtcpReceiverFrameLogSize;
+ ++*number_of_frames;
+
+ // Get all events of a single frame.
+ sorted_log_messages.clear();
+ do {
+ RtcpReceiverEventLogMessage event_log_message;
+ event_log_message.type = rit->second.type;
+ event_log_message.event_timestamp = rit->second.timestamp;
+ event_log_message.delay_delta = rit->second.delay_delta;
+ event_log_message.packet_id = rit->second.packet_id;
+ sorted_log_messages.push_back(event_log_message);
+ ++rit;
+ } while (rit != rtcp_events.rend() && rit->first == rtp_timestamp);
+
+ std::sort(sorted_log_messages.begin(),
+ sorted_log_messages.end(),
+ &EventTimestampLessThan);
+
+ // From |sorted_log_messages|, only take events that are no greater than
+ // |kMaxWireFormatTimeDeltaMs| seconds away from the latest event. Events
+ // older than that cannot be encoded over the wire.
+ std::vector<RtcpReceiverEventLogMessage>::reverse_iterator sorted_rit =
+ sorted_log_messages.rbegin();
+ base::TimeTicks first_event_timestamp = sorted_rit->event_timestamp;
+ size_t events_in_frame = 0;
+ while (sorted_rit != sorted_log_messages.rend() &&
+ events_in_frame < kRtcpMaxReceiverLogMessages &&
+ remaining_space >= kRtcpReceiverEventLogSize) {
+ base::TimeDelta delta(first_event_timestamp -
+ sorted_rit->event_timestamp);
+ if (delta.InMilliseconds() > kMaxWireFormatTimeDeltaMs)
+ break;
+ frame_log.event_log_messages_.push_front(*sorted_rit);
+ ++events_in_frame;
+ ++*total_number_of_messages_to_send;
+ remaining_space -= kRtcpReceiverEventLogSize;
+ ++sorted_rit;
+ }
+
+ receiver_log_message->push_front(frame_log);
+ }
+
+ rtcp_events_history_.push_front(*receiver_log_message);
+
+ // We don't try to match RTP timestamps of redundancy frame logs with those
+ // from the newest set (which would save the space of an extra RTP timestamp
+ // over the wire). Unless the redundancy frame logs are very recent, it's
+ // unlikely there will be a match anyway.
+ if (rtcp_events_history_.size() > kFirstRedundancyOffset) {
+ // Add first redundnacy messages, if enough space remaining
+ AddReceiverLog(rtcp_events_history_[kFirstRedundancyOffset],
+ receiver_log_message,
+ &remaining_space,
+ number_of_frames,
+ total_number_of_messages_to_send);
+ }
+
+ if (rtcp_events_history_.size() > kSecondRedundancyOffset) {
+ // Add second redundancy messages, if enough space remaining
+ AddReceiverLog(rtcp_events_history_[kSecondRedundancyOffset],
+ receiver_log_message,
+ &remaining_space,
+ number_of_frames,
+ total_number_of_messages_to_send);
+ }
+
+ if (rtcp_events_history_.size() > kReceiveLogMessageHistorySize) {
+ rtcp_events_history_.pop_back();
+ }
+
+ DCHECK_LE(rtcp_events_history_.size(), kReceiveLogMessageHistorySize);
+
+ *rtcp_log_size =
+ kRtcpCastLogHeaderSize + *number_of_frames * kRtcpReceiverFrameLogSize +
+ *total_number_of_messages_to_send * kRtcpReceiverEventLogSize;
+ DCHECK_GE(kMaxIpPacketSize, start_size + *rtcp_log_size)
+ << "Not enough buffer space.";
+
+ VLOG(3) << "number of frames: " << *number_of_frames;
+ VLOG(3) << "total messages to send: " << *total_number_of_messages_to_send;
+ VLOG(3) << "rtcp log size: " << *rtcp_log_size;
+ return *number_of_frames > 0;
}
} // namespace cast
diff --git a/chromium/media/cast/rtcp/rtcp_sender.h b/chromium/media/cast/rtcp/rtcp_sender.h
index e931c693c0f..f09a4fb0e53 100644
--- a/chromium/media/cast/rtcp/rtcp_sender.h
+++ b/chromium/media/cast/rtcp/rtcp_sender.h
@@ -5,95 +5,96 @@
#ifndef MEDIA_CAST_RTCP_RTCP_SENDER_H_
#define MEDIA_CAST_RTCP_RTCP_SENDER_H_
+#include <deque>
#include <list>
#include <string>
#include "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/receiver_rtcp_event_subscriber.h"
#include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/rtcp/rtcp_builder.h"
namespace media {
namespace cast {
+// We limit the size of receiver logs to avoid queuing up packets.
+const size_t kMaxReceiverLogBytes = 200;
+
+// The determines how long to hold receiver log events, based on how
+// many "receiver log message reports" ago the events were sent.
+const size_t kReceiveLogMessageHistorySize = 20;
+
+// This determines when to send events the second time.
+const size_t kFirstRedundancyOffset = 10;
+COMPILE_ASSERT(kFirstRedundancyOffset > 0 &&
+ kFirstRedundancyOffset <= kReceiveLogMessageHistorySize,
+ redundancy_offset_out_of_range);
+
+// When to send events the third time.
+const size_t kSecondRedundancyOffset = 20;
+COMPILE_ASSERT(kSecondRedundancyOffset >
+ kFirstRedundancyOffset && kSecondRedundancyOffset <=
+ kReceiveLogMessageHistorySize,
+ redundancy_offset_out_of_range);
+
+// TODO(mikhal): Resolve duplication between this and RtcpBuilder.
class RtcpSender {
public:
RtcpSender(scoped_refptr<CastEnvironment> cast_environment,
- PacedPacketSender* const paced_packet_sender,
+ transport::PacedPacketSender* outgoing_transport,
uint32 sending_ssrc,
const std::string& c_name);
virtual ~RtcpSender();
- void SendRtcpFromRtpSender(uint32 packet_type_flags,
- const RtcpSenderInfo* sender_info,
- const RtcpDlrrReportBlock* dlrr,
- RtcpSenderLogMessage* sender_log);
-
- void SendRtcpFromRtpReceiver(uint32 packet_type_flags,
- const RtcpReportBlock* report_block,
- const RtcpReceiverReferenceTimeReport* rrtr,
- const RtcpCastMessage* cast_message,
- RtcpReceiverLogMessage* receiver_log);
-
- enum RtcpPacketType {
- kRtcpSr = 0x0002,
- kRtcpRr = 0x0004,
- kRtcpBye = 0x0008,
- kRtcpPli = 0x0010,
- kRtcpNack = 0x0020,
- kRtcpFir = 0x0040,
- kRtcpSrReq = 0x0200,
- kRtcpDlrr = 0x0400,
- kRtcpRrtr = 0x0800,
- kRtcpRpsi = 0x8000,
- kRtcpRemb = 0x10000,
- kRtcpCast = 0x20000,
- kRtcpSenderLog = 0x40000,
- kRtcpReceiverLog = 0x80000,
- };
+ void SendRtcpFromRtpReceiver(
+ uint32 packet_type_flags,
+ const transport::RtcpReportBlock* report_block,
+ const RtcpReceiverReferenceTimeReport* rrtr,
+ const RtcpCastMessage* cast_message,
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
+ uint16 target_delay_ms);
private:
- void BuildSR(const RtcpSenderInfo& sender_info,
- const RtcpReportBlock* report_block,
- std::vector<uint8>* packet) const;
+ void BuildRR(const transport::RtcpReportBlock* report_block,
+ Packet* packet) const;
- void BuildRR(const RtcpReportBlock* report_block,
- std::vector<uint8>* packet) const;
+ void AddReportBlocks(const transport::RtcpReportBlock& report_block,
+ Packet* packet) const;
- void AddReportBlocks(const RtcpReportBlock& report_block,
- std::vector<uint8>* packet) const;
+ void BuildSdec(Packet* packet) const;
- void BuildSdec(std::vector<uint8>* packet) const;
+ void BuildPli(uint32 remote_ssrc, Packet* packet) const;
- void BuildPli(uint32 remote_ssrc,
- std::vector<uint8>* packet) const;
+ void BuildRemb(const RtcpRembMessage* remb, Packet* packet) const;
- void BuildRemb(const RtcpRembMessage* remb,
- std::vector<uint8>* packet) const;
+ void BuildRpsi(const RtcpRpsiMessage* rpsi, Packet* packet) const;
- void BuildRpsi(const RtcpRpsiMessage* rpsi,
- std::vector<uint8>* packet) const;
+ void BuildNack(const RtcpNackMessage* nack, Packet* packet) const;
- void BuildNack(const RtcpNackMessage* nack,
- std::vector<uint8>* packet) const;
-
- void BuildBye(std::vector<uint8>* packet) const;
-
- void BuildDlrrRb(const RtcpDlrrReportBlock* dlrr,
- std::vector<uint8>* packet) const;
+ void BuildBye(Packet* packet) const;
void BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
- std::vector<uint8>* packet) const;
+ Packet* packet) const;
void BuildCast(const RtcpCastMessage* cast_message,
- std::vector<uint8>* packet) const;
+ uint16 target_delay_ms,
+ Packet* packet) const;
- void BuildSenderLog(RtcpSenderLogMessage* sender_log_message,
- std::vector<uint8>* packet) const;
+ void BuildReceiverLog(
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
+ Packet* packet);
- void BuildReceiverLog(RtcpReceiverLogMessage* receiver_log_message,
- std::vector<uint8>* packet) const;
+ bool BuildRtcpReceiverLogMessage(
+ const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
+ size_t start_size,
+ RtcpReceiverLogMessage* receiver_log_message,
+ size_t* number_of_frames,
+ size_t* total_number_of_messages_to_send,
+ size_t* rtcp_log_size);
inline void BitrateToRembExponentBitrate(uint32 bitrate,
uint8* exponent,
@@ -113,9 +114,11 @@ class RtcpSender {
const std::string c_name_;
// Not owned by this class.
- PacedPacketSender* transport_;
+ transport::PacedPacketSender* const transport_;
scoped_refptr<CastEnvironment> cast_environment_;
+ std::deque<RtcpReceiverLogMessage> rtcp_events_history_;
+
DISALLOW_COPY_AND_ASSIGN(RtcpSender);
};
diff --git a/chromium/media/cast/rtcp/rtcp_sender_unittest.cc b/chromium/media/cast/rtcp/rtcp_sender_unittest.cc
index 16e9ee18ffb..0b0c7d3ab89 100644
--- a/chromium/media/cast/rtcp/rtcp_sender_unittest.cc
+++ b/chromium/media/cast/rtcp/rtcp_sender_unittest.cc
@@ -6,11 +6,13 @@
#include "base/test/simple_test_tick_clock.h"
#include "media/cast/cast_defines.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/pacing/paced_sender.h"
+#include "media/cast/rtcp/receiver_rtcp_event_subscriber.h"
#include "media/cast/rtcp/rtcp_sender.h"
#include "media/cast/rtcp/rtcp_utility.h"
#include "media/cast/rtcp/test_rtcp_packet_builder.h"
-#include "media/cast/test/fake_task_runner.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/pacing/paced_sender.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
@@ -19,95 +21,99 @@ namespace cast {
namespace {
static const uint32 kSendingSsrc = 0x12345678;
static const uint32 kMediaSsrc = 0x87654321;
+static const int16 kDefaultDelay = 100;
static const std::string kCName("test@10.1.1.1");
+
+transport::RtcpReportBlock GetReportBlock() {
+ transport::RtcpReportBlock report_block;
+ // Initialize remote_ssrc to a "clearly illegal" value.
+ report_block.remote_ssrc = 0xDEAD;
+ report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
+ report_block.fraction_lost = kLoss >> 24;
+ report_block.cumulative_lost = kLoss; // 24 bits valid.
+ report_block.extended_high_sequence_number = kExtendedMax;
+ report_block.jitter = kTestJitter;
+ report_block.last_sr = kLastSr;
+ report_block.delay_since_last_sr = kDelayLastSr;
+ return report_block;
+}
+
} // namespace
-class TestRtcpTransport : public PacedPacketSender {
+class TestRtcpTransport : public transport::PacedPacketSender {
public:
- TestRtcpTransport()
- : expected_packet_length_(0),
- packet_count_(0) {
- }
-
- virtual bool SendRtcpPacket(const Packet& packet) OVERRIDE {
- EXPECT_EQ(expected_packet_length_, packet.size());
- EXPECT_EQ(0, memcmp(expected_packet_, &(packet[0]), packet.size()));
+ TestRtcpTransport() : packet_count_(0) {}
+
+ virtual bool SendRtcpPacket(uint32 ssrc,
+ transport::PacketRef packet) OVERRIDE {
+ EXPECT_EQ(expected_packet_.size(), packet->data.size());
+ EXPECT_EQ(0, memcmp(expected_packet_.data(),
+ packet->data.data(),
+ packet->data.size()));
packet_count_++;
return true;
}
- virtual bool SendPackets(const PacketList& packets) OVERRIDE {
+ virtual bool SendPackets(
+ const transport::SendPacketVector& packets) OVERRIDE {
return false;
}
-
- virtual bool ResendPackets(const PacketList& packets) OVERRIDE {
+ virtual bool ResendPackets(
+ const transport::SendPacketVector& packets,
+ base::TimeDelta dedupe_window) OVERRIDE {
return false;
}
- void SetExpectedRtcpPacket(const uint8* rtcp_buffer, size_t length) {
- expected_packet_length_ = length;
- memcpy(expected_packet_, rtcp_buffer, length);
+ virtual void CancelSendingPacket(
+ const transport::PacketKey& packet_key) OVERRIDE {
+ }
+
+ void SetExpectedRtcpPacket(scoped_ptr<Packet> packet) {
+ expected_packet_.swap(*packet);
}
int packet_count() const { return packet_count_; }
private:
- uint8 expected_packet_[kIpPacketSize];
- size_t expected_packet_length_;
+ Packet expected_packet_;
int packet_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRtcpTransport);
};
class RtcpSenderTest : public ::testing::Test {
protected:
RtcpSenderTest()
- : task_runner_(new test::FakeTaskRunner(&testing_clock_)),
- cast_environment_(new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig())),
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)),
rtcp_sender_(new RtcpSender(cast_environment_,
&test_transport_,
kSendingSsrc,
- kCName)) {
- }
+ kCName)) {}
- base::SimpleTestTickClock testing_clock_;
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
TestRtcpTransport test_transport_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_refptr<CastEnvironment> cast_environment_;
scoped_ptr<RtcpSender> rtcp_sender_;
-};
-
-TEST_F(RtcpSenderTest, RtcpSenderReport) {
- RtcpSenderInfo sender_info;
- sender_info.ntp_seconds = kNtpHigh;
- sender_info.ntp_fraction = kNtpLow;
- sender_info.rtp_timestamp = kRtpTimestamp;
- sender_info.send_packet_count = kSendPacketCount;
- sender_info.send_octet_count = kSendOctetCount;
- // Sender report + c_name.
- TestRtcpPacketBuilder p;
- p.AddSr(kSendingSsrc, 0);
- p.AddSdesCname(kSendingSsrc, kCName);
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
-
- rtcp_sender_->SendRtcpFromRtpSender(RtcpSender::kRtcpSr,
- &sender_info,
- NULL,
- NULL);
-
- EXPECT_EQ(1, test_transport_.packet_count());
-}
+ DISALLOW_COPY_AND_ASSIGN(RtcpSenderTest);
+};
TEST_F(RtcpSenderTest, RtcpReceiverReport) {
// Empty receiver report + c_name.
TestRtcpPacketBuilder p1;
p1.AddRr(kSendingSsrc, 0);
p1.AddSdesCname(kSendingSsrc, kCName);
- test_transport_.SetExpectedRtcpPacket(p1.Packet(), p1.Length());
+ test_transport_.SetExpectedRtcpPacket(p1.GetPacket());
- rtcp_sender_->SendRtcpFromRtpReceiver(RtcpSender::kRtcpRr,
- NULL, NULL, NULL, NULL);
+ rtcp_sender_->SendRtcpFromRtpReceiver(
+ transport::kRtcpRr, NULL, NULL, NULL, NULL, kDefaultDelay);
EXPECT_EQ(1, test_transport_.packet_count());
@@ -116,133 +122,16 @@ TEST_F(RtcpSenderTest, RtcpReceiverReport) {
p2.AddRr(kSendingSsrc, 1);
p2.AddRb(kMediaSsrc);
p2.AddSdesCname(kSendingSsrc, kCName);
- test_transport_.SetExpectedRtcpPacket(p2.Packet(), p2.Length());
+ test_transport_.SetExpectedRtcpPacket(p2.GetPacket().Pass());
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
- rtcp_sender_->SendRtcpFromRtpReceiver(RtcpSender::kRtcpRr, &report_block,
- NULL, NULL, NULL);
+ rtcp_sender_->SendRtcpFromRtpReceiver(
+ transport::kRtcpRr, &report_block, NULL, NULL, NULL, kDefaultDelay);
EXPECT_EQ(2, test_transport_.packet_count());
}
-TEST_F(RtcpSenderTest, RtcpSenderReportWithDlrr) {
- RtcpSenderInfo sender_info;
- sender_info.ntp_seconds = kNtpHigh;
- sender_info.ntp_fraction = kNtpLow;
- sender_info.rtp_timestamp = kRtpTimestamp;
- sender_info.send_packet_count = kSendPacketCount;
- sender_info.send_octet_count = kSendOctetCount;
-
- // Sender report + c_name + dlrr.
- TestRtcpPacketBuilder p1;
- p1.AddSr(kSendingSsrc, 0);
- p1.AddSdesCname(kSendingSsrc, kCName);
- p1.AddXrHeader(kSendingSsrc);
- p1.AddXrDlrrBlock(kSendingSsrc);
- test_transport_.SetExpectedRtcpPacket(p1.Packet(), p1.Length());
-
- RtcpDlrrReportBlock dlrr_rb;
- dlrr_rb.last_rr = kLastRr;
- dlrr_rb.delay_since_last_rr = kDelayLastRr;
-
- rtcp_sender_->SendRtcpFromRtpSender(
- RtcpSender::kRtcpSr | RtcpSender::kRtcpDlrr,
- &sender_info,
- &dlrr_rb,
- NULL);
-
- EXPECT_EQ(1, test_transport_.packet_count());
-}
-
-TEST_F(RtcpSenderTest, RtcpSenderReportWithDlrrAndLog) {
- RtcpSenderInfo sender_info;
- sender_info.ntp_seconds = kNtpHigh;
- sender_info.ntp_fraction = kNtpLow;
- sender_info.rtp_timestamp = kRtpTimestamp;
- sender_info.send_packet_count = kSendPacketCount;
- sender_info.send_octet_count = kSendOctetCount;
-
- // Sender report + c_name + dlrr + sender log.
- TestRtcpPacketBuilder p;
- p.AddSr(kSendingSsrc, 0);
- p.AddSdesCname(kSendingSsrc, kCName);
- p.AddXrHeader(kSendingSsrc);
- p.AddXrDlrrBlock(kSendingSsrc);
- p.AddSenderLog(kSendingSsrc);
- p.AddSenderFrameLog(kRtcpSenderFrameStatusSentToNetwork, kRtpTimestamp);
-
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
-
- RtcpDlrrReportBlock dlrr_rb;
- dlrr_rb.last_rr = kLastRr;
- dlrr_rb.delay_since_last_rr = kDelayLastRr;
-
- RtcpSenderFrameLogMessage sender_frame_log;
- sender_frame_log.frame_status = kRtcpSenderFrameStatusSentToNetwork;
- sender_frame_log.rtp_timestamp = kRtpTimestamp;
-
- RtcpSenderLogMessage sender_log;
- sender_log.push_back(sender_frame_log);
-
- rtcp_sender_->SendRtcpFromRtpSender(
- RtcpSender::kRtcpSr | RtcpSender::kRtcpDlrr | RtcpSender::kRtcpSenderLog,
- &sender_info,
- &dlrr_rb,
- &sender_log);
-
- EXPECT_EQ(1, test_transport_.packet_count());
- EXPECT_TRUE(sender_log.empty());
-}
-
-TEST_F(RtcpSenderTest, RtcpSenderReporWithTooManyLogFrames) {
- RtcpSenderInfo sender_info;
- sender_info.ntp_seconds = kNtpHigh;
- sender_info.ntp_fraction = kNtpLow;
- sender_info.rtp_timestamp = kRtpTimestamp;
- sender_info.send_packet_count = kSendPacketCount;
- sender_info.send_octet_count = kSendOctetCount;
-
- // Sender report + c_name + sender log.
- TestRtcpPacketBuilder p;
- p.AddSr(kSendingSsrc, 0);
- p.AddSdesCname(kSendingSsrc, kCName);
- p.AddSenderLog(kSendingSsrc);
-
- for (int i = 0; i < 359; ++i) {
- p.AddSenderFrameLog(kRtcpSenderFrameStatusSentToNetwork,
- kRtpTimestamp + i * 90);
- }
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
-
-
- RtcpSenderLogMessage sender_log;
- for (int j = 0; j < 400; ++j) {
- RtcpSenderFrameLogMessage sender_frame_log;
- sender_frame_log.frame_status = kRtcpSenderFrameStatusSentToNetwork;
- sender_frame_log.rtp_timestamp = kRtpTimestamp + j * 90;
- sender_log.push_back(sender_frame_log);
- }
-
- rtcp_sender_->SendRtcpFromRtpSender(
- RtcpSender::kRtcpSr | RtcpSender::kRtcpSenderLog,
- &sender_info,
- NULL,
- &sender_log);
-
- EXPECT_EQ(1, test_transport_.packet_count());
- EXPECT_EQ(41u, sender_log.size());
-}
-
TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtr) {
// Receiver report with report block + c_name.
TestRtcpPacketBuilder p;
@@ -251,29 +140,21 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtr) {
p.AddSdesCname(kSendingSsrc, kCName);
p.AddXrHeader(kSendingSsrc);
p.AddXrRrtrBlock();
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
RtcpReceiverReferenceTimeReport rrtr;
rrtr.ntp_seconds = kNtpHigh;
rrtr.ntp_fraction = kNtpLow;
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpRrtr,
+ transport::kRtcpRr | transport::kRtcpRrtr,
&report_block,
&rrtr,
NULL,
- NULL);
+ NULL,
+ kDefaultDelay);
EXPECT_EQ(1, test_transport_.packet_count());
}
@@ -284,19 +165,10 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithCast) {
p.AddRr(kSendingSsrc, 1);
p.AddRb(kMediaSsrc);
p.AddSdesCname(kSendingSsrc, kCName);
- p.AddCast(kSendingSsrc, kMediaSsrc);
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+ p.AddCast(kSendingSsrc, kMediaSsrc, kDefaultDelay);
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
RtcpCastMessage cast_message(kMediaSsrc);
cast_message.ack_frame_id_ = kAckFrameId;
@@ -310,11 +182,12 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithCast) {
missing_packets;
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpCast,
+ transport::kRtcpRr | transport::kRtcpCast,
&report_block,
NULL,
&cast_message,
- NULL);
+ NULL,
+ kDefaultDelay);
EXPECT_EQ(1, test_transport_.packet_count());
}
@@ -326,19 +199,10 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtraAndCastMessage) {
p.AddSdesCname(kSendingSsrc, kCName);
p.AddXrHeader(kSendingSsrc);
p.AddXrRrtrBlock();
- p.AddCast(kSendingSsrc, kMediaSsrc);
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+ p.AddCast(kSendingSsrc, kMediaSsrc, kDefaultDelay);
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
RtcpReceiverReferenceTimeReport rrtr;
rrtr.ntp_seconds = kNtpHigh;
@@ -356,11 +220,12 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtraAndCastMessage) {
missing_packets;
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpRrtr | RtcpSender::kRtcpCast,
+ transport::kRtcpRr | transport::kRtcpRrtr | transport::kRtcpCast,
&report_block,
&rrtr,
&cast_message,
- NULL);
+ NULL,
+ kDefaultDelay);
EXPECT_EQ(1, test_transport_.packet_count());
}
@@ -368,7 +233,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtraAndCastMessage) {
TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtrCastMessageAndLog) {
static const uint32 kTimeBaseMs = 12345678;
static const uint32 kTimeDelayMs = 10;
- static const uint32 kDelayDeltaMs = 123;
TestRtcpPacketBuilder p;
p.AddRr(kSendingSsrc, 1);
@@ -376,19 +240,10 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtrCastMessageAndLog) {
p.AddSdesCname(kSendingSsrc, kCName);
p.AddXrHeader(kSendingSsrc);
p.AddXrRrtrBlock();
- p.AddCast(kSendingSsrc, kMediaSsrc);
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+ p.AddCast(kSendingSsrc, kMediaSsrc, kDefaultDelay);
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
RtcpReceiverReferenceTimeReport rrtr;
rrtr.ntp_seconds = kNtpHigh;
@@ -405,185 +260,296 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtrCastMessageAndLog) {
cast_message.missing_frames_and_packets_[kFrameIdWithLostPackets] =
missing_packets;
- // Test empty Log message.
- RtcpReceiverLogMessage receiver_log;
+ ReceiverRtcpEventSubscriber event_subscriber(500, VIDEO_EVENT);
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
- VLOG(0) << " Test empty Log " ;
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpRrtr | RtcpSender::kRtcpCast |
- RtcpSender::kRtcpReceiverLog,
+ transport::kRtcpRr | transport::kRtcpRrtr | transport::kRtcpCast |
+ transport::kRtcpReceiverLog,
&report_block,
&rrtr,
&cast_message,
- &receiver_log);
-
+ &rtcp_events,
+ kDefaultDelay);
base::SimpleTestTickClock testing_clock;
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
p.AddReceiverLog(kSendingSsrc);
p.AddReceiverFrameLog(kRtpTimestamp, 2, kTimeBaseMs);
- p.AddReceiverEventLog(kDelayDeltaMs, 1, 0);
- p.AddReceiverEventLog(kLostPacketId1, 6, kTimeDelayMs);
-
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+ p.AddReceiverEventLog(0, FRAME_ACK_SENT, 0);
+ p.AddReceiverEventLog(kLostPacketId1, PACKET_RECEIVED, kTimeDelayMs);
- RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
- RtcpReceiverEventLogMessage event_log;
-
- event_log.type = kAckSent;
- event_log.event_timestamp = testing_clock.NowTicks();
- event_log.delay_delta = base::TimeDelta::FromMilliseconds(kDelayDeltaMs);
- frame_log.event_log_messages_.push_back(event_log);
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
+ FrameEvent frame_event;
+ frame_event.rtp_timestamp = kRtpTimestamp;
+ frame_event.type = FRAME_ACK_SENT;
+ frame_event.media_type = VIDEO_EVENT;
+ frame_event.timestamp = testing_clock.NowTicks();
+ event_subscriber.OnReceiveFrameEvent(frame_event);
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs));
- event_log.type = kPacketReceived;
- event_log.event_timestamp = testing_clock.NowTicks();
- event_log.packet_id = kLostPacketId1;
- frame_log.event_log_messages_.push_back(event_log);
- receiver_log.push_back(frame_log);
+ PacketEvent packet_event;
+ packet_event.rtp_timestamp = kRtpTimestamp;
+ packet_event.type = PACKET_RECEIVED;
+ packet_event.media_type = VIDEO_EVENT;
+ packet_event.timestamp = testing_clock.NowTicks();
+ packet_event.packet_id = kLostPacketId1;
+ event_subscriber.OnReceivePacketEvent(packet_event);
+ event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
+ EXPECT_EQ(2u, rtcp_events.size());
- VLOG(0) << " Test Log " ;
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpRrtr | RtcpSender::kRtcpCast |
- RtcpSender::kRtcpReceiverLog,
+ transport::kRtcpRr | transport::kRtcpRrtr | transport::kRtcpCast |
+ transport::kRtcpReceiverLog,
&report_block,
&rrtr,
&cast_message,
- &receiver_log);
+ &rtcp_events,
+ kDefaultDelay);
- EXPECT_TRUE(receiver_log.empty());
EXPECT_EQ(2, test_transport_.packet_count());
}
TEST_F(RtcpSenderTest, RtcpReceiverReportWithOversizedFrameLog) {
static const uint32 kTimeBaseMs = 12345678;
static const uint32 kTimeDelayMs = 10;
- static const uint32 kDelayDeltaMs = 123;
TestRtcpPacketBuilder p;
p.AddRr(kSendingSsrc, 1);
p.AddRb(kMediaSsrc);
p.AddSdesCname(kSendingSsrc, kCName);
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
base::SimpleTestTickClock testing_clock;
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
p.AddReceiverLog(kSendingSsrc);
- p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs);
- p.AddReceiverEventLog(kDelayDeltaMs, 1, 0);
- p.AddReceiverFrameLog(kRtpTimestamp + 2345,
- kRtcpMaxReceiverLogMessages, kTimeBaseMs);
-
- for (size_t i = 0; i < kRtcpMaxReceiverLogMessages; ++i) {
+ int remaining_bytes = kMaxReceiverLogBytes;
+ remaining_bytes -= kRtcpCastLogHeaderSize;
+
+ remaining_bytes -= kRtcpReceiverFrameLogSize;
+ int num_events = remaining_bytes / kRtcpReceiverEventLogSize;
+ EXPECT_LE(num_events, static_cast<int>(kRtcpMaxReceiverLogMessages));
+ // Only the last |num_events| events are sent due to receiver log size cap.
+ p.AddReceiverFrameLog(
+ kRtpTimestamp + 2345,
+ num_events,
+ kTimeBaseMs + (kRtcpMaxReceiverLogMessages - num_events) * kTimeDelayMs);
+ for (int i = 0; i < num_events; i++) {
p.AddReceiverEventLog(
- kLostPacketId1, 6, static_cast<uint16>(kTimeDelayMs * i));
+ kLostPacketId1, PACKET_RECEIVED,
+ static_cast<uint16>(kTimeDelayMs * i));
}
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
-
- RtcpReceiverFrameLogMessage frame_1_log(kRtpTimestamp);
- RtcpReceiverEventLogMessage event_log;
-
- event_log.type = kAckSent;
- event_log.event_timestamp = testing_clock.NowTicks();
- event_log.delay_delta = base::TimeDelta::FromMilliseconds(kDelayDeltaMs);
- frame_1_log.event_log_messages_.push_back(event_log);
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- RtcpReceiverLogMessage receiver_log;
- receiver_log.push_back(frame_1_log);
+ ReceiverRtcpEventSubscriber event_subscriber(500, VIDEO_EVENT);
+ FrameEvent frame_event;
+ frame_event.rtp_timestamp = kRtpTimestamp;
+ frame_event.type = FRAME_ACK_SENT;
+ frame_event.media_type = VIDEO_EVENT;
+ frame_event.timestamp = testing_clock.NowTicks();
+ event_subscriber.OnReceiveFrameEvent(frame_event);
- RtcpReceiverFrameLogMessage frame_2_log(kRtpTimestamp + 2345);
-
- for (int j = 0; j < 300; ++j) {
- event_log.type = kPacketReceived;
- event_log.event_timestamp = testing_clock.NowTicks();
- event_log.packet_id = kLostPacketId1;
- frame_2_log.event_log_messages_.push_back(event_log);
+ for (size_t i = 0; i < kRtcpMaxReceiverLogMessages; ++i) {
+ PacketEvent packet_event;
+ packet_event.rtp_timestamp = kRtpTimestamp + 2345;
+ packet_event.type = PACKET_RECEIVED;
+ packet_event.media_type = VIDEO_EVENT;
+ packet_event.timestamp = testing_clock.NowTicks();
+ packet_event.packet_id = kLostPacketId1;
+ event_subscriber.OnReceivePacketEvent(packet_event);
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs));
}
- receiver_log.push_back(frame_2_log);
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpReceiverLog,
+ transport::kRtcpRr | transport::kRtcpReceiverLog,
&report_block,
NULL,
NULL,
- &receiver_log);
+ &rtcp_events,
+ kDefaultDelay);
EXPECT_EQ(1, test_transport_.packet_count());
- EXPECT_EQ(1u, receiver_log.size());
- EXPECT_EQ(300u - kRtcpMaxReceiverLogMessages,
- receiver_log.front().event_log_messages_.size());
}
TEST_F(RtcpSenderTest, RtcpReceiverReportWithTooManyLogFrames) {
static const uint32 kTimeBaseMs = 12345678;
static const uint32 kTimeDelayMs = 10;
- static const uint32 kDelayDeltaMs = 123;
TestRtcpPacketBuilder p;
p.AddRr(kSendingSsrc, 1);
p.AddRb(kMediaSsrc);
p.AddSdesCname(kSendingSsrc, kCName);
- RtcpReportBlock report_block;
- // Initialize remote_ssrc to a "clearly illegal" value.
- report_block.remote_ssrc = 0xDEAD;
- report_block.media_ssrc = kMediaSsrc; // SSRC of the RTP packet sender.
- report_block.fraction_lost = kLoss >> 24;
- report_block.cumulative_lost = kLoss; // 24 bits valid.
- report_block.extended_high_sequence_number = kExtendedMax;
- report_block.jitter = kTestJitter;
- report_block.last_sr = kLastSr;
- report_block.delay_since_last_sr = kDelayLastSr;
+ transport::RtcpReportBlock report_block = GetReportBlock();
base::SimpleTestTickClock testing_clock;
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
p.AddReceiverLog(kSendingSsrc);
- for (int i = 0; i < 119; ++i) {
- p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs);
- p.AddReceiverEventLog(kDelayDeltaMs, 1, 0);
- }
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+ int remaining_bytes = kMaxReceiverLogBytes;
+ remaining_bytes -= kRtcpCastLogHeaderSize;
+
+ int num_events =
+ remaining_bytes / (kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize);
- RtcpReceiverLogMessage receiver_log;
+ // The last |num_events| events are sent due to receiver log size cap.
+ for (size_t i = kRtcpMaxReceiverLogMessages - num_events;
+ i < kRtcpMaxReceiverLogMessages;
+ ++i) {
+ p.AddReceiverFrameLog(kRtpTimestamp + i, 1, kTimeBaseMs + i * kTimeDelayMs);
+ p.AddReceiverEventLog(0, FRAME_ACK_SENT, 0);
+ }
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- for (int j = 0; j < 200; ++j) {
- RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
- RtcpReceiverEventLogMessage event_log;
+ ReceiverRtcpEventSubscriber event_subscriber(500, VIDEO_EVENT);
- event_log.type = kAckSent;
- event_log.event_timestamp = testing_clock.NowTicks();
- event_log.delay_delta = base::TimeDelta::FromMilliseconds(kDelayDeltaMs);
- frame_log.event_log_messages_.push_back(event_log);
- receiver_log.push_back(frame_log);
+ for (size_t i = 0; i < kRtcpMaxReceiverLogMessages; ++i) {
+ FrameEvent frame_event;
+ frame_event.rtp_timestamp = kRtpTimestamp + static_cast<int>(i);
+ frame_event.type = FRAME_ACK_SENT;
+ frame_event.media_type = VIDEO_EVENT;
+ frame_event.timestamp = testing_clock.NowTicks();
+ event_subscriber.OnReceiveFrameEvent(frame_event);
testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs));
}
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
+
rtcp_sender_->SendRtcpFromRtpReceiver(
- RtcpSender::kRtcpRr | RtcpSender::kRtcpReceiverLog,
+ transport::kRtcpRr | transport::kRtcpReceiverLog,
&report_block,
NULL,
NULL,
- &receiver_log);
+ &rtcp_events,
+ kDefaultDelay);
EXPECT_EQ(1, test_transport_.packet_count());
- EXPECT_EQ(81u, receiver_log.size());
+}
+
+TEST_F(RtcpSenderTest, RtcpReceiverReportWithOldLogFrames) {
+ static const uint32 kTimeBaseMs = 12345678;
+
+ TestRtcpPacketBuilder p;
+ p.AddRr(kSendingSsrc, 1);
+ p.AddRb(kMediaSsrc);
+ p.AddSdesCname(kSendingSsrc, kCName);
+
+ transport::RtcpReportBlock report_block = GetReportBlock();
+
+ base::SimpleTestTickClock testing_clock;
+ testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
+
+ p.AddReceiverLog(kSendingSsrc);
+
+ // Log 11 events for a single frame, each |kTimeBetweenEventsMs| apart.
+ // Only last 10 events will be sent because the first event is more than
+ // 4095 milliseconds away from latest event.
+ const int kTimeBetweenEventsMs = 410;
+ p.AddReceiverFrameLog(kRtpTimestamp, 10, kTimeBaseMs + kTimeBetweenEventsMs);
+ for (int i = 0; i < 10; ++i) {
+ p.AddReceiverEventLog(0, FRAME_ACK_SENT, i * kTimeBetweenEventsMs);
+ }
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
+
+ ReceiverRtcpEventSubscriber event_subscriber(500, VIDEO_EVENT);
+ for (int i = 0; i < 11; ++i) {
+ FrameEvent frame_event;
+ frame_event.rtp_timestamp = kRtpTimestamp;
+ frame_event.type = FRAME_ACK_SENT;
+ frame_event.media_type = VIDEO_EVENT;
+ frame_event.timestamp = testing_clock.NowTicks();
+ event_subscriber.OnReceiveFrameEvent(frame_event);
+ testing_clock.Advance(
+ base::TimeDelta::FromMilliseconds(kTimeBetweenEventsMs));
+ }
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
+
+ rtcp_sender_->SendRtcpFromRtpReceiver(
+ transport::kRtcpRr | transport::kRtcpReceiverLog,
+ &report_block,
+ NULL,
+ NULL,
+ &rtcp_events,
+ kDefaultDelay);
+
+ EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpSenderTest, RtcpReceiverReportRedundancy) {
+ uint32 time_base_ms = 12345678;
+ int kTimeBetweenEventsMs = 10;
+
+ transport::RtcpReportBlock report_block = GetReportBlock();
+
+ base::SimpleTestTickClock testing_clock;
+ testing_clock.Advance(base::TimeDelta::FromMilliseconds(time_base_ms));
+
+ ReceiverRtcpEventSubscriber event_subscriber(500, VIDEO_EVENT);
+ size_t packet_count = kReceiveLogMessageHistorySize + 10;
+ for (size_t i = 0; i < packet_count; i++) {
+ TestRtcpPacketBuilder p;
+ p.AddRr(kSendingSsrc, 1);
+ p.AddRb(kMediaSsrc);
+ p.AddSdesCname(kSendingSsrc, kCName);
+
+ p.AddReceiverLog(kSendingSsrc);
+
+ if (i >= kSecondRedundancyOffset) {
+ p.AddReceiverFrameLog(
+ kRtpTimestamp,
+ 1,
+ time_base_ms - kSecondRedundancyOffset * kTimeBetweenEventsMs);
+ p.AddReceiverEventLog(0, FRAME_ACK_SENT, 0);
+ }
+ if (i >= kFirstRedundancyOffset) {
+ p.AddReceiverFrameLog(
+ kRtpTimestamp,
+ 1,
+ time_base_ms - kFirstRedundancyOffset * kTimeBetweenEventsMs);
+ p.AddReceiverEventLog(0, FRAME_ACK_SENT, 0);
+ }
+ p.AddReceiverFrameLog(kRtpTimestamp, 1, time_base_ms);
+ p.AddReceiverEventLog(0, FRAME_ACK_SENT, 0);
+
+ test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
+
+ FrameEvent frame_event;
+ frame_event.rtp_timestamp = kRtpTimestamp;
+ frame_event.type = FRAME_ACK_SENT;
+ frame_event.media_type = VIDEO_EVENT;
+ frame_event.timestamp = testing_clock.NowTicks();
+ event_subscriber.OnReceiveFrameEvent(frame_event);
+
+ ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
+ event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
+
+ rtcp_sender_->SendRtcpFromRtpReceiver(
+ transport::kRtcpRr | transport::kRtcpReceiverLog,
+ &report_block,
+ NULL,
+ NULL,
+ &rtcp_events,
+ kDefaultDelay);
+
+ testing_clock.Advance(
+ base::TimeDelta::FromMilliseconds(kTimeBetweenEventsMs));
+ time_base_ms += kTimeBetweenEventsMs;
+ }
+
+ EXPECT_EQ(static_cast<int>(packet_count), test_transport_.packet_count());
}
} // namespace cast
diff --git a/chromium/media/cast/rtcp/rtcp_unittest.cc b/chromium/media/cast/rtcp/rtcp_unittest.cc
index 535f3c34f83..095e6d24df9 100644
--- a/chromium/media/cast/rtcp/rtcp_unittest.cc
+++ b/chromium/media/cast/rtcp/rtcp_unittest.cc
@@ -2,15 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include "base/test/simple_test_tick_clock.h"
#include "media/cast/cast_defines.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/rtcp/mock_rtcp_receiver_feedback.h"
#include "media/cast/rtcp/mock_rtcp_sender_feedback.h"
#include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtcp/test_rtcp_packet_builder.h"
-#include "media/cast/test/fake_task_runner.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_sender_impl.h"
+#include "media/cast/transport/pacing/paced_sender.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
@@ -22,60 +26,109 @@ static const uint32 kSenderSsrc = 0x10203;
static const uint32 kReceiverSsrc = 0x40506;
static const std::string kCName("test@10.1.1.1");
static const uint32 kRtcpIntervalMs = 500;
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
static const int64 kAddedDelay = 123;
-static const int64 kAddedShortDelay= 100;
+static const int64 kAddedShortDelay = 100;
-class LocalRtcpTransport : public PacedPacketSender {
+class RtcpTestPacketSender : public transport::PacketSender {
public:
- explicit LocalRtcpTransport(scoped_refptr<CastEnvironment> cast_environment,
- base::SimpleTestTickClock* testing_clock)
+ explicit RtcpTestPacketSender(base::SimpleTestTickClock* testing_clock)
: drop_packets_(false),
short_delay_(false),
+ rtcp_receiver_(NULL),
testing_clock_(testing_clock) {}
+ virtual ~RtcpTestPacketSender() {}
+ // Packet lists imply a RTP packet.
+ void set_rtcp_receiver(Rtcp* rtcp) { rtcp_receiver_ = rtcp; }
+
+ void set_short_delay() { short_delay_ = true; }
+
+ void set_drop_packets(bool drop_packets) { drop_packets_ = drop_packets; }
+
+ // A singular packet implies a RTCP packet.
+ virtual bool SendPacket(transport::PacketRef packet,
+ const base::Closure& cb) OVERRIDE {
+ if (short_delay_) {
+ testing_clock_->Advance(
+ base::TimeDelta::FromMilliseconds(kAddedShortDelay));
+ } else {
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(kAddedDelay));
+ }
+ if (drop_packets_)
+ return true;
+
+ rtcp_receiver_->IncomingRtcpPacket(&packet->data[0], packet->data.size());
+ return true;
+ }
- void SetRtcpReceiver(Rtcp* rtcp) { rtcp_ = rtcp; }
+ private:
+ bool drop_packets_;
+ bool short_delay_;
+ Rtcp* rtcp_receiver_;
+ base::SimpleTestTickClock* testing_clock_;
- void SetShortDelay() { short_delay_ = true; }
+ DISALLOW_COPY_AND_ASSIGN(RtcpTestPacketSender);
+};
- void SetDropPackets(bool drop_packets) { drop_packets_ = drop_packets; }
+class LocalRtcpTransport : public transport::PacedPacketSender {
+ public:
+ LocalRtcpTransport(scoped_refptr<CastEnvironment> cast_environment,
+ base::SimpleTestTickClock* testing_clock)
+ : drop_packets_(false),
+ short_delay_(false),
+ testing_clock_(testing_clock) {}
+ void set_rtcp_receiver(Rtcp* rtcp) { rtcp_ = rtcp; }
- virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE {
+ void set_short_delay() { short_delay_ = true; }
+
+ void set_drop_packets(bool drop_packets) { drop_packets_ = drop_packets; }
+
+ virtual bool SendRtcpPacket(uint32 ssrc,
+ transport::PacketRef packet) OVERRIDE {
if (short_delay_) {
testing_clock_->Advance(
base::TimeDelta::FromMilliseconds(kAddedShortDelay));
} else {
testing_clock_->Advance(base::TimeDelta::FromMilliseconds(kAddedDelay));
}
- if (drop_packets_) return true;
+ if (drop_packets_)
+ return true;
- rtcp_->IncomingRtcpPacket(&(packet[0]), packet.size());
+ rtcp_->IncomingRtcpPacket(&packet->data[0], packet->data.size());
return true;
}
- virtual bool SendPackets(const PacketList& packets) OVERRIDE {
+ virtual bool SendPackets(
+ const transport::SendPacketVector& packets) OVERRIDE {
return false;
}
- virtual bool ResendPackets(const PacketList& packets) OVERRIDE {
+ virtual bool ResendPackets(
+ const transport::SendPacketVector& packets,
+ base::TimeDelta dedupe_window) OVERRIDE {
return false;
}
+ virtual void CancelSendingPacket(
+ const transport::PacketKey& packet_key) OVERRIDE {
+ }
+
private:
bool drop_packets_;
bool short_delay_;
Rtcp* rtcp_;
base::SimpleTestTickClock* testing_clock_;
scoped_refptr<CastEnvironment> cast_environment_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalRtcpTransport);
};
class RtcpPeer : public Rtcp {
public:
RtcpPeer(scoped_refptr<CastEnvironment> cast_environment,
RtcpSenderFeedback* sender_feedback,
- PacedPacketSender* const paced_packet_sender,
- RtpSenderStatistics* rtp_sender_statistics,
+ transport::CastTransportSender* const transport_sender,
+ transport::PacedPacketSender* paced_packet_sender,
RtpReceiverStatistics* rtp_receiver_statistics,
RtcpMode rtcp_mode,
const base::TimeDelta& rtcp_interval,
@@ -84,95 +137,129 @@ class RtcpPeer : public Rtcp {
const std::string& c_name)
: Rtcp(cast_environment,
sender_feedback,
+ transport_sender,
paced_packet_sender,
- rtp_sender_statistics,
rtp_receiver_statistics,
rtcp_mode,
rtcp_interval,
local_ssrc,
remote_ssrc,
- c_name) {
- }
+ c_name,
+ AUDIO_EVENT) {}
- using Rtcp::CheckForWrapAround;
+ using Rtcp::OnReceivedNtp;
using Rtcp::OnReceivedLipSyncInfo;
};
class RtcpTest : public ::testing::Test {
protected:
RtcpTest()
- : task_runner_(new test::FakeTaskRunner(&testing_clock_)),
- cast_environment_(new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig())),
- transport_(cast_environment_, &testing_clock_) {
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)),
+ sender_to_receiver_(testing_clock_),
+ receiver_to_sender_(cast_environment_, testing_clock_) {
+ testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
+ net::IPEndPoint dummy_endpoint;
+ transport_sender_.reset(new transport::CastTransportSenderImpl(
+ NULL,
+ testing_clock_,
+ dummy_endpoint,
+ base::Bind(&UpdateCastTransportStatus),
+ transport::BulkRawEventsCallback(),
+ base::TimeDelta(),
+ task_runner_,
+ &sender_to_receiver_));
+ transport::CastTransportAudioConfig config;
+ config.rtp.config.ssrc = kSenderSsrc;
+ config.rtp.max_outstanding_frames = 1;
+ transport_sender_->InitializeAudio(config);
+ EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(0);
}
virtual ~RtcpTest() {}
- virtual void SetUp() {
- EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(0);
+ static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
+ bool result = (status == transport::TRANSPORT_AUDIO_INITIALIZED ||
+ status == transport::TRANSPORT_VIDEO_INITIALIZED);
+ EXPECT_TRUE(result);
+ }
+
+ void RunTasks(int during_ms) {
+ for (int i = 0; i < during_ms; ++i) {
+ // Call process the timers every 1 ms.
+ testing_clock_->Advance(base::TimeDelta::FromMilliseconds(1));
+ task_runner_->RunTasks();
+ }
}
- base::SimpleTestTickClock testing_clock_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_refptr<CastEnvironment> cast_environment_;
- LocalRtcpTransport transport_;
+ RtcpTestPacketSender sender_to_receiver_;
+ scoped_ptr<transport::CastTransportSenderImpl> transport_sender_;
+ LocalRtcpTransport receiver_to_sender_;
MockRtcpSenderFeedback mock_sender_feedback_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpTest);
};
TEST_F(RtcpTest, TimeToSend) {
- base::TimeTicks start_time;
- start_time += base::TimeDelta::FromMilliseconds(kStartMillisecond);
+ const base::TimeTicks start_time = testing_clock_->NowTicks();
Rtcp rtcp(cast_environment_,
&mock_sender_feedback_,
- &transport_,
- NULL,
+ transport_sender_.get(),
+ &receiver_to_sender_,
NULL,
kRtcpCompound,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kSenderSsrc,
kReceiverSsrc,
- kCName);
- transport_.SetRtcpReceiver(&rtcp);
+ kCName,
+ AUDIO_EVENT);
+ receiver_to_sender_.set_rtcp_receiver(&rtcp);
EXPECT_LE(start_time, rtcp.TimeToSendNextRtcpReport());
- EXPECT_GE(start_time + base::TimeDelta::FromMilliseconds(
- kRtcpIntervalMs * 3 / 2),
- rtcp.TimeToSendNextRtcpReport());
+ EXPECT_GE(
+ start_time + base::TimeDelta::FromMilliseconds(kRtcpIntervalMs * 3 / 2),
+ rtcp.TimeToSendNextRtcpReport());
base::TimeDelta delta = rtcp.TimeToSendNextRtcpReport() - start_time;
- testing_clock_.Advance(delta);
- EXPECT_EQ(testing_clock_.NowTicks(), rtcp.TimeToSendNextRtcpReport());
+ testing_clock_->Advance(delta);
+ EXPECT_EQ(testing_clock_->NowTicks(), rtcp.TimeToSendNextRtcpReport());
}
TEST_F(RtcpTest, BasicSenderReport) {
Rtcp rtcp(cast_environment_,
&mock_sender_feedback_,
- &transport_,
+ transport_sender_.get(),
NULL,
NULL,
kRtcpCompound,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kSenderSsrc,
kReceiverSsrc,
- kCName);
- transport_.SetRtcpReceiver(&rtcp);
- rtcp.SendRtcpFromRtpSender(NULL);
+ kCName,
+ AUDIO_EVENT);
+ sender_to_receiver_.set_rtcp_receiver(&rtcp);
+ rtcp.SendRtcpFromRtpSender(base::TimeTicks(), 0);
}
TEST_F(RtcpTest, BasicReceiverReport) {
Rtcp rtcp(cast_environment_,
&mock_sender_feedback_,
- &transport_,
NULL,
+ &receiver_to_sender_,
NULL,
kRtcpCompound,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kSenderSsrc,
kReceiverSsrc,
- kCName);
- transport_.SetRtcpReceiver(&rtcp);
+ kCName,
+ AUDIO_EVENT);
+ receiver_to_sender_.set_rtcp_receiver(&rtcp);
rtcp.SendRtcpFromRtpReceiver(NULL, NULL);
}
@@ -182,20 +269,20 @@ TEST_F(RtcpTest, BasicCast) {
// Media receiver.
Rtcp rtcp(cast_environment_,
&mock_sender_feedback_,
- &transport_,
NULL,
+ &receiver_to_sender_,
NULL,
kRtcpReducedSize,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kSenderSsrc,
kSenderSsrc,
- kCName);
- transport_.SetRtcpReceiver(&rtcp);
+ kCName,
+ AUDIO_EVENT);
+ receiver_to_sender_.set_rtcp_receiver(&rtcp);
RtcpCastMessage cast_message(kSenderSsrc);
cast_message.ack_frame_id_ = kAckFrameId;
PacketIdSet missing_packets;
- cast_message.missing_frames_and_packets_[
- kLostFrameId] = missing_packets;
+ cast_message.missing_frames_and_packets_[kLostFrameId] = missing_packets;
missing_packets.insert(kLostPacketId1);
missing_packets.insert(kLostPacketId2);
@@ -207,334 +294,254 @@ TEST_F(RtcpTest, BasicCast) {
TEST_F(RtcpTest, RttReducedSizeRtcp) {
// Media receiver.
- LocalRtcpTransport receiver_transport(cast_environment_, &testing_clock_);
Rtcp rtcp_receiver(cast_environment_,
&mock_sender_feedback_,
- &receiver_transport,
NULL,
+ &receiver_to_sender_,
NULL,
kRtcpReducedSize,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kReceiverSsrc,
kSenderSsrc,
- kCName);
+ kCName,
+ AUDIO_EVENT);
// Media sender.
- LocalRtcpTransport sender_transport(cast_environment_, &testing_clock_);
Rtcp rtcp_sender(cast_environment_,
&mock_sender_feedback_,
- &sender_transport,
+ transport_sender_.get(),
NULL,
NULL,
kRtcpReducedSize,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kSenderSsrc,
kReceiverSsrc,
- kCName);
+ kCName,
+ AUDIO_EVENT);
- receiver_transport.SetRtcpReceiver(&rtcp_sender);
- sender_transport.SetRtcpReceiver(&rtcp_receiver);
+ sender_to_receiver_.set_rtcp_receiver(&rtcp_receiver);
+ receiver_to_sender_.set_rtcp_receiver(&rtcp_sender);
base::TimeDelta rtt;
base::TimeDelta avg_rtt;
base::TimeDelta min_rtt;
base::TimeDelta max_rtt;
- EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- rtcp_sender.SendRtcpFromRtpSender(NULL);
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 1);
+ RunTasks(33);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
- rtcp_sender.SendRtcpFromRtpSender(NULL);
- EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
-
- EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+ EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 2);
+ RunTasks(33);
+ EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+
+ EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
}
TEST_F(RtcpTest, Rtt) {
// Media receiver.
- LocalRtcpTransport receiver_transport(cast_environment_, &testing_clock_);
Rtcp rtcp_receiver(cast_environment_,
&mock_sender_feedback_,
- &receiver_transport,
NULL,
+ &receiver_to_sender_,
NULL,
kRtcpCompound,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kReceiverSsrc,
kSenderSsrc,
- kCName);
+ kCName,
+ AUDIO_EVENT);
// Media sender.
- LocalRtcpTransport sender_transport(cast_environment_, &testing_clock_);
Rtcp rtcp_sender(cast_environment_,
&mock_sender_feedback_,
- &sender_transport,
+ transport_sender_.get(),
NULL,
NULL,
kRtcpCompound,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
kSenderSsrc,
kReceiverSsrc,
- kCName);
+ kCName,
+ AUDIO_EVENT);
- receiver_transport.SetRtcpReceiver(&rtcp_sender);
- sender_transport.SetRtcpReceiver(&rtcp_receiver);
+ receiver_to_sender_.set_rtcp_receiver(&rtcp_sender);
+ sender_to_receiver_.set_rtcp_receiver(&rtcp_receiver);
base::TimeDelta rtt;
base::TimeDelta avg_rtt;
base::TimeDelta min_rtt;
base::TimeDelta max_rtt;
- EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- rtcp_sender.SendRtcpFromRtpSender(NULL);
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 1);
+ RunTasks(33);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
- rtcp_sender.SendRtcpFromRtpSender(NULL);
- EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+ EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ RunTasks(33);
- receiver_transport.SetShortDelay();
- sender_transport.SetShortDelay();
- rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(kAddedDelay + kAddedShortDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR((kAddedShortDelay + 3 * kAddedDelay) / 2,
- avg_rtt.InMilliseconds(),
- 1);
- EXPECT_NEAR(kAddedDelay + kAddedShortDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+ EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ RunTasks(33);
- rtcp_sender.SendRtcpFromRtpSender(NULL);
- EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
+
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 2);
+ RunTasks(33);
+ EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
+
+ receiver_to_sender_.set_short_delay();
+ sender_to_receiver_.set_short_delay();
+ rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
+ EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(kAddedDelay + kAddedShortDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(
+ (kAddedShortDelay + 3 * kAddedDelay) / 2, avg_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(kAddedDelay + kAddedShortDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
+
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 3);
+ RunTasks(33);
+ EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
EXPECT_NEAR(2 * kAddedShortDelay, rtt.InMilliseconds(), 1);
EXPECT_NEAR((2 * kAddedShortDelay + 2 * kAddedDelay) / 2,
- avg_rtt.InMilliseconds(),
- 1);
- EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+ avg_rtt.InMilliseconds(),
+ 1);
+ EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(2 * kAddedShortDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+ EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(2 * kAddedShortDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(2 * kAddedShortDelay, rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 1);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+ EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(2 * kAddedShortDelay, rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 2);
+ EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
}
TEST_F(RtcpTest, RttWithPacketLoss) {
// Media receiver.
- LocalRtcpTransport receiver_transport(cast_environment_, &testing_clock_);
Rtcp rtcp_receiver(cast_environment_,
&mock_sender_feedback_,
- &receiver_transport,
NULL,
+ &receiver_to_sender_,
NULL,
kRtcpReducedSize,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
- kSenderSsrc,
kReceiverSsrc,
- kCName);
+ kSenderSsrc,
+ kCName,
+ AUDIO_EVENT);
// Media sender.
- LocalRtcpTransport sender_transport(cast_environment_, &testing_clock_);
Rtcp rtcp_sender(cast_environment_,
&mock_sender_feedback_,
- &sender_transport,
+ transport_sender_.get(),
NULL,
NULL,
kRtcpReducedSize,
base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
- kReceiverSsrc,
kSenderSsrc,
- kCName);
+ kReceiverSsrc,
+ kCName,
+ AUDIO_EVENT);
- receiver_transport.SetRtcpReceiver(&rtcp_sender);
- sender_transport.SetRtcpReceiver(&rtcp_receiver);
+ receiver_to_sender_.set_rtcp_receiver(&rtcp_sender);
+ sender_to_receiver_.set_rtcp_receiver(&rtcp_receiver);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- rtcp_sender.SendRtcpFromRtpSender(NULL);
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 0);
+ RunTasks(33);
base::TimeDelta rtt;
base::TimeDelta avg_rtt;
base::TimeDelta min_rtt;
base::TimeDelta max_rtt;
- EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
- receiver_transport.SetShortDelay();
- sender_transport.SetShortDelay();
- receiver_transport.SetDropPackets(true);
+ receiver_to_sender_.set_short_delay();
+ sender_to_receiver_.set_short_delay();
+ receiver_to_sender_.set_drop_packets(true);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, NULL);
- rtcp_sender.SendRtcpFromRtpSender(NULL);
+ rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 1);
+ RunTasks(33);
- EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(kAddedDelay + kAddedShortDelay, rtt.InMilliseconds(), 1);
+ EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
+ EXPECT_NEAR(kAddedDelay + kAddedShortDelay, rtt.InMilliseconds(), 2);
}
TEST_F(RtcpTest, NtpAndTime) {
- const int64 kSecondsbetweenYear1900and2010 = GG_INT64_C(40176 * 24 * 60 * 60);
- const int64 kSecondsbetweenYear1900and2030 = GG_INT64_C(47481 * 24 * 60 * 60);
+ const int64 kSecondsbetweenYear1900and2010 = INT64_C(40176 * 24 * 60 * 60);
+ const int64 kSecondsbetweenYear1900and2030 = INT64_C(47481 * 24 * 60 * 60);
uint32 ntp_seconds_1 = 0;
- uint32 ntp_fractions_1 = 0;
+ uint32 ntp_fraction_1 = 0;
base::TimeTicks input_time = base::TimeTicks::Now();
- ConvertTimeTicksToNtp(input_time, &ntp_seconds_1, &ntp_fractions_1);
+ ConvertTimeTicksToNtp(input_time, &ntp_seconds_1, &ntp_fraction_1);
// Verify absolute value.
EXPECT_GT(ntp_seconds_1, kSecondsbetweenYear1900and2010);
EXPECT_LT(ntp_seconds_1, kSecondsbetweenYear1900and2030);
- base::TimeTicks out_1 = ConvertNtpToTimeTicks(ntp_seconds_1, ntp_fractions_1);
+ base::TimeTicks out_1 = ConvertNtpToTimeTicks(ntp_seconds_1, ntp_fraction_1);
EXPECT_EQ(input_time, out_1); // Verify inverse.
base::TimeDelta time_delta = base::TimeDelta::FromMilliseconds(1000);
input_time += time_delta;
uint32 ntp_seconds_2 = 0;
- uint32 ntp_fractions_2 = 0;
+ uint32 ntp_fraction_2 = 0;
- ConvertTimeTicksToNtp(input_time, &ntp_seconds_2, &ntp_fractions_2);
- base::TimeTicks out_2 = ConvertNtpToTimeTicks(ntp_seconds_2, ntp_fractions_2);
+ ConvertTimeTicksToNtp(input_time, &ntp_seconds_2, &ntp_fraction_2);
+ base::TimeTicks out_2 = ConvertNtpToTimeTicks(ntp_seconds_2, ntp_fraction_2);
EXPECT_EQ(input_time, out_2); // Verify inverse.
// Verify delta.
EXPECT_EQ((out_2 - out_1), time_delta);
- EXPECT_EQ((ntp_seconds_2 - ntp_seconds_1), GG_UINT32_C(1));
- EXPECT_NEAR(ntp_fractions_2, ntp_fractions_1, 1);
+ EXPECT_EQ((ntp_seconds_2 - ntp_seconds_1), UINT32_C(1));
+ EXPECT_NEAR(ntp_fraction_2, ntp_fraction_1, 1);
time_delta = base::TimeDelta::FromMilliseconds(500);
input_time += time_delta;
uint32 ntp_seconds_3 = 0;
- uint32 ntp_fractions_3 = 0;
+ uint32 ntp_fraction_3 = 0;
- ConvertTimeTicksToNtp(input_time, &ntp_seconds_3, &ntp_fractions_3);
- base::TimeTicks out_3 = ConvertNtpToTimeTicks(ntp_seconds_3, ntp_fractions_3);
+ ConvertTimeTicksToNtp(input_time, &ntp_seconds_3, &ntp_fraction_3);
+ base::TimeTicks out_3 = ConvertNtpToTimeTicks(ntp_seconds_3, ntp_fraction_3);
EXPECT_EQ(input_time, out_3); // Verify inverse.
// Verify delta.
EXPECT_EQ((out_3 - out_2), time_delta);
- EXPECT_NEAR((ntp_fractions_3 - ntp_fractions_2), 0xffffffff / 2, 1);
-}
-
-TEST_F(RtcpTest, WrapAround) {
- RtcpPeer rtcp_peer(cast_environment_,
- &mock_sender_feedback_,
- NULL,
- NULL,
- NULL,
- kRtcpReducedSize,
- base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
- kReceiverSsrc,
- kSenderSsrc,
- kCName);
- uint32 new_timestamp = 0;
- uint32 old_timestamp = 0;
- EXPECT_EQ(0, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
- new_timestamp = 1234567890;
- old_timestamp = 1234567000;
- EXPECT_EQ(0, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
- new_timestamp = 1234567000;
- old_timestamp = 1234567890;
- EXPECT_EQ(0, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
- new_timestamp = 123;
- old_timestamp = 4234567890u;
- EXPECT_EQ(1, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
- new_timestamp = 4234567890u;
- old_timestamp = 123;
- EXPECT_EQ(-1, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
-}
-
-TEST_F(RtcpTest, RtpTimestampInSenderTime) {
- RtcpPeer rtcp_peer(cast_environment_,
- &mock_sender_feedback_,
- NULL,
- NULL,
- NULL,
- kRtcpReducedSize,
- base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
- kReceiverSsrc,
- kSenderSsrc,
- kCName);
- int frequency = 32000;
- uint32 rtp_timestamp = 64000;
- base::TimeTicks rtp_timestamp_in_ticks;
-
- // Test fail before we get a OnReceivedLipSyncInfo.
- EXPECT_FALSE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
-
- uint32 ntp_seconds = 0;
- uint32 ntp_fractions = 0;
- uint64 input_time_us = 12345678901000LL;
- base::TimeTicks input_time;
- input_time += base::TimeDelta::FromMicroseconds(input_time_us);
-
- // Test exact match.
- ConvertTimeTicksToNtp(input_time, &ntp_seconds, &ntp_fractions);
- rtcp_peer.OnReceivedLipSyncInfo(rtp_timestamp, ntp_seconds, ntp_fractions);
- EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
- EXPECT_EQ(input_time, rtp_timestamp_in_ticks);
-
- // Test older rtp_timestamp.
- rtp_timestamp = 32000;
- EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
- EXPECT_EQ(input_time - base::TimeDelta::FromMilliseconds(1000),
- rtp_timestamp_in_ticks);
-
- // Test older rtp_timestamp with wrap.
- rtp_timestamp = 4294903296u;
- EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
- EXPECT_EQ(input_time - base::TimeDelta::FromMilliseconds(4000),
- rtp_timestamp_in_ticks);
-
- // Test newer rtp_timestamp.
- rtp_timestamp = 128000;
- EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
- EXPECT_EQ(input_time + base::TimeDelta::FromMilliseconds(2000),
- rtp_timestamp_in_ticks);
-
- // Test newer rtp_timestamp with wrap.
- rtp_timestamp = 4294903296u;
- rtcp_peer.OnReceivedLipSyncInfo(rtp_timestamp, ntp_seconds, ntp_fractions);
- rtp_timestamp = 64000;
- EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
- EXPECT_EQ(input_time + base::TimeDelta::FromMilliseconds(4000),
- rtp_timestamp_in_ticks);
+ EXPECT_NEAR((ntp_fraction_3 - ntp_fraction_2), 0xffffffff / 2, 1);
}
} // namespace cast
diff --git a/chromium/media/cast/rtcp/rtcp_utility.cc b/chromium/media/cast/rtcp/rtcp_utility.cc
index daeaa8aaceb..e29f82e9cf9 100644
--- a/chromium/media/cast/rtcp/rtcp_utility.cc
+++ b/chromium/media/cast/rtcp/rtcp_utility.cc
@@ -4,8 +4,9 @@
#include "media/cast/rtcp/rtcp_utility.h"
+#include "base/big_endian.h"
#include "base/logging.h"
-#include "net/base/big_endian.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace media {
namespace cast {
@@ -19,18 +20,15 @@ RtcpParser::RtcpParser(const uint8* rtcpData, size_t rtcpDataLength)
state_(kStateTopLevel),
number_of_blocks_(0),
field_type_(kRtcpNotValidCode) {
+ memset(&field_, 0, sizeof(field_));
Validate();
}
RtcpParser::~RtcpParser() {}
-RtcpFieldTypes RtcpParser::FieldType() const {
- return field_type_;
-}
+RtcpFieldTypes RtcpParser::FieldType() const { return field_type_; }
-const RtcpField& RtcpParser::Field() const {
- return field_;
-}
+const RtcpField& RtcpParser::Field() const { return field_; }
RtcpFieldTypes RtcpParser::Begin() {
rtcp_data_ = rtcp_data_begin_;
@@ -41,7 +39,8 @@ RtcpFieldTypes RtcpParser::Iterate() {
// Reset packet type
field_type_ = kRtcpNotValidCode;
- if (!IsValid()) return kRtcpNotValidCode;
+ if (!IsValid())
+ return kRtcpNotValidCode;
switch (state_) {
case kStateTopLevel:
@@ -62,9 +61,6 @@ RtcpFieldTypes RtcpParser::Iterate() {
case kStateApplicationSpecificCastReceiverEventLog:
IterateCastReceiverLogEvent();
break;
- case kStateApplicationSpecificCastSenderLog:
- IterateCastSenderLog();
- break;
case kStateExtendedReportBlock:
IterateExtendedReportItem();
break;
@@ -101,51 +97,53 @@ void RtcpParser::IterateTopLevel() {
RtcpCommonHeader header;
bool success = RtcpParseCommonHeader(rtcp_data_, rtcp_data_end_, &header);
- if (!success) return;
+ if (!success)
+ return;
rtcp_block_end_ = rtcp_data_ + header.length_in_octets;
- if (rtcp_block_end_ > rtcp_data_end_) return; // Bad block!
+ if (rtcp_block_end_ > rtcp_data_end_)
+ return; // Bad block!
switch (header.PT) {
- case kPacketTypeSenderReport:
+ case transport::kPacketTypeSenderReport:
// number of Report blocks
number_of_blocks_ = header.IC;
ParseSR();
return;
- case kPacketTypeReceiverReport:
+ case transport::kPacketTypeReceiverReport:
// number of Report blocks
number_of_blocks_ = header.IC;
ParseRR();
return;
- case kPacketTypeSdes:
+ case transport::kPacketTypeSdes:
// number of Sdes blocks
number_of_blocks_ = header.IC;
if (!ParseSdes()) {
break; // Nothing supported found, continue to next block!
}
return;
- case kPacketTypeBye:
+ case transport::kPacketTypeBye:
number_of_blocks_ = header.IC;
if (!ParseBye()) {
// Nothing supported found, continue to next block!
break;
}
return;
- case kPacketTypeApplicationDefined:
+ case transport::kPacketTypeApplicationDefined:
if (!ParseApplicationDefined(header.IC)) {
// Nothing supported found, continue to next block!
break;
}
return;
- case kPacketTypeGenericRtpFeedback: // Fall through!
- case kPacketTypePayloadSpecific:
+ case transport::kPacketTypeGenericRtpFeedback: // Fall through!
+ case transport::kPacketTypePayloadSpecific:
if (!ParseFeedBackCommon(header)) {
// Nothing supported found, continue to next block!
break;
}
return;
- case kPacketTypeXr:
+ case transport::kPacketTypeXr:
if (!ParseExtendedReport()) {
break; // Nothing supported found, continue to next block!
}
@@ -160,103 +158,111 @@ void RtcpParser::IterateTopLevel() {
void RtcpParser::IterateReportBlockItem() {
bool success = ParseReportBlockItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateSdesItem() {
bool success = ParseSdesItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateByeItem() {
bool success = ParseByeItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateExtendedReportItem() {
bool success = ParseExtendedReportItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateExtendedReportDelaySinceLastReceiverReportItem() {
bool success = ParseExtendedReportDelaySinceLastReceiverReport();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateNackItem() {
bool success = ParseNackItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateRpsiItem() {
bool success = ParseRpsiItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateFirItem() {
bool success = ParseFirItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IteratePayloadSpecificAppItem() {
bool success = ParsePayloadSpecificAppItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IteratePayloadSpecificRembItem() {
bool success = ParsePayloadSpecificRembItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IteratePayloadSpecificCastItem() {
bool success = ParsePayloadSpecificCastItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IteratePayloadSpecificCastNackItem() {
bool success = ParsePayloadSpecificCastNackItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateCastReceiverLogFrame() {
bool success = ParseCastReceiverLogFrameItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::IterateCastReceiverLogEvent() {
bool success = ParseCastReceiverLogEventItem();
- if (!success) Iterate();
-}
-
-void RtcpParser::IterateCastSenderLog() {
- bool success = ParseCastSenderLogItem();
- if (!success) Iterate();
+ if (!success)
+ Iterate();
}
void RtcpParser::Validate() {
- if (rtcp_data_ == NULL) return; // NOT VALID
+ if (rtcp_data_ == NULL)
+ return; // NOT VALID
RtcpCommonHeader header;
- bool success = RtcpParseCommonHeader(rtcp_data_begin_, rtcp_data_end_,
- &header);
+ bool success =
+ RtcpParseCommonHeader(rtcp_data_begin_, rtcp_data_end_, &header);
- if (!success) return; // NOT VALID!
+ if (!success)
+ return; // NOT VALID!
valid_packet_ = true;
}
-bool RtcpParser::IsValid() const {
- return valid_packet_;
-}
+bool RtcpParser::IsValid() const { return valid_packet_; }
-void RtcpParser::EndCurrentBlock() {
- rtcp_data_ = rtcp_block_end_;
-}
+void RtcpParser::EndCurrentBlock() { rtcp_data_ = rtcp_block_end_; }
bool RtcpParser::RtcpParseCommonHeader(const uint8* data_begin,
const uint8* data_end,
RtcpCommonHeader* parsed_header) const {
- if (!data_begin || !data_end) return false;
+ if (!data_begin || !data_end)
+ return false;
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -266,31 +272,36 @@ bool RtcpParser::RtcpParseCommonHeader(const uint8* data_begin,
//
// Common header for all Rtcp packets, 4 octets.
- if ((data_end - data_begin) < 4) return false;
+ if ((data_end - data_begin) < 4)
+ return false;
- parsed_header->V = data_begin[0] >> 6;
- parsed_header->P = ((data_begin[0] & 0x20) == 0) ? false : true;
+ parsed_header->V = data_begin[0] >> 6;
+ parsed_header->P = ((data_begin[0] & 0x20) == 0) ? false : true;
parsed_header->IC = data_begin[0] & 0x1f;
parsed_header->PT = data_begin[1];
parsed_header->length_in_octets =
((data_begin[2] << 8) + data_begin[3] + 1) * 4;
- if (parsed_header->length_in_octets == 0) return false;
+ if (parsed_header->length_in_octets == 0)
+ return false;
// Check if RTP version field == 2.
- if (parsed_header->V != 2) return false;
+ if (parsed_header->V != 2)
+ return false;
return true;
}
bool RtcpParser::ParseRR() {
ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 8) return false;
+ if (length < 8)
+ return false;
field_type_ = kRtcpRrCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.Skip(4); // Skip header
big_endian_reader.ReadU32(&field_.receiver_report.sender_ssrc);
field_.receiver_report.number_of_report_blocks = number_of_blocks_;
@@ -309,7 +320,8 @@ bool RtcpParser::ParseSR() {
}
field_type_ = kRtcpSrCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.Skip(4); // Skip header
big_endian_reader.ReadU32(&field_.sender_report.sender_ssrc);
big_endian_reader.ReadU32(&field_.sender_report.ntp_most_significant);
@@ -339,7 +351,8 @@ bool RtcpParser::ParseReportBlockItem() {
return false;
}
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&field_.report_block_item.ssrc);
big_endian_reader.ReadU8(&field_.report_block_item.fraction_lost);
@@ -399,7 +412,8 @@ bool RtcpParser::ParseSdesItem() {
}
uint32 ssrc;
- net::BigEndianReader big_endian_reader(rtcp_data_, data_length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), data_length);
big_endian_reader.ReadU32(&ssrc);
rtcp_data_ += 4;
@@ -418,7 +432,8 @@ bool RtcpParser::ParseSdesTypes() {
// Only the c_name item is mandatory. RFC 3550 page 46.
bool found_c_name = false;
ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
while (big_endian_reader.remaining() > 0) {
uint8 tag;
@@ -484,7 +499,8 @@ bool RtcpParser::ParseByeItem() {
field_type_ = kRtcpByeCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&field_.bye.sender_ssrc);
rtcp_data_ += 4;
@@ -498,8 +514,7 @@ bool RtcpParser::ParseByeItem() {
bool RtcpParser::ParseApplicationDefined(uint8 subtype) {
ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 16 ||
- !(subtype == kSenderLogSubtype || subtype == kReceiverLogSubtype)) {
+ if (length < 16 || subtype != kReceiverLogSubtype) {
state_ = kStateTopLevel;
EndCurrentBlock();
return false;
@@ -508,7 +523,8 @@ bool RtcpParser::ParseApplicationDefined(uint8 subtype) {
uint32 sender_ssrc;
uint32 name;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.Skip(4); // Skip header.
big_endian_reader.ReadU32(&sender_ssrc);
big_endian_reader.ReadU32(&name);
@@ -520,11 +536,6 @@ bool RtcpParser::ParseApplicationDefined(uint8 subtype) {
}
rtcp_data_ += 12;
switch (subtype) {
- case kSenderLogSubtype:
- state_ = kStateApplicationSpecificCastSenderLog;
- field_type_ = kRtcpApplicationSpecificCastSenderLogCode;
- field_.cast_sender_log.sender_ssrc = sender_ssrc;
- break;
case kReceiverLogSubtype:
state_ = kStateApplicationSpecificCastReceiverFrameLog;
field_type_ = kRtcpApplicationSpecificCastReceiverLogCode;
@@ -545,7 +556,8 @@ bool RtcpParser::ParseCastReceiverLogFrameItem() {
}
uint32 rtp_timestamp;
uint32 data;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&rtp_timestamp);
big_endian_reader.ReadU32(&data);
@@ -577,7 +589,8 @@ bool RtcpParser::ParseCastReceiverLogEventItem() {
uint16 delay_delta_or_packet_id;
uint16 event_type_and_timestamp_delta;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU16(&delay_delta_or_packet_id);
big_endian_reader.ReadU16(&event_type_and_timestamp_delta);
@@ -585,7 +598,9 @@ bool RtcpParser::ParseCastReceiverLogEventItem() {
field_.cast_receiver_log.event =
static_cast<uint8>(event_type_and_timestamp_delta >> 12);
- field_.cast_receiver_log.delay_delta_or_packet_id = delay_delta_or_packet_id;
+ // delay_delta is in union'ed with packet_id.
+ field_.cast_receiver_log.delay_delta_or_packet_id.packet_id =
+ delay_delta_or_packet_id;
field_.cast_receiver_log.event_timestamp_delta =
event_type_and_timestamp_delta & 0xfff;
@@ -593,30 +608,10 @@ bool RtcpParser::ParseCastReceiverLogEventItem() {
return true;
}
-bool RtcpParser::ParseCastSenderLogItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
-
- if (length < 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- uint32 data;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
- big_endian_reader.ReadU32(&data);
-
- rtcp_data_ += 4;
-
- field_.cast_sender_log.status = static_cast<uint8>(data >> 24);
- // We have 24 LSB of the RTP timestamp on the wire.
- field_.cast_sender_log.rtp_timestamp = data & 0xffffff;
- field_type_ = kRtcpApplicationSpecificCastSenderLogCode;
- return true;
-}
-
bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
- DCHECK((header.PT == kPacketTypeGenericRtpFeedback) ||
- (header.PT == kPacketTypePayloadSpecific)) << "Invalid state";
+ DCHECK((header.PT == transport::kPacketTypeGenericRtpFeedback) ||
+ (header.PT == transport::kPacketTypePayloadSpecific))
+ << "Invalid state";
ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
@@ -627,21 +622,22 @@ bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
uint32 sender_ssrc;
uint32 media_ssrc;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.Skip(4); // Skip header.
big_endian_reader.ReadU32(&sender_ssrc);
big_endian_reader.ReadU32(&media_ssrc);
rtcp_data_ += 12;
- if (header.PT == kPacketTypeGenericRtpFeedback) {
+ if (header.PT == transport::kPacketTypeGenericRtpFeedback) {
// Transport layer feedback
switch (header.IC) {
case 1:
// Nack
field_type_ = kRtcpGenericRtpFeedbackNackCode;
field_.nack.sender_ssrc = sender_ssrc;
- field_.nack.media_ssrc = media_ssrc;
+ field_.nack.media_ssrc = media_ssrc;
state_ = kStateGenericRtpFeedbackNack;
return true;
case 2:
@@ -667,14 +663,14 @@ bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
EndCurrentBlock();
return false;
- } else if (header.PT == kPacketTypePayloadSpecific) {
+ } else if (header.PT == transport::kPacketTypePayloadSpecific) {
// Payload specific feedback
switch (header.IC) {
case 1:
// PLI
field_type_ = kRtcpPayloadSpecificPliCode;
field_.pli.sender_ssrc = sender_ssrc;
- field_.pli.media_ssrc = media_ssrc;
+ field_.pli.media_ssrc = media_ssrc;
// Note: No state transition, PLI FCI is empty!
return true;
@@ -684,7 +680,7 @@ bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
case 3:
field_type_ = kRtcpPayloadSpecificRpsiCode;
field_.rpsi.sender_ssrc = sender_ssrc;
- field_.rpsi.media_ssrc = media_ssrc;
+ field_.rpsi.media_ssrc = media_ssrc;
state_ = kStatePayloadSpecificRpsi;
return true;
case 4:
@@ -693,7 +689,7 @@ bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
case 15:
field_type_ = kRtcpPayloadSpecificAppCode;
field_.application_specific.sender_ssrc = sender_ssrc;
- field_.application_specific.media_ssrc = media_ssrc;
+ field_.application_specific.media_ssrc = media_ssrc;
state_ = kStatePayloadSpecificApplication;
return true;
default:
@@ -736,7 +732,8 @@ bool RtcpParser::ParseRpsiItem() {
field_type_ = kRtcpPayloadSpecificRpsiCode;
uint8 padding_bits;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU8(&padding_bits);
big_endian_reader.ReadU8(&field_.rpsi.payload_type);
big_endian_reader.ReadBytes(&field_.rpsi.native_bit_string, length - 2);
@@ -759,7 +756,8 @@ bool RtcpParser::ParseNackItem() {
field_type_ = kRtcpGenericRtpFeedbackNackItemCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU16(&field_.nack_item.packet_id);
big_endian_reader.ReadU16(&field_.nack_item.bitmask);
rtcp_data_ += 4;
@@ -775,7 +773,8 @@ bool RtcpParser::ParsePayloadSpecificAppItem() {
return false;
}
uint32 name;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&name);
rtcp_data_ += 4;
@@ -802,7 +801,8 @@ bool RtcpParser::ParsePayloadSpecificRembItem() {
return false;
}
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU8(&field_.remb_item.number_of_ssrcs);
uint8 byte_1;
@@ -841,9 +841,11 @@ bool RtcpParser::ParsePayloadSpecificCastItem() {
}
field_type_ = kRtcpPayloadSpecificCastCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU8(&field_.cast_item.last_frame_id);
big_endian_reader.ReadU8(&field_.cast_item.number_of_lost_fields);
+ big_endian_reader.ReadU16(&field_.cast_item.target_delay_ms);
rtcp_data_ += 4;
@@ -867,7 +869,8 @@ bool RtcpParser::ParsePayloadSpecificCastNackItem() {
}
field_type_ = kRtcpPayloadSpecificCastNackItemCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU8(&field_.cast_nack_item.frame_id);
big_endian_reader.ReadU16(&field_.cast_nack_item.packet_id);
big_endian_reader.ReadU8(&field_.cast_nack_item.bitmask);
@@ -887,7 +890,8 @@ bool RtcpParser::ParseFirItem() {
}
field_type_ = kRtcpPayloadSpecificFirItemCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&field_.fir_item.ssrc);
big_endian_reader.ReadU8(&field_.fir_item.command_sequence_number);
@@ -897,11 +901,13 @@ bool RtcpParser::ParseFirItem() {
bool RtcpParser::ParseExtendedReport() {
ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 8) return false;
+ if (length < 8)
+ return false;
field_type_ = kRtcpXrCode;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.Skip(4); // Skip header.
big_endian_reader.ReadU32(&field_.extended_report.sender_ssrc);
@@ -921,7 +927,8 @@ bool RtcpParser::ParseExtendedReportItem() {
uint8 block_type;
uint16 block_length;
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU8(&block_type);
big_endian_reader.Skip(1); // Ignore reserved.
big_endian_reader.ReadU16(&block_length);
@@ -970,7 +977,8 @@ bool RtcpParser::ParseExtendedReportReceiverReferenceTimeReport() {
return false;
}
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&field_.rrtr.ntp_most_significant);
big_endian_reader.ReadU32(&field_.rrtr.ntp_least_significant);
@@ -993,7 +1001,8 @@ bool RtcpParser::ParseExtendedReportDelaySinceLastReceiverReport() {
return false;
}
- net::BigEndianReader big_endian_reader(rtcp_data_, length);
+ base::BigEndianReader big_endian_reader(
+ reinterpret_cast<const char*>(rtcp_data_), length);
big_endian_reader.ReadU32(&field_.dlrr.receivers_ssrc);
big_endian_reader.ReadU32(&field_.dlrr.last_receiver_report);
big_endian_reader.ReadU32(&field_.dlrr.delay_last_receiver_report);
@@ -1005,5 +1014,53 @@ bool RtcpParser::ParseExtendedReportDelaySinceLastReceiverReport() {
return true;
}
+// Converts a log event type to an integer value.
+// NOTE: We have only allocated 4 bits to represent the type of event over the
+// wire. Therefore, this function can only return values from 0 to 15.
+uint8 ConvertEventTypeToWireFormat(CastLoggingEvent event) {
+ switch (event) {
+ case FRAME_ACK_SENT:
+ return 11;
+ case FRAME_PLAYOUT:
+ return 12;
+ case FRAME_DECODED:
+ return 13;
+ case PACKET_RECEIVED:
+ return 14;
+ default:
+ return 0; // Not an interesting event.
+ }
+}
+
+CastLoggingEvent TranslateToLogEventFromWireFormat(uint8 event) {
+ // TODO(imcheng): Remove the old mappings once they are no longer used.
+ switch (event) {
+ case 1: // AudioAckSent
+ case 5: // VideoAckSent
+ case 11: // Unified
+ return FRAME_ACK_SENT;
+ case 2: // AudioPlayoutDelay
+ case 7: // VideoRenderDelay
+ case 12: // Unified
+ return FRAME_PLAYOUT;
+ case 3: // AudioFrameDecoded
+ case 6: // VideoFrameDecoded
+ case 13: // Unified
+ return FRAME_DECODED;
+ case 4: // AudioPacketReceived
+ case 8: // VideoPacketReceived
+ case 14: // Unified
+ return PACKET_RECEIVED;
+ case 9: // DuplicateAudioPacketReceived
+ case 10: // DuplicateVideoPacketReceived
+ default:
+ // If the sender adds new log messages we will end up here until we add
+ // the new messages in the receiver.
+ VLOG(1) << "Unexpected log message received: " << static_cast<int>(event);
+ NOTREACHED();
+ return UNKNOWN;
+ }
+}
+
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/rtcp_utility.h b/chromium/media/cast/rtcp/rtcp_utility.h
index 5cf55d91060..34f3f25a889 100644
--- a/chromium/media/cast/rtcp/rtcp_utility.h
+++ b/chromium/media/cast/rtcp/rtcp_utility.h
@@ -7,6 +7,7 @@
#include "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
+#include "media/cast/logging/logging_defines.h"
#include "media/cast/rtcp/rtcp_defines.h"
namespace media {
@@ -21,7 +22,6 @@ static const int kRtcpMaxNumberOfRembFeedbackSsrcs = 255;
static const uint32 kRemb = ('R' << 24) + ('E' << 16) + ('M' << 8) + 'B';
static const uint32 kCast = ('C' << 24) + ('A' << 16) + ('S' << 8) + 'T';
-static const uint8 kSenderLogSubtype = 1;
static const uint8 kReceiverLogSubtype = 2;
static const size_t kRtcpMaxReceiverLogMessages = 256;
@@ -37,7 +37,7 @@ struct RtcpFieldReceiverReport {
struct RtcpFieldSenderReport {
// RFC 3550.
uint32 sender_ssrc;
- uint8 number_of_report_blocks;
+ uint8 number_of_report_blocks;
uint32 ntp_most_significant;
uint32 ntp_least_significant;
uint32 rtp_timestamp;
@@ -48,7 +48,7 @@ struct RtcpFieldSenderReport {
struct RtcpFieldReportBlockItem {
// RFC 3550.
uint32 ssrc;
- uint8 fraction_lost;
+ uint8 fraction_lost;
uint32 cumulative_number_of_packets_lost;
uint32 extended_highest_sequence_number;
uint32 jitter;
@@ -101,9 +101,9 @@ struct RtcpFieldPayloadSpecificRpsi {
// RFC 4585.
uint32 sender_ssrc;
uint32 media_ssrc;
- uint8 payload_type;
+ uint8 payload_type;
uint16 number_of_valid_bits;
- uint8 native_bit_string[kRtcpRpsiDataSize];
+ uint8 native_bit_string[kRtcpRpsiDataSize];
};
struct RtcpFieldXr {
@@ -138,6 +138,7 @@ struct RtcpFieldPayloadSpecificRembItem {
struct RtcpFieldPayloadSpecificCastItem {
uint8 last_frame_id;
uint8 number_of_lost_fields;
+ uint16 target_delay_ms;
};
struct RtcpFieldPayloadSpecificCastNackItem {
@@ -151,41 +152,37 @@ struct RtcpFieldApplicationSpecificCastReceiverLogItem {
uint32 rtp_timestamp;
uint32 event_timestamp_base;
uint8 event;
- uint16 delay_delta_or_packet_id;
+ union {
+ uint16 packet_id;
+ int16 delay_delta;
+ } delay_delta_or_packet_id;
uint16 event_timestamp_delta;
};
-struct RtcpFieldApplicationSpecificCastSenderLogItem {
- uint32 sender_ssrc;
- uint8 status;
- uint32 rtp_timestamp;
-};
-
union RtcpField {
- RtcpFieldReceiverReport receiver_report;
- RtcpFieldSenderReport sender_report;
- RtcpFieldReportBlockItem report_block_item;
- RtcpFieldSdesCName c_name;
- RtcpFieldBye bye;
-
- RtcpFieldXr extended_report;
- RtcpFieldXrRrtr rrtr;
- RtcpFieldXrDlrr dlrr;
-
- RtcpFieldGenericRtpFeedbackNack nack;
- RtcpFieldGenericRtpFeedbackNackItem nack_item;
-
- RtcpFieldPayloadSpecificPli pli;
- RtcpFieldPayloadSpecificRpsi rpsi;
- RtcpFieldPayloadSpecificFir fir;
- RtcpFieldPayloadSpecificFirItem fir_item;
- RtcpFieldPayloadSpecificApplication application_specific;
- RtcpFieldPayloadSpecificRembItem remb_item;
- RtcpFieldPayloadSpecificCastItem cast_item;
- RtcpFieldPayloadSpecificCastNackItem cast_nack_item;
+ RtcpFieldReceiverReport receiver_report;
+ RtcpFieldSenderReport sender_report;
+ RtcpFieldReportBlockItem report_block_item;
+ RtcpFieldSdesCName c_name;
+ RtcpFieldBye bye;
+
+ RtcpFieldXr extended_report;
+ RtcpFieldXrRrtr rrtr;
+ RtcpFieldXrDlrr dlrr;
+
+ RtcpFieldGenericRtpFeedbackNack nack;
+ RtcpFieldGenericRtpFeedbackNackItem nack_item;
+
+ RtcpFieldPayloadSpecificPli pli;
+ RtcpFieldPayloadSpecificRpsi rpsi;
+ RtcpFieldPayloadSpecificFir fir;
+ RtcpFieldPayloadSpecificFirItem fir_item;
+ RtcpFieldPayloadSpecificApplication application_specific;
+ RtcpFieldPayloadSpecificRembItem remb_item;
+ RtcpFieldPayloadSpecificCastItem cast_item;
+ RtcpFieldPayloadSpecificCastNackItem cast_nack_item;
RtcpFieldApplicationSpecificCastReceiverLogItem cast_receiver_log;
- RtcpFieldApplicationSpecificCastSenderLogItem cast_sender_log;
};
enum RtcpFieldTypes {
@@ -195,7 +192,6 @@ enum RtcpFieldTypes {
kRtcpRrCode,
kRtcpSrCode,
kRtcpReportBlockItemCode,
-
kRtcpSdesCode,
kRtcpSdesChunkCode,
kRtcpByeCode,
@@ -209,7 +205,6 @@ enum RtcpFieldTypes {
// RFC 4585.
kRtcpGenericRtpFeedbackNackCode,
kRtcpGenericRtpFeedbackNackItemCode,
-
kRtcpPayloadSpecificPliCode,
kRtcpPayloadSpecificRpsiCode,
kRtcpPayloadSpecificAppCode,
@@ -222,7 +217,6 @@ enum RtcpFieldTypes {
kRtcpApplicationSpecificCastReceiverLogCode,
kRtcpApplicationSpecificCastReceiverLogFrameCode,
kRtcpApplicationSpecificCastReceiverLogEventCode,
- kRtcpApplicationSpecificCastSenderLogCode,
// RFC 5104.
kRtcpPayloadSpecificFirCode,
@@ -233,27 +227,13 @@ enum RtcpFieldTypes {
};
struct RtcpCommonHeader {
- uint8 V; // Version.
- bool P; // Padding.
- uint8 IC; // Item count / subtype.
- uint8 PT; // Packet Type.
+ uint8 V; // Version.
+ bool P; // Padding.
+ uint8 IC; // Item count / subtype.
+ uint8 PT; // Packet Type.
uint16 length_in_octets;
};
-enum RtcpPacketTypes {
- kPacketTypeLow = 194, // SMPTE time-code mapping.
- kPacketTypeInterArrivalJitterReport = 195,
- kPacketTypeSenderReport = 200,
- kPacketTypeReceiverReport = 201,
- kPacketTypeSdes = 202,
- kPacketTypeBye = 203,
- kPacketTypeApplicationDefined = 204,
- kPacketTypeGenericRtpFeedback = 205,
- kPacketTypePayloadSpecific = 206,
- kPacketTypeXr = 207,
- kPacketTypeHigh = 210, // Port Mapping.
-};
-
class RtcpParser {
public:
RtcpParser(const uint8* rtcp_data, size_t rtcp_length);
@@ -275,7 +255,6 @@ class RtcpParser {
kStateBye,
kStateApplicationSpecificCastReceiverFrameLog,
kStateApplicationSpecificCastReceiverEventLog,
- kStateApplicationSpecificCastSenderLog,
kStateExtendedReportBlock,
kStateExtendedReportDelaySinceLastReceiverReport,
kStateGenericRtpFeedbackNack,
@@ -297,7 +276,6 @@ class RtcpParser {
void IterateByeItem();
void IterateCastReceiverLogFrame();
void IterateCastReceiverLogEvent();
- void IterateCastSenderLog();
void IterateExtendedReportItem();
void IterateExtendedReportDelaySinceLastReceiverReportItem();
void IterateNackItem();
@@ -323,7 +301,6 @@ class RtcpParser {
bool ParseApplicationDefined(uint8 subtype);
bool ParseCastReceiverLogFrameItem();
bool ParseCastReceiverLogEventItem();
- bool ParseCastSenderLogItem();
bool ParseExtendedReport();
bool ParseExtendedReportItem();
@@ -355,6 +332,14 @@ class RtcpParser {
DISALLOW_COPY_AND_ASSIGN(RtcpParser);
};
+// Converts a log event type to an integer value.
+// NOTE: We have only allocated 4 bits to represent the type of event over the
+// wire. Therefore, this function can only return values from 0 to 15.
+uint8 ConvertEventTypeToWireFormat(CastLoggingEvent event);
+
+// The inverse of |ConvertEventTypeToWireFormat()|.
+CastLoggingEvent TranslateToLogEventFromWireFormat(uint8 event);
+
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtcp/test_rtcp_packet_builder.cc b/chromium/media/cast/rtcp/test_rtcp_packet_builder.cc
index f4117f53dec..8d0809d928e 100644
--- a/chromium/media/cast/rtcp/test_rtcp_packet_builder.cc
+++ b/chromium/media/cast/rtcp/test_rtcp_packet_builder.cc
@@ -5,14 +5,14 @@
#include "media/cast/rtcp/test_rtcp_packet_builder.h"
#include "base/logging.h"
+#include "media/cast/rtcp/rtcp_utility.h"
namespace media {
namespace cast {
TestRtcpPacketBuilder::TestRtcpPacketBuilder()
: ptr_of_length_(NULL),
- big_endian_writer_(buffer_, kIpPacketSize) {
-}
+ big_endian_writer_(reinterpret_cast<char*>(buffer_), kMaxIpPacketSize) {}
void TestRtcpPacketBuilder::AddSr(uint32 sender_ssrc,
int number_of_report_blocks) {
@@ -91,8 +91,8 @@ void TestRtcpPacketBuilder::AddXrHeader(uint32 sender_ssrc) {
}
void TestRtcpPacketBuilder::AddXrUnknownBlock() {
- big_endian_writer_.WriteU8(9); // Block type.
- big_endian_writer_.WriteU8(0); // Reserved.
+ big_endian_writer_.WriteU8(9); // Block type.
+ big_endian_writer_.WriteU8(0); // Reserved.
big_endian_writer_.WriteU16(4); // Block length.
// First receiver same as sender of this report.
big_endian_writer_.WriteU32(0);
@@ -102,8 +102,8 @@ void TestRtcpPacketBuilder::AddXrUnknownBlock() {
}
void TestRtcpPacketBuilder::AddXrDlrrBlock(uint32 sender_ssrc) {
- big_endian_writer_.WriteU8(5); // Block type.
- big_endian_writer_.WriteU8(0); // Reserved.
+ big_endian_writer_.WriteU8(5); // Block type.
+ big_endian_writer_.WriteU8(0); // Reserved.
big_endian_writer_.WriteU16(3); // Block length.
// First receiver same as sender of this report.
@@ -113,8 +113,8 @@ void TestRtcpPacketBuilder::AddXrDlrrBlock(uint32 sender_ssrc) {
}
void TestRtcpPacketBuilder::AddXrExtendedDlrrBlock(uint32 sender_ssrc) {
- big_endian_writer_.WriteU8(5); // Block type.
- big_endian_writer_.WriteU8(0); // Reserved.
+ big_endian_writer_.WriteU8(5); // Block type.
+ big_endian_writer_.WriteU8(0); // Reserved.
big_endian_writer_.WriteU16(9); // Block length.
big_endian_writer_.WriteU32(0xaaaaaaaa);
big_endian_writer_.WriteU32(0xaaaaaaaa);
@@ -130,8 +130,8 @@ void TestRtcpPacketBuilder::AddXrExtendedDlrrBlock(uint32 sender_ssrc) {
}
void TestRtcpPacketBuilder::AddXrRrtrBlock() {
- big_endian_writer_.WriteU8(4); // Block type.
- big_endian_writer_.WriteU8(0); // Reserved.
+ big_endian_writer_.WriteU8(4); // Block type.
+ big_endian_writer_.WriteU8(0); // Reserved.
big_endian_writer_.WriteU16(2); // Block length.
big_endian_writer_.WriteU32(kNtpHigh);
big_endian_writer_.WriteU32(kNtpLow);
@@ -167,8 +167,8 @@ void TestRtcpPacketBuilder::AddRpsi(uint32 sender_ssrc, uint32 media_ssrc) {
uint64 picture_id = kPictureId;
for (int i = 9; i > 0; i--) {
- big_endian_writer_.WriteU8(
- 0x80 | static_cast<uint8>(picture_id >> (i * 7)));
+ big_endian_writer_.WriteU8(0x80 |
+ static_cast<uint8>(picture_id >> (i * 7)));
}
// Add last byte of picture ID.
big_endian_writer_.WriteU8(static_cast<uint8>(picture_id & 0x7f));
@@ -189,7 +189,9 @@ void TestRtcpPacketBuilder::AddRemb(uint32 sender_ssrc, uint32 media_ssrc) {
big_endian_writer_.WriteU32(media_ssrc);
}
-void TestRtcpPacketBuilder::AddCast(uint32 sender_ssrc, uint32 media_ssrc) {
+void TestRtcpPacketBuilder::AddCast(uint32 sender_ssrc,
+ uint32 media_ssrc,
+ uint16 target_delay_ms) {
AddRtcpHeader(206, 15);
big_endian_writer_.WriteU32(sender_ssrc);
big_endian_writer_.WriteU32(media_ssrc);
@@ -198,8 +200,8 @@ void TestRtcpPacketBuilder::AddCast(uint32 sender_ssrc, uint32 media_ssrc) {
big_endian_writer_.WriteU8('S');
big_endian_writer_.WriteU8('T');
big_endian_writer_.WriteU8(kAckFrameId);
- big_endian_writer_.WriteU8(3); // Loss fields.
- big_endian_writer_.WriteU16(0); // Reserved.
+ big_endian_writer_.WriteU8(3); // Loss fields.
+ big_endian_writer_.WriteU16(target_delay_ms);
big_endian_writer_.WriteU8(kLostFrameId);
big_endian_writer_.WriteU16(kRtcpCastAllPacketsLost);
big_endian_writer_.WriteU8(0); // Lost packet id mask.
@@ -211,21 +213,6 @@ void TestRtcpPacketBuilder::AddCast(uint32 sender_ssrc, uint32 media_ssrc) {
big_endian_writer_.WriteU8(0); // Lost packet id mask.
}
-void TestRtcpPacketBuilder::AddSenderLog(uint32 sender_ssrc) {
- AddRtcpHeader(204, 1);
- big_endian_writer_.WriteU32(sender_ssrc);
- big_endian_writer_.WriteU8('C');
- big_endian_writer_.WriteU8('A');
- big_endian_writer_.WriteU8('S');
- big_endian_writer_.WriteU8('T');
-}
-
-void TestRtcpPacketBuilder::AddSenderFrameLog(uint8 event_id,
- uint32 rtp_timestamp) {
- big_endian_writer_.WriteU32(
- (static_cast<uint32>(event_id) << 24) + (rtp_timestamp & 0xffffff));
-}
-
void TestRtcpPacketBuilder::AddReceiverLog(uint32 sender_ssrc) {
AddRtcpHeader(204, 2);
big_endian_writer_.WriteU32(sender_ssrc);
@@ -236,7 +223,8 @@ void TestRtcpPacketBuilder::AddReceiverLog(uint32 sender_ssrc) {
}
void TestRtcpPacketBuilder::AddReceiverFrameLog(uint32 rtp_timestamp,
- int num_events, uint32 event_timesamp_base) {
+ int num_events,
+ uint32 event_timesamp_base) {
big_endian_writer_.WriteU32(rtp_timestamp);
big_endian_writer_.WriteU8(static_cast<uint8>(num_events - 1));
big_endian_writer_.WriteU8(static_cast<uint8>(event_timesamp_base >> 16));
@@ -245,14 +233,22 @@ void TestRtcpPacketBuilder::AddReceiverFrameLog(uint32 rtp_timestamp,
}
void TestRtcpPacketBuilder::AddReceiverEventLog(uint16 event_data,
- uint8 event_id, uint16 event_timesamp_delta) {
+ CastLoggingEvent event,
+ uint16 event_timesamp_delta) {
big_endian_writer_.WriteU16(event_data);
+ uint8 event_id = ConvertEventTypeToWireFormat(event);
uint16 type_and_delta = static_cast<uint16>(event_id) << 12;
type_and_delta += event_timesamp_delta & 0x0fff;
big_endian_writer_.WriteU16(type_and_delta);
}
-const uint8* TestRtcpPacketBuilder::Packet() {
+scoped_ptr<media::cast::Packet> TestRtcpPacketBuilder::GetPacket() {
+ PatchLengthField();
+ return scoped_ptr<media::cast::Packet>(
+ new media::cast::Packet(buffer_, buffer_ + Length()));
+}
+
+const uint8* TestRtcpPacketBuilder::Data() {
PatchLengthField();
return buffer_;
}
diff --git a/chromium/media/cast/rtcp/test_rtcp_packet_builder.h b/chromium/media/cast/rtcp/test_rtcp_packet_builder.h
index 9b63a37fa4a..d4266670aba 100644
--- a/chromium/media/cast/rtcp/test_rtcp_packet_builder.h
+++ b/chromium/media/cast/rtcp/test_rtcp_packet_builder.h
@@ -7,8 +7,9 @@
#ifndef MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
#define MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
+#include "base/big_endian.h"
+#include "media/cast/cast_config.h"
#include "media/cast/rtcp/rtcp_defines.h"
-#include "net/base/big_endian.h"
namespace media {
namespace cast {
@@ -58,7 +59,9 @@ class TestRtcpPacketBuilder {
TestRtcpPacketBuilder();
void AddSr(uint32 sender_ssrc, int number_of_report_blocks);
- void AddSrWithNtp(uint32 sender_ssrc, uint32 ntp_high, uint32 ntp_low,
+ void AddSrWithNtp(uint32 sender_ssrc,
+ uint32 ntp_high,
+ uint32 ntp_low,
uint32 rtp_timestamp);
void AddRr(uint32 sender_ssrc, int number_of_report_blocks);
void AddRb(uint32 rtp_ssrc);
@@ -76,17 +79,18 @@ class TestRtcpPacketBuilder {
void AddPli(uint32 sender_ssrc, uint32 media_ssrc);
void AddRpsi(uint32 sender_ssrc, uint32 media_ssrc);
void AddRemb(uint32 sender_ssrc, uint32 media_ssrc);
- void AddCast(uint32 sender_ssrc, uint32 media_ssrc);
- void AddSenderLog(uint32 sender_ssrc);
- void AddSenderFrameLog(uint8 event_id, uint32 rtp_timestamp);
+ void AddCast(uint32 sender_ssrc, uint32 media_ssrc, uint16 target_delay_ms);
void AddReceiverLog(uint32 sender_ssrc);
- void AddReceiverFrameLog(uint32 rtp_timestamp, int num_events,
+ void AddReceiverFrameLog(uint32 rtp_timestamp,
+ int num_events,
uint32 event_timesamp_base);
- void AddReceiverEventLog(uint16 event_data, uint8 event_id,
+ void AddReceiverEventLog(uint16 event_data,
+ CastLoggingEvent event,
uint16 event_timesamp_delta);
- const uint8* Packet();
- int Length() { return kIpPacketSize - big_endian_writer_.remaining(); }
+ scoped_ptr<Packet> GetPacket();
+ const uint8* Data();
+ int Length() { return kMaxIpPacketSize - big_endian_writer_.remaining(); }
private:
void AddRtcpHeader(int payload, int format_or_count);
@@ -94,12 +98,14 @@ class TestRtcpPacketBuilder {
// Where the length field of the current packet is.
// Note: 0 is not a legal value, it is used for "uninitialized".
- uint8 buffer_[kIpPacketSize];
+ uint8 buffer_[kMaxIpPacketSize];
char* ptr_of_length_;
- net::BigEndianWriter big_endian_writer_;
+ base::BigEndianWriter big_endian_writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRtcpPacketBuilder);
};
} // namespace cast
} // namespace media
-#endif // MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
+#endif // MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
diff --git a/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.cc b/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.cc
index 8681d087aa3..02b6c0be454 100644
--- a/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.cc
+++ b/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.cc
@@ -7,11 +7,9 @@
namespace media {
namespace cast {
-MockRtpPayloadFeedback::MockRtpPayloadFeedback() {
-}
+MockRtpPayloadFeedback::MockRtpPayloadFeedback() {}
-MockRtpPayloadFeedback::~MockRtpPayloadFeedback() {
-}
+MockRtpPayloadFeedback::~MockRtpPayloadFeedback() {}
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.h b/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.h
index 003b67bc0da..14e48673bd3 100644
--- a/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.h
+++ b/chromium/media/cast/rtp_receiver/mock_rtp_payload_feedback.h
@@ -16,8 +16,7 @@ class MockRtpPayloadFeedback : public RtpPayloadFeedback {
MockRtpPayloadFeedback();
virtual ~MockRtpPayloadFeedback();
- MOCK_METHOD1(CastFeedback,
- void(const RtcpCastMessage& cast_feedback));
+ MOCK_METHOD1(CastFeedback, void(const RtcpCastMessage& cast_feedback));
};
} // namespace cast
diff --git a/chromium/media/cast/rtp_receiver/receiver_stats.cc b/chromium/media/cast/rtp_receiver/receiver_stats.cc
index 9d34583a769..7eff86763f8 100644
--- a/chromium/media/cast/rtp_receiver/receiver_stats.cc
+++ b/chromium/media/cast/rtp_receiver/receiver_stats.cc
@@ -37,15 +37,16 @@ void ReceiverStats::GetStatistics(uint8* fraction_lost,
diff = max_sequence_number_ - interval_min_sequence_number_ + 1;
} else {
diff = kMaxSequenceNumber * (interval_wrap_count_ - 1) +
- (max_sequence_number_ - interval_min_sequence_number_ +
- kMaxSequenceNumber + 1);
+ (max_sequence_number_ - interval_min_sequence_number_ +
+ kMaxSequenceNumber + 1);
}
if (diff < 1) {
*fraction_lost = 0;
} else {
- *fraction_lost = static_cast<uint8>((256 * (1 -
- static_cast<float>(interval_number_packets_) / abs(diff))));
+ float tmp_ratio =
+ (1 - static_cast<float>(interval_number_packets_) / abs(diff));
+ *fraction_lost = static_cast<uint8>(256 * tmp_ratio);
}
}
@@ -55,16 +56,17 @@ void ReceiverStats::GetStatistics(uint8* fraction_lost,
} else if (sequence_number_cycles_ == 0) {
*cumulative_lost = expected_packets_num - total_number_packets_;
} else {
- *cumulative_lost = kMaxSequenceNumber * (sequence_number_cycles_ - 1) +
+ *cumulative_lost =
+ kMaxSequenceNumber * (sequence_number_cycles_ - 1) +
(expected_packets_num - total_number_packets_ + kMaxSequenceNumber);
}
// Extended high sequence number consists of the highest seq number and the
// number of cycles (wrap).
- *extended_high_sequence_number = (sequence_number_cycles_ << 16) +
- max_sequence_number_;
+ *extended_high_sequence_number =
+ (sequence_number_cycles_ << 16) + max_sequence_number_;
- *jitter = static_cast<uint32>(abs(jitter_.InMillisecondsRoundedUp()));
+ *jitter = static_cast<uint32>(std::abs(jitter_.InMillisecondsRoundedUp()));
// Reset interval values.
interval_min_sequence_number_ = 0;
@@ -73,7 +75,7 @@ void ReceiverStats::GetStatistics(uint8* fraction_lost,
}
void ReceiverStats::UpdateStatistics(const RtpCastHeader& header) {
- uint16 new_seq_num = header.webrtc.header.sequenceNumber;
+ const uint16 new_seq_num = header.sequence_number;
if (interval_number_packets_ == 0) {
// First packet in the interval.
@@ -97,10 +99,11 @@ void ReceiverStats::UpdateStatistics(const RtpCastHeader& header) {
// Compute Jitter.
base::TimeTicks now = clock_->NowTicks();
base::TimeDelta delta_new_timestamp =
- base::TimeDelta::FromMilliseconds(header.webrtc.header.timestamp);
+ base::TimeDelta::FromMilliseconds(header.rtp_timestamp);
if (total_number_packets_ > 0) {
// Update jitter.
- base::TimeDelta delta = (now - last_received_packet_time_) -
+ base::TimeDelta delta =
+ (now - last_received_packet_time_) -
((delta_new_timestamp - last_received_timestamp_) / 90);
jitter_ += (delta - jitter_) / 16;
}
diff --git a/chromium/media/cast/rtp_receiver/receiver_stats.h b/chromium/media/cast/rtp_receiver/receiver_stats.h
index c91ee507e0c..05a067f7870 100644
--- a/chromium/media/cast/rtp_receiver/receiver_stats.h
+++ b/chromium/media/cast/rtp_receiver/receiver_stats.h
@@ -7,20 +7,21 @@
#include "base/time/tick_clock.h"
#include "base/time/time.h"
+#include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtp_receiver/rtp_receiver_defines.h"
namespace media {
namespace cast {
-class ReceiverStats {
+class ReceiverStats : public RtpReceiverStatistics {
public:
explicit ReceiverStats(base::TickClock* clock);
- ~ReceiverStats();
+ virtual ~ReceiverStats() OVERRIDE;
- void GetStatistics(uint8* fraction_lost,
- uint32* cumulative_lost, // 24 bits valid.
- uint32* extended_high_sequence_number,
- uint32* jitter);
+ virtual void GetStatistics(uint8* fraction_lost,
+ uint32* cumulative_lost, // 24 bits valid.
+ uint32* extended_high_sequence_number,
+ uint32* jitter) OVERRIDE;
void UpdateStatistics(const RtpCastHeader& header);
private:
@@ -39,6 +40,8 @@ class ReceiverStats {
int interval_min_sequence_number_;
int interval_number_packets_;
int interval_wrap_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceiverStats);
};
} // namespace cast
diff --git a/chromium/media/cast/rtp_receiver/receiver_stats_unittest.cc b/chromium/media/cast/rtp_receiver/receiver_stats_unittest.cc
index 2788cb592de..98059cdde71 100644
--- a/chromium/media/cast/rtp_receiver/receiver_stats_unittest.cc
+++ b/chromium/media/cast/rtp_receiver/receiver_stats_unittest.cc
@@ -4,6 +4,8 @@
#include <gtest/gtest.h>
+#include <stdint.h>
+
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "media/cast/rtp_receiver/receiver_stats.h"
@@ -12,30 +14,24 @@
namespace media {
namespace cast {
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
+static const int64 kStartMillisecond = INT64_C(12345678900000);
static const uint32 kStdTimeIncrementMs = 33;
class ReceiverStatsTest : public ::testing::Test {
protected:
ReceiverStatsTest()
: stats_(&testing_clock_),
- rtp_header_(),
fraction_lost_(0),
cumulative_lost_(0),
extended_high_sequence_number_(0),
jitter_(0) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
- start_time_ = testing_clock_.NowTicks();
+ start_time_ = testing_clock_.NowTicks();
delta_increments_ = base::TimeDelta::FromMilliseconds(kStdTimeIncrementMs);
}
virtual ~ReceiverStatsTest() {}
- virtual void SetUp() {
- rtp_header_.webrtc.header.sequenceNumber = 0;
- rtp_header_.webrtc.header.timestamp = 0;
- }
-
uint32 ExpectedJitter(uint32 const_interval, int num_packets) {
float jitter = 0;
// Assume timestamps have a constant kStdTimeIncrementMs interval.
@@ -56,11 +52,15 @@ class ReceiverStatsTest : public ::testing::Test {
base::SimpleTestTickClock testing_clock_;
base::TimeTicks start_time_;
base::TimeDelta delta_increments_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceiverStatsTest);
};
TEST_F(ReceiverStatsTest, ResetState) {
- stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
- &extended_high_sequence_number_, &jitter_);
+ stats_.GetStatistics(&fraction_lost_,
+ &cumulative_lost_,
+ &extended_high_sequence_number_,
+ &jitter_);
EXPECT_EQ(0u, fraction_lost_);
EXPECT_EQ(0u, cumulative_lost_);
EXPECT_EQ(0u, extended_high_sequence_number_);
@@ -72,75 +72,81 @@ TEST_F(ReceiverStatsTest, LossCount) {
if (i % 4)
stats_.UpdateStatistics(rtp_header_);
if (i % 3) {
- rtp_header_.webrtc.header.timestamp += 33 * 90;
+ rtp_header_.rtp_timestamp += 33 * 90;
}
- ++rtp_header_.webrtc.header.sequenceNumber;
+ ++rtp_header_.sequence_number;
testing_clock_.Advance(delta_increments_);
}
- stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
- &extended_high_sequence_number_, &jitter_);
+ stats_.GetStatistics(&fraction_lost_,
+ &cumulative_lost_,
+ &extended_high_sequence_number_,
+ &jitter_);
EXPECT_EQ(63u, fraction_lost_);
EXPECT_EQ(74u, cumulative_lost_);
// Build extended sequence number.
- uint32 extended_seq_num = rtp_header_.webrtc.header.sequenceNumber - 1;
+ const uint32 extended_seq_num = rtp_header_.sequence_number - 1;
EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
}
TEST_F(ReceiverStatsTest, NoLossWrap) {
- rtp_header_.webrtc.header.sequenceNumber = 65500;
+ rtp_header_.sequence_number = 65500;
for (int i = 0; i < 300; ++i) {
- stats_.UpdateStatistics(rtp_header_);
+ stats_.UpdateStatistics(rtp_header_);
if (i % 3) {
- rtp_header_.webrtc.header.timestamp += 33 * 90;
+ rtp_header_.rtp_timestamp += 33 * 90;
}
- ++rtp_header_.webrtc.header.sequenceNumber;
+ ++rtp_header_.sequence_number;
testing_clock_.Advance(delta_increments_);
}
- stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
- &extended_high_sequence_number_, &jitter_);
+ stats_.GetStatistics(&fraction_lost_,
+ &cumulative_lost_,
+ &extended_high_sequence_number_,
+ &jitter_);
EXPECT_EQ(0u, fraction_lost_);
EXPECT_EQ(0u, cumulative_lost_);
// Build extended sequence number (one wrap cycle).
- uint32 extended_seq_num = (1 << 16) +
- rtp_header_.webrtc.header.sequenceNumber - 1;
+ const uint32 extended_seq_num = (1 << 16) + rtp_header_.sequence_number - 1;
EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
}
TEST_F(ReceiverStatsTest, LossCountWrap) {
- const uint32 start_sequence_number = 65500;
- rtp_header_.webrtc.header.sequenceNumber = start_sequence_number;
+ const uint32 kStartSequenceNumber = 65500;
+ rtp_header_.sequence_number = kStartSequenceNumber;
for (int i = 0; i < 300; ++i) {
if (i % 4)
stats_.UpdateStatistics(rtp_header_);
if (i % 3)
// Update timestamp.
- ++rtp_header_.webrtc.header.timestamp;
- ++rtp_header_.webrtc.header.sequenceNumber;
+ ++rtp_header_.rtp_timestamp;
+ ++rtp_header_.sequence_number;
testing_clock_.Advance(delta_increments_);
}
- stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
- &extended_high_sequence_number_, &jitter_);
+ stats_.GetStatistics(&fraction_lost_,
+ &cumulative_lost_,
+ &extended_high_sequence_number_,
+ &jitter_);
EXPECT_EQ(63u, fraction_lost_);
EXPECT_EQ(74u, cumulative_lost_);
// Build extended sequence number (one wrap cycle).
- uint32 extended_seq_num = (1 << 16) +
- rtp_header_.webrtc.header.sequenceNumber - 1;
+ const uint32 extended_seq_num = (1 << 16) + rtp_header_.sequence_number - 1;
EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
}
TEST_F(ReceiverStatsTest, BasicJitter) {
for (int i = 0; i < 300; ++i) {
stats_.UpdateStatistics(rtp_header_);
- ++rtp_header_.webrtc.header.sequenceNumber;
- rtp_header_.webrtc.header.timestamp += 33 * 90;
+ ++rtp_header_.sequence_number;
+ rtp_header_.rtp_timestamp += 33 * 90;
testing_clock_.Advance(delta_increments_);
}
- stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
- &extended_high_sequence_number_, &jitter_);
+ stats_.GetStatistics(&fraction_lost_,
+ &cumulative_lost_,
+ &extended_high_sequence_number_,
+ &jitter_);
EXPECT_FALSE(fraction_lost_);
EXPECT_FALSE(cumulative_lost_);
// Build extended sequence number (one wrap cycle).
- uint32 extended_seq_num = rtp_header_.webrtc.header.sequenceNumber - 1;
+ const uint32 extended_seq_num = rtp_header_.sequence_number - 1;
EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
EXPECT_EQ(ExpectedJitter(kStdTimeIncrementMs, 300), jitter_);
}
@@ -149,21 +155,23 @@ TEST_F(ReceiverStatsTest, NonTrivialJitter) {
const int kAdditionalIncrement = 5;
for (int i = 0; i < 300; ++i) {
stats_.UpdateStatistics(rtp_header_);
- ++rtp_header_.webrtc.header.sequenceNumber;
- rtp_header_.webrtc.header.timestamp += 33 * 90;
+ ++rtp_header_.sequence_number;
+ rtp_header_.rtp_timestamp += 33 * 90;
base::TimeDelta additional_delta =
base::TimeDelta::FromMilliseconds(kAdditionalIncrement);
testing_clock_.Advance(delta_increments_ + additional_delta);
}
- stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
- &extended_high_sequence_number_, &jitter_);
+ stats_.GetStatistics(&fraction_lost_,
+ &cumulative_lost_,
+ &extended_high_sequence_number_,
+ &jitter_);
EXPECT_FALSE(fraction_lost_);
EXPECT_FALSE(cumulative_lost_);
// Build extended sequence number (one wrap cycle).
- uint32 extended_seq_num = rtp_header_.webrtc.header.sequenceNumber - 1;
+ const uint32 extended_seq_num = rtp_header_.sequence_number - 1;
EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
- EXPECT_EQ(
- ExpectedJitter(kStdTimeIncrementMs + kAdditionalIncrement, 300), jitter_);
+ EXPECT_EQ(ExpectedJitter(kStdTimeIncrementMs + kAdditionalIncrement, 300),
+ jitter_);
}
} // namespace cast
diff --git a/chromium/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h b/chromium/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h
index b6647a835be..f5edf7c43fe 100644
--- a/chromium/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h
+++ b/chromium/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h
@@ -14,19 +14,17 @@ namespace cast {
class MockRtpFeedback : public RtpFeedback {
public:
MOCK_METHOD4(OnInitializeDecoder,
- int32(const int8 payloadType,
- const int frequency,
- const uint8 channels,
- const uint32 rate));
+ int32(const int8 payloadType,
+ const int frequency,
+ const uint8 channels,
+ const uint32 rate));
- MOCK_METHOD1(OnPacketTimeout,
- void(const int32 id));
+ MOCK_METHOD1(OnPacketTimeout, void(const int32 id));
MOCK_METHOD2(OnReceivedPacket,
- void(const int32 id, const RtpRtcpPacketType packet_type));
+ void(const int32 id, const RtpRtcpPacketField packet_type));
MOCK_METHOD2(OnPeriodicDeadOrAlive,
void(const int32 id, const RTPAliveType alive));
- MOCK_METHOD2(OnIncomingSSRCChanged,
- void(const int32 id, const uint32 ssrc));
+ MOCK_METHOD2(OnIncomingSSRCChanged, void(const int32 id, const uint32 ssrc));
MOCK_METHOD3(OnIncomingCSRCChanged,
void(const int32 id, const uint32 csrc, const bool added));
};
diff --git a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc
index 6ef20fe64e3..f44e82dac2b 100644
--- a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc
+++ b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc
@@ -4,104 +4,113 @@
#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h"
+#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/cast_defines.h"
-#include "media/cast/rtp_receiver/rtp_receiver.h"
-#include "net/base/big_endian.h"
namespace media {
namespace cast {
-static const size_t kRtpCommonHeaderLength = 12;
-static const size_t kRtpCastHeaderLength = 7;
+static const size_t kRtpHeaderLength = 12;
+static const size_t kCastHeaderLength = 7;
+static const uint8 kRtpExtensionBitMask = 0x10;
+static const uint8 kRtpMarkerBitMask = 0x80;
static const uint8 kCastKeyFrameBitMask = 0x80;
static const uint8 kCastReferenceFrameIdBitMask = 0x40;
-RtpParser::RtpParser(RtpData* incoming_payload_callback,
- const RtpParserConfig parser_config)
- : data_callback_(incoming_payload_callback),
- parser_config_(parser_config) {}
+RtpParser::RtpParser(uint32 expected_sender_ssrc, uint8 expected_payload_type)
+ : expected_sender_ssrc_(expected_sender_ssrc),
+ expected_payload_type_(expected_payload_type) {}
RtpParser::~RtpParser() {}
-bool RtpParser::ParsePacket(const uint8* packet, size_t length,
- RtpCastHeader* rtp_header) {
- if (length == 0) return false;
- // Get RTP general header.
- if (!ParseCommon(packet, length, rtp_header)) return false;
- if (rtp_header->webrtc.header.payloadType == parser_config_.payload_type &&
- rtp_header->webrtc.header.ssrc == parser_config_.ssrc) {
- return ParseCast(packet + kRtpCommonHeaderLength,
- length - kRtpCommonHeaderLength, rtp_header);
- }
- // Not a valid payload type / ssrc combination.
- return false;
-}
-
-bool RtpParser::ParseCommon(const uint8* packet,
+bool RtpParser::ParsePacket(const uint8* packet,
size_t length,
- RtpCastHeader* rtp_header) {
- if (length < kRtpCommonHeaderLength) return false;
- uint8 version = packet[0] >> 6;
- if (version != 2) return false;
- uint8 cc = packet[0] & 0x0f;
- bool marker = ((packet[1] & 0x80) != 0);
- int payload_type = packet[1] & 0x7f;
-
- uint16 sequence_number;
- uint32 rtp_timestamp, ssrc;
- net::BigEndianReader big_endian_reader(packet + 2, 10);
- big_endian_reader.ReadU16(&sequence_number);
- big_endian_reader.ReadU32(&rtp_timestamp);
- big_endian_reader.ReadU32(&ssrc);
-
- if (ssrc != parser_config_.ssrc) return false;
-
- rtp_header->webrtc.header.markerBit = marker;
- rtp_header->webrtc.header.payloadType = payload_type;
- rtp_header->webrtc.header.sequenceNumber = sequence_number;
- rtp_header->webrtc.header.timestamp = rtp_timestamp;
- rtp_header->webrtc.header.ssrc = ssrc;
- rtp_header->webrtc.header.numCSRCs = cc;
-
- uint8 csrc_octs = cc * 4;
- rtp_header->webrtc.type.Audio.numEnergy = rtp_header->webrtc.header.numCSRCs;
- rtp_header->webrtc.header.headerLength = kRtpCommonHeaderLength + csrc_octs;
- rtp_header->webrtc.type.Audio.isCNG = false;
- rtp_header->webrtc.type.Audio.channel = parser_config_.audio_channels;
- // TODO(pwestin): look at x bit and skip data.
- return true;
-}
-
-bool RtpParser::ParseCast(const uint8* packet,
- size_t length,
- RtpCastHeader* rtp_header) {
- if (length < kRtpCastHeaderLength) return false;
-
- // Extract header.
- const uint8* data_ptr = packet;
- size_t data_length = length;
- rtp_header->is_key_frame = (data_ptr[0] & kCastKeyFrameBitMask);
- rtp_header->is_reference = (data_ptr[0] & kCastReferenceFrameIdBitMask);
- rtp_header->frame_id = frame_id_wrap_helper_.MapTo32bitsFrameId(data_ptr[1]);
-
- net::BigEndianReader big_endian_reader(data_ptr + 2, 4);
- big_endian_reader.ReadU16(&rtp_header->packet_id);
- big_endian_reader.ReadU16(&rtp_header->max_packet_id);
-
- if (rtp_header->is_reference) {
- rtp_header->reference_frame_id =
- reference_frame_id_wrap_helper_.MapTo32bitsFrameId(data_ptr[6]);
- data_ptr += kRtpCastHeaderLength;
- data_length -= kRtpCastHeaderLength;
- } else {
- data_ptr += kRtpCastHeaderLength - 1;
- data_length -= kRtpCastHeaderLength - 1;
+ RtpCastHeader* header,
+ const uint8** payload_data,
+ size_t* payload_size) {
+ DCHECK(packet);
+ DCHECK(header);
+ DCHECK(payload_data);
+ DCHECK(payload_size);
+
+ if (length < (kRtpHeaderLength + kCastHeaderLength))
+ return false;
+
+ base::BigEndianReader reader(reinterpret_cast<const char*>(packet), length);
+
+ // Parse the RTP header. See
+ // http://en.wikipedia.org/wiki/Real-time_Transport_Protocol for an
+ // explanation of the standard RTP packet header.
+ uint8 bits;
+ if (!reader.ReadU8(&bits))
+ return false;
+ const uint8 version = bits >> 6;
+ if (version != 2)
+ return false;
+ if (bits & kRtpExtensionBitMask)
+ return false; // We lack the implementation to skip over an extension.
+ if (!reader.ReadU8(&bits))
+ return false;
+ header->marker = !!(bits & kRtpMarkerBitMask);
+ header->payload_type = bits & ~kRtpMarkerBitMask;
+ if (header->payload_type != expected_payload_type_)
+ return false; // Punt: Unexpected payload type.
+ if (!reader.ReadU16(&header->sequence_number) ||
+ !reader.ReadU32(&header->rtp_timestamp) ||
+ !reader.ReadU32(&header->sender_ssrc)) {
+ return false;
+ }
+ if (header->sender_ssrc != expected_sender_ssrc_)
+ return false; // Punt: Sender's SSRC does not match the expected one.
+
+ // Parse the Cast header. Note that, from the RTP protocol's perspective, the
+ // Cast header is part of the payload (and not meant to be an extension
+ // header).
+ if (!reader.ReadU8(&bits))
+ return false;
+ header->is_key_frame = !!(bits & kCastKeyFrameBitMask);
+ const bool includes_specific_frame_reference =
+ !!(bits & kCastReferenceFrameIdBitMask);
+ uint8 truncated_frame_id;
+ if (!reader.ReadU8(&truncated_frame_id) ||
+ !reader.ReadU16(&header->packet_id) ||
+ !reader.ReadU16(&header->max_packet_id)) {
+ return false;
+ }
+ // Sanity-check: Do the packet ID values make sense w.r.t. each other?
+ if (header->max_packet_id < header->packet_id)
+ return false;
+ uint8 truncated_reference_frame_id;
+ if (!includes_specific_frame_reference) {
+ // By default, a key frame only references itself; and non-key frames
+ // reference their direct predecessor.
+ truncated_reference_frame_id = truncated_frame_id;
+ if (!header->is_key_frame)
+ --truncated_reference_frame_id;
+ } else if (!reader.ReadU8(&truncated_reference_frame_id)) {
+ return false;
}
- if (rtp_header->max_packet_id < rtp_header->packet_id) return false;
+ // Only the lower 8 bits of the |frame_id| were serialized, so do some magic
+ // to restore the upper 24 bits.
+ //
+ // Note: The call to |frame_id_wrap_helper_| has side effects, so we must not
+ // call it until we know the entire deserialization will succeed.
+ header->frame_id =
+ frame_id_wrap_helper_.MapTo32bitsFrameId(truncated_frame_id);
+ // When the upper 24 bits are restored to |reference_frame_id|, make sure
+ // |reference_frame_id| will be strictly less than or equal to |frame_id|.
+ if (truncated_reference_frame_id <= truncated_frame_id)
+ header->reference_frame_id = header->frame_id & 0xffffff00;
+ else
+ header->reference_frame_id = (header->frame_id & 0xffffff00) - 0x00000100;
+ header->reference_frame_id |= truncated_reference_frame_id;
+
+ // All remaining data in the packet is the payload.
+ *payload_data = reinterpret_cast<const uint8*>(reader.ptr());
+ *payload_size = reader.remaining();
- data_callback_->OnReceivedPayloadData(data_ptr, data_length, rtp_header);
return true;
}
diff --git a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.gyp b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.gyp
deleted file mode 100644
index 258b0bff532..00000000000
--- a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.gyp
+++ /dev/null
@@ -1,23 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_rtp_parser',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- ],
- 'sources': [
- 'rtp_parser.cc',
- 'rtp_parser.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.h b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.h
index 33bc92a6e6e..35118cf1446 100644
--- a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.h
+++ b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser.h
@@ -5,49 +5,40 @@
#ifndef MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_RTP_PARSER_H_
#define MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_RTP_PARSER_H_
-#include "media/cast/net/cast_net_defines.h"
#include "media/cast/rtp_receiver/rtp_receiver_defines.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace media {
namespace cast {
-class RtpData;
-
-struct RtpParserConfig {
- RtpParserConfig() {
- ssrc = 0;
- payload_type = 0;
- audio_channels = 0;
- }
-
- uint32 ssrc;
- int payload_type;
- AudioCodec audio_codec;
- VideoCodec video_codec;
- int audio_channels;
-};
-
+// TODO(miu): RtpParser and RtpPacketizer should be consolidated into a single
+// module that handles all RTP/Cast packet serialization and deserialization
+// throughout the media/cast library.
class RtpParser {
public:
- RtpParser(RtpData* incoming_payload_callback,
- const RtpParserConfig parser_config);
-
- ~RtpParser();
-
- bool ParsePacket(const uint8* packet, size_t length,
- RtpCastHeader* rtp_header);
+ RtpParser(uint32 expected_sender_ssrc, uint8 expected_payload_type);
+
+ virtual ~RtpParser();
+
+ // Parses the |packet|, expecting an RTP header along with a Cast header at
+ // the beginning of the the RTP payload. This method populates the structure
+ // pointed to by |rtp_header| and sets the |payload_data| pointer and
+ // |payload_size| to the memory region within |packet| containing the Cast
+ // payload data. Returns false if the data appears to be invalid, is not from
+ // the expected sender (as identified by the SSRC field), or is not the
+ // expected payload type.
+ bool ParsePacket(const uint8* packet,
+ size_t length,
+ RtpCastHeader* rtp_header,
+ const uint8** payload_data,
+ size_t* payload_size);
private:
- bool ParseCommon(const uint8* packet, size_t length,
- RtpCastHeader* rtp_header);
-
- bool ParseCast(const uint8* packet, size_t length,
- RtpCastHeader* rtp_header);
+ const uint32 expected_sender_ssrc_;
+ const uint8 expected_payload_type_;
+ transport::FrameIdWrapHelper frame_id_wrap_helper_;
- RtpData* data_callback_;
- RtpParserConfig parser_config_;
- FrameIdWrapHelper frame_id_wrap_helper_;
- FrameIdWrapHelper reference_frame_id_wrap_helper_;
+ DISALLOW_COPY_AND_ASSIGN(RtpParser);
};
} // namespace cast
diff --git a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc
index c0f91d10fff..47c79139ffc 100644
--- a/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc
+++ b/chromium/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc
@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <gtest/gtest.h>
-
#include "base/memory/scoped_ptr.h"
+#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"
namespace media {
namespace cast {
@@ -20,178 +19,178 @@ static const uint32 kTestTimestamp = 111111;
static const uint16 kTestSeqNum = 4321;
static const uint8 kRefFrameId = 17;
-class RtpDataTest : public RtpData {
- public:
- RtpDataTest() {
- expected_header_.reset(new RtpCastHeader());
- }
-
- virtual ~RtpDataTest() {}
-
- void SetExpectedHeader(const RtpCastHeader& cast_header) {
- memcpy(expected_header_.get(), &cast_header, sizeof(RtpCastHeader));
- }
-
- virtual void OnReceivedPayloadData(const uint8* payloadData,
- size_t payloadSize,
- const RtpCastHeader* rtpHeader) OVERRIDE {
- VerifyCommonHeader(*rtpHeader);
- VerifyCastHeader(*rtpHeader);
- }
-
- void VerifyCommonHeader(const RtpCastHeader& parsed_header) {
- EXPECT_EQ(expected_header_->packet_id == expected_header_->max_packet_id,
- parsed_header.webrtc.header.markerBit);
- EXPECT_EQ(kTestPayloadType, parsed_header.webrtc.header.payloadType);
- EXPECT_EQ(kTestSsrc, parsed_header.webrtc.header.ssrc);
- EXPECT_EQ(0, parsed_header.webrtc.header.numCSRCs);
- }
-
- void VerifyCastHeader(const RtpCastHeader& parsed_header) {
- EXPECT_EQ(expected_header_->is_key_frame, parsed_header.is_key_frame);
- EXPECT_EQ(expected_header_->frame_id, parsed_header.frame_id);
- EXPECT_EQ(expected_header_->packet_id, parsed_header.packet_id);
- EXPECT_EQ(expected_header_->max_packet_id, parsed_header.max_packet_id);
- EXPECT_EQ(expected_header_->is_reference, parsed_header.is_reference);
- }
-
- private:
- scoped_ptr<RtpCastHeader> expected_header_;
-};
-
class RtpParserTest : public ::testing::Test {
protected:
- RtpParserTest() {
- PopulateConfig();
- rtp_data_.reset(new RtpDataTest());
- rtp_parser_.reset(new RtpParser(rtp_data_.get(), config_));
- }
-
- virtual ~RtpParserTest() {}
-
- virtual void SetUp() {
- cast_header_.is_reference = true;
- cast_header_.reference_frame_id = kRefFrameId;
+ RtpParserTest() : rtp_parser_(kTestSsrc, kTestPayloadType) {
packet_builder_.SetSsrc(kTestSsrc);
- packet_builder_.SetReferenceFrameId(kRefFrameId, true);
packet_builder_.SetSequenceNumber(kTestSeqNum);
packet_builder_.SetTimestamp(kTestTimestamp);
packet_builder_.SetPayloadType(kTestPayloadType);
packet_builder_.SetMarkerBit(true); // Only one packet.
+ cast_header_.sender_ssrc = kTestSsrc;
+ cast_header_.sequence_number = kTestSeqNum;
+ cast_header_.rtp_timestamp = kTestTimestamp;
+ cast_header_.payload_type = kTestPayloadType;
+ cast_header_.marker = true;
+ }
+
+ virtual ~RtpParserTest() {}
+
+ void ExpectParsesPacket() {
+ RtpCastHeader parsed_header;
+ const uint8* payload = NULL;
+ size_t payload_size = -1;
+ EXPECT_TRUE(rtp_parser_.ParsePacket(
+ packet_, kPacketLength, &parsed_header, &payload, &payload_size));
+
+ EXPECT_EQ(cast_header_.marker, parsed_header.marker);
+ EXPECT_EQ(cast_header_.payload_type, parsed_header.payload_type);
+ EXPECT_EQ(cast_header_.sequence_number, parsed_header.sequence_number);
+ EXPECT_EQ(cast_header_.rtp_timestamp, parsed_header.rtp_timestamp);
+ EXPECT_EQ(cast_header_.sender_ssrc, parsed_header.sender_ssrc);
+
+ EXPECT_EQ(cast_header_.is_key_frame, parsed_header.is_key_frame);
+ EXPECT_EQ(cast_header_.frame_id, parsed_header.frame_id);
+ EXPECT_EQ(cast_header_.packet_id, parsed_header.packet_id);
+ EXPECT_EQ(cast_header_.max_packet_id, parsed_header.max_packet_id);
+ EXPECT_EQ(cast_header_.reference_frame_id,
+ parsed_header.reference_frame_id);
+
+ EXPECT_TRUE(!!payload);
+ EXPECT_NE(static_cast<size_t>(-1), payload_size);
}
- void PopulateConfig() {
- config_.payload_type = kTestPayloadType;
- config_.ssrc = kTestSsrc;
+ void ExpectDoesNotParsePacket() {
+ RtpCastHeader parsed_header;
+ const uint8* payload = NULL;
+ size_t payload_size = -1;
+ EXPECT_FALSE(rtp_parser_.ParsePacket(
+ packet_, kPacketLength, &parsed_header, &payload, &payload_size));
}
- scoped_ptr<RtpDataTest> rtp_data_;
RtpPacketBuilder packet_builder_;
- scoped_ptr<RtpParser> rtp_parser_;
- RtpParserConfig config_;
+ uint8 packet_[kPacketLength];
+ RtpParser rtp_parser_;
RtpCastHeader cast_header_;
};
TEST_F(RtpParserTest, ParseDefaultCastPacket) {
- // Build generic data packet.
- uint8 packet[kPacketLength];
- packet_builder_.BuildHeader(packet, kPacketLength);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- rtp_data_->SetExpectedHeader(cast_header_);
- EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ ExpectParsesPacket();
}
TEST_F(RtpParserTest, ParseNonDefaultCastPacket) {
- // Build generic data packet.
- uint8 packet[kPacketLength];
packet_builder_.SetKeyFrame(true);
- packet_builder_.SetFrameId(10);
+ packet_builder_.SetFrameIds(10, 10);
packet_builder_.SetPacketId(5);
packet_builder_.SetMaxPacketId(15);
packet_builder_.SetMarkerBit(false);
- packet_builder_.BuildHeader(packet, kPacketLength);
+ packet_builder_.BuildHeader(packet_, kPacketLength);
cast_header_.is_key_frame = true;
cast_header_.frame_id = 10;
+ cast_header_.reference_frame_id = 10;
cast_header_.packet_id = 5;
cast_header_.max_packet_id = 15;
- rtp_data_->SetExpectedHeader(cast_header_);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+ cast_header_.marker = false;
+ ExpectParsesPacket();
}
TEST_F(RtpParserTest, TooBigPacketId) {
- // Build generic data packet.
- uint8 packet[kPacketLength];
packet_builder_.SetKeyFrame(true);
- packet_builder_.SetFrameId(10);
+ packet_builder_.SetFrameIds(10, 10);
packet_builder_.SetPacketId(15);
packet_builder_.SetMaxPacketId(5);
- packet_builder_.BuildHeader(packet, kPacketLength);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- EXPECT_FALSE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ cast_header_.is_key_frame = true;
+ cast_header_.frame_id = 10;
+ cast_header_.reference_frame_id = 10;
+ cast_header_.packet_id = 15;
+ cast_header_.max_packet_id = 5;
+ ExpectDoesNotParsePacket();
}
TEST_F(RtpParserTest, MaxPacketId) {
- // Build generic data packet.
- uint8 packet[kPacketLength];
packet_builder_.SetKeyFrame(true);
- packet_builder_.SetFrameId(10);
+ packet_builder_.SetFrameIds(10, 10);
packet_builder_.SetPacketId(65535);
packet_builder_.SetMaxPacketId(65535);
- packet_builder_.BuildHeader(packet, kPacketLength);
+ packet_builder_.BuildHeader(packet_, kPacketLength);
cast_header_.is_key_frame = true;
cast_header_.frame_id = 10;
+ cast_header_.reference_frame_id = 10;
cast_header_.packet_id = 65535;
cast_header_.max_packet_id = 65535;
- rtp_data_->SetExpectedHeader(cast_header_);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+ ExpectParsesPacket();
}
TEST_F(RtpParserTest, InvalidPayloadType) {
- // Build generic data packet.
- uint8 packet[kPacketLength];
packet_builder_.SetKeyFrame(true);
- packet_builder_.SetFrameId(10);
+ packet_builder_.SetFrameIds(10, 10);
packet_builder_.SetPacketId(65535);
packet_builder_.SetMaxPacketId(65535);
packet_builder_.SetPayloadType(kTestPayloadType - 1);
- packet_builder_.BuildHeader(packet, kPacketLength);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- EXPECT_FALSE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ cast_header_.is_key_frame = true;
+ cast_header_.frame_id = 10;
+ cast_header_.reference_frame_id = 10;
+ cast_header_.packet_id = 65535;
+ cast_header_.max_packet_id = 65535;
+ cast_header_.payload_type = kTestPayloadType - 1;
+ ExpectDoesNotParsePacket();
}
TEST_F(RtpParserTest, InvalidSsrc) {
- // Build generic data packet.
- uint8 packet[kPacketLength];
packet_builder_.SetKeyFrame(true);
- packet_builder_.SetFrameId(10);
+ packet_builder_.SetFrameIds(10, 10);
packet_builder_.SetPacketId(65535);
packet_builder_.SetMaxPacketId(65535);
packet_builder_.SetSsrc(kTestSsrc - 1);
- packet_builder_.BuildHeader(packet, kPacketLength);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- EXPECT_FALSE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ cast_header_.is_key_frame = true;
+ cast_header_.frame_id = 10;
+ cast_header_.reference_frame_id = 10;
+ cast_header_.packet_id = 65535;
+ cast_header_.max_packet_id = 65535;
+ cast_header_.sender_ssrc = kTestSsrc - 1;
+ ExpectDoesNotParsePacket();
+}
+
+TEST_F(RtpParserTest, ParseCastPacketWithSpecificFrameReference) {
+ packet_builder_.SetFrameIds(kRefFrameId + 3, kRefFrameId);
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ cast_header_.frame_id = kRefFrameId + 3;
+ cast_header_.reference_frame_id = kRefFrameId;
+ ExpectParsesPacket();
+}
+
+TEST_F(RtpParserTest, ParseExpandingFrameIdTo32Bits) {
+ const uint32 kMaxFrameId = 1000;
+ packet_builder_.SetKeyFrame(true);
+ cast_header_.is_key_frame = true;
+ for (uint32 frame_id = 0; frame_id <= kMaxFrameId; ++frame_id) {
+ packet_builder_.SetFrameIds(frame_id, frame_id);
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ cast_header_.frame_id = frame_id;
+ cast_header_.reference_frame_id = frame_id;
+ ExpectParsesPacket();
+ }
}
-TEST_F(RtpParserTest, ParseCastPacketWithoutReference) {
- cast_header_.is_reference = false;
- cast_header_.reference_frame_id = 0;
- packet_builder_.SetReferenceFrameId(kRefFrameId, false);
-
- // Build generic data packet.
- uint8 packet[kPacketLength];
- packet_builder_.BuildHeader(packet, kPacketLength);
- // Parse packet as is.
- RtpCastHeader rtp_header;
- rtp_data_->SetExpectedHeader(cast_header_);
- EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+TEST_F(RtpParserTest, ParseExpandingReferenceFrameIdTo32Bits) {
+ const uint32 kMaxFrameId = 1000;
+ const uint32 kMaxBackReferenceOffset = 10;
+ packet_builder_.SetKeyFrame(false);
+ cast_header_.is_key_frame = false;
+ for (uint32 frame_id = kMaxBackReferenceOffset;
+ frame_id <= kMaxFrameId; ++frame_id) {
+ const uint32 reference_frame_id =
+ frame_id - base::RandInt(1, kMaxBackReferenceOffset);
+ packet_builder_.SetFrameIds(frame_id, reference_frame_id);
+ packet_builder_.BuildHeader(packet_, kPacketLength);
+ cast_header_.frame_id = frame_id;
+ cast_header_.reference_frame_id = reference_frame_id;
+ ExpectParsesPacket();
+ }
}
} // namespace cast
diff --git a/chromium/media/cast/rtp_receiver/rtp_receiver.cc b/chromium/media/cast/rtp_receiver/rtp_receiver.cc
deleted file mode 100644
index 3c804d9bd9b..00000000000
--- a/chromium/media/cast/rtp_receiver/rtp_receiver.cc
+++ /dev/null
@@ -1,70 +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/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"
-#include "net/base/big_endian.h"
-
-namespace media {
-namespace cast {
-
-RtpReceiver::RtpReceiver(base::TickClock* clock,
- const AudioReceiverConfig* audio_config,
- const VideoReceiverConfig* video_config,
- RtpData* incoming_payload_callback) {
- DCHECK(incoming_payload_callback) << "Invalid argument";
- DCHECK(audio_config || video_config) << "Invalid argument";
-
- // Configure parser.
- RtpParserConfig config;
- if (audio_config) {
- config.ssrc = audio_config->incoming_ssrc;
- config.payload_type = audio_config->rtp_payload_type;
- config.audio_codec = audio_config->codec;
- config.audio_channels = audio_config->channels;
- } else {
- config.ssrc = video_config->incoming_ssrc;
- config.payload_type = video_config->rtp_payload_type;
- config.video_codec = video_config->codec;
- }
- stats_.reset(new ReceiverStats(clock));
- parser_.reset(new RtpParser(incoming_payload_callback, 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;
- net::BigEndianReader big_endian_reader(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;
- if (!parser_->ParsePacket(packet, length, &rtp_header)) return false;
-
- stats_->UpdateStatistics(rtp_header);
- return true;
-}
-
-void RtpReceiver::GetStatistics(uint8* fraction_lost,
- uint32* cumulative_lost,
- uint32* extended_high_sequence_number,
- uint32* jitter) {
- stats_->GetStatistics(fraction_lost,
- cumulative_lost,
- extended_high_sequence_number,
- jitter);
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/rtp_receiver/rtp_receiver.gyp b/chromium/media/cast/rtp_receiver/rtp_receiver.gyp
deleted file mode 100644
index b612964c070..00000000000
--- a/chromium/media/cast/rtp_receiver/rtp_receiver.gyp
+++ /dev/null
@@ -1,26 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_rtp_receiver',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- ],
- 'sources': [
- 'receiver_stats.cc',
- 'receiver_stats.h',
- 'rtp_receiver.cc',
- 'rtp_receiver.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- 'rtp_parser/rtp_parser.gyp:*',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/rtp_receiver/rtp_receiver.h b/chromium/media/cast/rtp_receiver/rtp_receiver.h
deleted file mode 100644
index 5639d7d8c36..00000000000
--- a/chromium/media/cast/rtp_receiver/rtp_receiver.h
+++ /dev/null
@@ -1,56 +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/rtp_receiver_defines.h"
-
-namespace media {
-namespace cast {
-
-class RtpData {
- public:
- virtual void OnReceivedPayloadData(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader* rtp_header) = 0;
-
- protected:
- virtual ~RtpData() {}
-};
-
-class ReceiverStats;
-class RtpParser;
-
-class RtpReceiver {
- public:
- RtpReceiver(base::TickClock* clock,
- const AudioReceiverConfig* audio_config,
- const VideoReceiverConfig* video_config,
- RtpData* incoming_payload_callback);
- ~RtpReceiver();
-
- static uint32 GetSsrcOfSender(const uint8* rtcp_buffer, size_t length);
-
- bool ReceivedPacket(const uint8* packet, size_t length);
-
- void GetStatistics(uint8* fraction_lost,
- uint32* cumulative_lost, // 24 bits valid.
- uint32* extended_high_sequence_number,
- uint32* jitter);
-
- private:
- scoped_ptr<ReceiverStats> stats_;
- scoped_ptr<RtpParser> parser_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_
diff --git a/chromium/media/cast/rtp_receiver/rtp_receiver_defines.cc b/chromium/media/cast/rtp_receiver/rtp_receiver_defines.cc
new file mode 100644
index 00000000000..e42b2b733c7
--- /dev/null
+++ b/chromium/media/cast/rtp_receiver/rtp_receiver_defines.cc
@@ -0,0 +1,25 @@
+// 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/rtp_receiver/rtp_receiver_defines.h"
+
+namespace media {
+namespace cast {
+
+RtpCastHeader::RtpCastHeader()
+ : marker(false),
+ payload_type(0),
+ sequence_number(0),
+ rtp_timestamp(0),
+ sender_ssrc(0),
+ is_key_frame(false),
+ frame_id(0),
+ packet_id(0),
+ max_packet_id(0),
+ reference_frame_id(0) {}
+
+RtpPayloadFeedback::~RtpPayloadFeedback() {}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/rtp_receiver/rtp_receiver_defines.h b/chromium/media/cast/rtp_receiver/rtp_receiver_defines.h
index ae957e3ae6b..d907436f489 100644
--- a/chromium/media/cast/rtp_receiver/rtp_receiver_defines.h
+++ b/chromium/media/cast/rtp_receiver/rtp_receiver_defines.h
@@ -8,29 +8,25 @@
#include "base/basictypes.h"
#include "media/cast/cast_config.h"
#include "media/cast/rtcp/rtcp_defines.h"
-#include "third_party/webrtc/modules/interface/module_common_types.h"
namespace media {
namespace cast {
-const uint8 kRtpMarkerBitMask = 0x80;
-
struct RtpCastHeader {
- RtpCastHeader() {
- is_key_frame = false;
- frame_id = 0;
- packet_id = 0;
- max_packet_id = 0;
- is_reference = false;
- reference_frame_id = 0;
- }
- webrtc::WebRtcRTPHeader webrtc;
+ RtpCastHeader();
+
+ // Elements from RTP packet header.
+ bool marker;
+ uint8 payload_type;
+ uint16 sequence_number;
+ uint32 rtp_timestamp;
+ uint32 sender_ssrc;
+
+ // Elements from Cast header (at beginning of RTP payload).
bool is_key_frame;
uint32 frame_id;
uint16 packet_id;
uint16 max_packet_id;
- bool is_reference; // Set to true if the previous frame is not available,
- // and the reference frame id is available.
uint32 reference_frame_id;
};
@@ -39,7 +35,7 @@ class RtpPayloadFeedback {
virtual void CastFeedback(const RtcpCastMessage& cast_feedback) = 0;
protected:
- virtual ~RtpPayloadFeedback() {}
+ virtual ~RtpPayloadFeedback();
};
} // namespace cast
diff --git a/chromium/media/cast/rtp_timestamp_helper.cc b/chromium/media/cast/rtp_timestamp_helper.cc
new file mode 100644
index 00000000000..3349e7b33fd
--- /dev/null
+++ b/chromium/media/cast/rtp_timestamp_helper.cc
@@ -0,0 +1,36 @@
+// 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/rtp_timestamp_helper.h"
+
+namespace media {
+namespace cast {
+
+RtpTimestampHelper::RtpTimestampHelper(int frequency)
+ : frequency_(frequency),
+ last_rtp_timestamp_(0) {
+}
+
+RtpTimestampHelper::~RtpTimestampHelper() {
+}
+
+bool RtpTimestampHelper::GetCurrentTimeAsRtpTimestamp(
+ const base::TimeTicks& now, uint32* rtp_timestamp) const {
+ if (last_capture_time_.is_null())
+ return false;
+ const base::TimeDelta elapsed_time = now - last_capture_time_;
+ const int64 rtp_delta =
+ elapsed_time * frequency_ / base::TimeDelta::FromSeconds(1);
+ *rtp_timestamp = last_rtp_timestamp_ + static_cast<uint32>(rtp_delta);
+ return true;
+}
+
+void RtpTimestampHelper::StoreLatestTime(
+ base::TimeTicks capture_time, uint32 rtp_timestamp) {
+ last_capture_time_ = capture_time;
+ last_rtp_timestamp_ = rtp_timestamp;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/rtp_timestamp_helper.h b/chromium/media/cast/rtp_timestamp_helper.h
new file mode 100644
index 00000000000..b9c650c5063
--- /dev/null
+++ b/chromium/media/cast/rtp_timestamp_helper.h
@@ -0,0 +1,41 @@
+// 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_RTP_TIMESTAMP_HELPER_H_
+#define MEDIA_CAST_RTP_TIMESTAMP_HELPER_H_
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+// A helper class used to convert current time ticks into RTP timestamp.
+class RtpTimestampHelper {
+ public:
+ explicit RtpTimestampHelper(int frequency);
+ ~RtpTimestampHelper();
+
+ // Compute a RTP timestamp using current time, last encoded time and
+ // last encoded RTP timestamp.
+ // Return true if |rtp_timestamp| is computed.
+ bool GetCurrentTimeAsRtpTimestamp(const base::TimeTicks& now,
+ uint32* rtp_timestamp) const;
+
+ // Store the capture time and the corresponding RTP timestamp for the
+ // last encoded frame.
+ void StoreLatestTime(base::TimeTicks capture_time, uint32 rtp_timestamp);
+
+ private:
+ int frequency_;
+ base::TimeTicks last_capture_time_;
+ uint32 last_rtp_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtpTimestampHelper);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_CAST_DEFINES_H_
diff --git a/chromium/media/cast/test/transport/transport.gyp b/chromium/media/cast/test/transport/transport.gyp
deleted file mode 100644
index 79be3d28e6d..00000000000
--- a/chromium/media/cast/test/transport/transport.gyp
+++ /dev/null
@@ -1,22 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_transport',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'sources': [
- 'transport.cc',
- 'transport.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/net/net.gyp:net',
- ],
- },
- ],
-} \ No newline at end of file
diff --git a/chromium/media/cast/test/utility/utility.gyp b/chromium/media/cast/test/utility/utility.gyp
deleted file mode 100644
index 021c2d9a416..00000000000
--- a/chromium/media/cast/test/utility/utility.gyp
+++ /dev/null
@@ -1,28 +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.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_test_utility',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- ],
- 'dependencies': [
- '<(DEPTH)/ui/gfx/gfx.gyp:gfx',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- '<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
-
- ],
- 'sources': [
- 'input_helper.cc',
- 'input_helper.h',
- '<(DEPTH)/media/cast/test/audio_utility.cc',
- '<(DEPTH)/media/cast/test/fake_task_runner.cc',
- '<(DEPTH)/media/cast/test/video_utility.cc',
- ], # source
- },
- ],
-} \ No newline at end of file
diff --git a/chromium/media/cast/transport/cast_transport_config.cc b/chromium/media/cast/transport/cast_transport_config.cc
new file mode 100644
index 00000000000..16e90347137
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_config.cc
@@ -0,0 +1,82 @@
+// 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/transport/cast_transport_config.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+namespace {
+const int kDefaultRtpMaxDelayMs = 100;
+} // namespace
+
+RtpConfig::RtpConfig()
+ : ssrc(0),
+ max_delay_ms(kDefaultRtpMaxDelayMs),
+ payload_type(0) {}
+
+RtpConfig::~RtpConfig() {}
+
+CastTransportRtpConfig::CastTransportRtpConfig()
+ : max_outstanding_frames(-1) {}
+
+CastTransportRtpConfig::~CastTransportRtpConfig() {}
+
+CastTransportAudioConfig::CastTransportAudioConfig()
+ : codec(kOpus), frequency(0), channels(0) {}
+
+CastTransportAudioConfig::~CastTransportAudioConfig() {}
+
+CastTransportVideoConfig::CastTransportVideoConfig() : codec(kVp8) {}
+
+CastTransportVideoConfig::~CastTransportVideoConfig() {}
+
+EncodedFrame::EncodedFrame()
+ : dependency(UNKNOWN_DEPENDENCY),
+ frame_id(0),
+ referenced_frame_id(0),
+ rtp_timestamp(0) {}
+
+EncodedFrame::~EncodedFrame() {}
+
+void EncodedFrame::CopyMetadataTo(EncodedFrame* dest) const {
+ DCHECK(dest);
+ dest->dependency = this->dependency;
+ dest->frame_id = this->frame_id;
+ dest->referenced_frame_id = this->referenced_frame_id;
+ dest->rtp_timestamp = this->rtp_timestamp;
+ dest->reference_time = this->reference_time;
+}
+
+RtcpSenderInfo::RtcpSenderInfo()
+ : ntp_seconds(0),
+ ntp_fraction(0),
+ rtp_timestamp(0),
+ send_packet_count(0),
+ send_octet_count(0) {}
+RtcpSenderInfo::~RtcpSenderInfo() {}
+
+RtcpReportBlock::RtcpReportBlock()
+ : remote_ssrc(0),
+ media_ssrc(0),
+ fraction_lost(0),
+ cumulative_lost(0),
+ extended_high_sequence_number(0),
+ jitter(0),
+ last_sr(0),
+ delay_since_last_sr(0) {}
+RtcpReportBlock::~RtcpReportBlock() {}
+
+RtcpDlrrReportBlock::RtcpDlrrReportBlock()
+ : last_rr(0), delay_since_last_rr(0) {}
+RtcpDlrrReportBlock::~RtcpDlrrReportBlock() {}
+
+SendRtcpFromRtpSenderData::SendRtcpFromRtpSenderData()
+ : packet_type_flags(0), sending_ssrc(0) {}
+SendRtcpFromRtpSenderData::~SendRtcpFromRtpSenderData() {}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/cast_transport_config.h b/chromium/media/cast/transport/cast_transport_config.h
new file mode 100644
index 00000000000..96b771acb99
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_config.h
@@ -0,0 +1,221 @@
+// 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_TRANSPORT_CAST_TRANSPORT_CONFIG_H_
+#define MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_CONFIG_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/stl_util.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "net/base/ip_endpoint.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+enum RtcpMode {
+ kRtcpCompound, // Compound RTCP mode is described by RFC 4585.
+ kRtcpReducedSize, // Reduced-size RTCP mode is described by RFC 5506.
+};
+
+enum VideoCodec {
+ kUnknownVideoCodec,
+ kFakeSoftwareVideo,
+ kVp8,
+ kH264,
+ kVideoCodecLast = kH264
+};
+
+enum AudioCodec {
+ kUnknownAudioCodec,
+ kOpus,
+ kPcm16,
+ kAudioCodecLast = kPcm16
+};
+
+struct RtpConfig {
+ RtpConfig();
+ ~RtpConfig();
+ uint32 ssrc;
+ int max_delay_ms;
+ int payload_type;
+ std::string aes_key; // Binary string of size kAesKeySize.
+ std::string aes_iv_mask; // Binary string of size kAesBlockSize.
+};
+
+struct CastTransportRtpConfig {
+ CastTransportRtpConfig();
+ ~CastTransportRtpConfig();
+ RtpConfig config;
+ int max_outstanding_frames;
+};
+
+struct CastTransportAudioConfig {
+ CastTransportAudioConfig();
+ ~CastTransportAudioConfig();
+
+ CastTransportRtpConfig rtp;
+ AudioCodec codec;
+ int frequency;
+ int channels;
+};
+
+struct CastTransportVideoConfig {
+ CastTransportVideoConfig();
+ ~CastTransportVideoConfig();
+
+ CastTransportRtpConfig rtp;
+ VideoCodec codec;
+};
+
+// A combination of metadata and data for one encoded frame. This can contain
+// audio data or video data or other.
+struct EncodedFrame {
+ enum Dependency {
+ // "null" value, used to indicate whether |dependency| has been set.
+ UNKNOWN_DEPENDENCY,
+
+ // Not decodable without the reference frame indicated by
+ // |referenced_frame_id|.
+ DEPENDENT,
+
+ // Independently decodable.
+ INDEPENDENT,
+
+ // Independently decodable, and no future frames will depend on any frames
+ // before this one.
+ KEY,
+
+ DEPENDENCY_LAST = KEY
+ };
+
+ EncodedFrame();
+ ~EncodedFrame();
+
+ // Convenience accessors to data as an array of uint8 elements.
+ const uint8* bytes() const {
+ return reinterpret_cast<uint8*>(string_as_array(
+ const_cast<std::string*>(&data)));
+ }
+ uint8* mutable_bytes() {
+ return reinterpret_cast<uint8*>(string_as_array(&data));
+ }
+
+ // Copies all data members except |data| to |dest|.
+ // Does not modify |dest->data|.
+ void CopyMetadataTo(EncodedFrame* dest) const;
+
+ // This frame's dependency relationship with respect to other frames.
+ Dependency dependency;
+
+ // The label associated with this frame. Implies an ordering relative to
+ // other frames in the same stream.
+ uint32 frame_id;
+
+ // The label associated with the frame upon which this frame depends. If
+ // this frame does not require any other frame in order to become decodable
+ // (e.g., key frames), |referenced_frame_id| must equal |frame_id|.
+ uint32 referenced_frame_id;
+
+ // The stream timestamp, on the timeline of the signal data. For example, RTP
+ // timestamps for audio are usually defined as the total number of audio
+ // samples encoded in all prior frames. A playback system uses this value to
+ // detect gaps in the stream, and otherwise stretch the signal to match
+ // playout targets.
+ uint32 rtp_timestamp;
+
+ // The common reference clock timestamp for this frame. This value originates
+ // from a sender and is used to provide lip synchronization between streams in
+ // a receiver. Thus, in the sender context, this is set to the time at which
+ // the frame was captured/recorded. In the receiver context, this is set to
+ // the target playout time. Over a sequence of frames, this time value is
+ // expected to drift with respect to the elapsed time implied by the RTP
+ // timestamps; and it may not necessarily increment with precise regularity.
+ base::TimeTicks reference_time;
+
+ // The encoded signal data.
+ std::string data;
+};
+
+typedef std::vector<uint8> Packet;
+typedef scoped_refptr<base::RefCountedData<Packet> > PacketRef;
+typedef std::vector<PacketRef> PacketList;
+
+typedef base::Callback<void(scoped_ptr<Packet> packet)> PacketReceiverCallback;
+
+class PacketSender {
+ public:
+ // Send a packet to the network. Returns false if the network is blocked
+ // and we should wait for |cb| to be called. It is not allowed to called
+ // SendPacket again until |cb| has been called. Any other errors that
+ // occur will be reported through side channels, in such cases, this function
+ // will return true indicating that the channel is not blocked.
+ virtual bool SendPacket(PacketRef packet, const base::Closure& cb) = 0;
+ virtual ~PacketSender() {}
+};
+
+struct RtcpSenderInfo {
+ RtcpSenderInfo();
+ ~RtcpSenderInfo();
+ // First three members are used for lipsync.
+ // First two members are used for rtt.
+ uint32 ntp_seconds;
+ uint32 ntp_fraction;
+ uint32 rtp_timestamp;
+ uint32 send_packet_count;
+ size_t send_octet_count;
+};
+
+struct RtcpReportBlock {
+ RtcpReportBlock();
+ ~RtcpReportBlock();
+ uint32 remote_ssrc; // SSRC of sender of this report.
+ uint32 media_ssrc; // SSRC of the RTP packet sender.
+ uint8 fraction_lost;
+ uint32 cumulative_lost; // 24 bits valid.
+ uint32 extended_high_sequence_number;
+ uint32 jitter;
+ uint32 last_sr;
+ uint32 delay_since_last_sr;
+};
+
+struct RtcpDlrrReportBlock {
+ RtcpDlrrReportBlock();
+ ~RtcpDlrrReportBlock();
+ uint32 last_rr;
+ uint32 delay_since_last_rr;
+};
+
+// This is only needed because IPC messages don't support more than
+// 5 arguments.
+struct SendRtcpFromRtpSenderData {
+ SendRtcpFromRtpSenderData();
+ ~SendRtcpFromRtpSenderData();
+ uint32 packet_type_flags;
+ uint32 sending_ssrc;
+ std::string c_name;
+ uint32 ntp_seconds;
+ uint32 ntp_fraction;
+ uint32 rtp_timestamp;
+};
+
+inline bool operator==(RtcpSenderInfo lhs, RtcpSenderInfo rhs) {
+ return lhs.ntp_seconds == rhs.ntp_seconds &&
+ lhs.ntp_fraction == rhs.ntp_fraction &&
+ lhs.rtp_timestamp == rhs.rtp_timestamp &&
+ lhs.send_packet_count == rhs.send_packet_count &&
+ lhs.send_octet_count == rhs.send_octet_count;
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_CONFIG_H_
diff --git a/chromium/media/cast/transport/cast_transport_defines.h b/chromium/media/cast/transport/cast_transport_defines.h
new file mode 100644
index 00000000000..a34f7c539ab
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_defines.h
@@ -0,0 +1,169 @@
+// 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_TRANSPORT_CAST_TRANSPORT_DEFINES_H_
+#define MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_DEFINES_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+// TODO(mikhal): Implement and add more types.
+enum CastTransportStatus {
+ TRANSPORT_AUDIO_UNINITIALIZED = 0,
+ TRANSPORT_VIDEO_UNINITIALIZED,
+ TRANSPORT_AUDIO_INITIALIZED,
+ TRANSPORT_VIDEO_INITIALIZED,
+ TRANSPORT_INVALID_CRYPTO_CONFIG,
+ TRANSPORT_SOCKET_ERROR,
+ CAST_TRANSPORT_STATUS_LAST = TRANSPORT_SOCKET_ERROR
+};
+
+const size_t kMaxIpPacketSize = 1500;
+// Each uint16 represents one packet id within a cast frame.
+typedef std::set<uint16> PacketIdSet;
+// Each uint8 represents one cast frame.
+typedef std::map<uint8, PacketIdSet> MissingFramesAndPacketsMap;
+
+// Crypto.
+const size_t kAesBlockSize = 16;
+const size_t kAesKeySize = 16;
+
+inline std::string GetAesNonce(uint32 frame_id, const std::string& iv_mask) {
+ std::string aes_nonce(kAesBlockSize, 0);
+
+ // Serializing frame_id in big-endian order (aes_nonce[8] is the most
+ // significant byte of frame_id).
+ aes_nonce[11] = frame_id & 0xff;
+ aes_nonce[10] = (frame_id >> 8) & 0xff;
+ aes_nonce[9] = (frame_id >> 16) & 0xff;
+ aes_nonce[8] = (frame_id >> 24) & 0xff;
+
+ for (size_t i = 0; i < kAesBlockSize; ++i) {
+ aes_nonce[i] ^= iv_mask[i];
+ }
+ return aes_nonce;
+}
+
+// Rtcp defines.
+
+enum RtcpPacketFields {
+ kPacketTypeLow = 194, // SMPTE time-code mapping.
+ kPacketTypeInterArrivalJitterReport = 195,
+ kPacketTypeSenderReport = 200,
+ kPacketTypeReceiverReport = 201,
+ kPacketTypeSdes = 202,
+ kPacketTypeBye = 203,
+ kPacketTypeApplicationDefined = 204,
+ kPacketTypeGenericRtpFeedback = 205,
+ kPacketTypePayloadSpecific = 206,
+ kPacketTypeXr = 207,
+ kPacketTypeHigh = 210, // Port Mapping.
+};
+
+enum RtcpPacketField {
+ kRtcpSr = 0x0002,
+ kRtcpRr = 0x0004,
+ kRtcpBye = 0x0008,
+ kRtcpPli = 0x0010,
+ kRtcpNack = 0x0020,
+ kRtcpFir = 0x0040,
+ kRtcpSrReq = 0x0200,
+ kRtcpDlrr = 0x0400,
+ kRtcpRrtr = 0x0800,
+ kRtcpRpsi = 0x8000,
+ kRtcpRemb = 0x10000,
+ kRtcpCast = 0x20000,
+ kRtcpSenderLog = 0x40000,
+ kRtcpReceiverLog = 0x80000,
+ };
+
+// Each uint16 represents one packet id within a cast frame.
+typedef std::set<uint16> PacketIdSet;
+// Each uint8 represents one cast frame.
+typedef std::map<uint8, PacketIdSet> MissingFramesAndPacketsMap;
+
+// TODO(miu): UGLY IN-LINE DEFINITION IN HEADER FILE! Move to appropriate
+// location, separated into .h and .cc files.
+class FrameIdWrapHelper {
+ public:
+ FrameIdWrapHelper()
+ : first_(true), frame_id_wrap_count_(0), range_(kLowRange) {}
+
+ uint32 MapTo32bitsFrameId(const uint8 over_the_wire_frame_id) {
+ if (first_) {
+ first_ = false;
+ if (over_the_wire_frame_id == 0xff) {
+ // Special case for startup.
+ return kStartFrameId;
+ }
+ }
+
+ uint32 wrap_count = frame_id_wrap_count_;
+ switch (range_) {
+ case kLowRange:
+ if (over_the_wire_frame_id > kLowRangeThreshold &&
+ over_the_wire_frame_id < kHighRangeThreshold) {
+ range_ = kMiddleRange;
+ }
+ if (over_the_wire_frame_id >= kHighRangeThreshold) {
+ // Wrap count was incremented in High->Low transition, but this frame
+ // is 'old', actually from before the wrap count got incremented.
+ --wrap_count;
+ }
+ break;
+ case kMiddleRange:
+ if (over_the_wire_frame_id >= kHighRangeThreshold) {
+ range_ = kHighRange;
+ }
+ break;
+ case kHighRange:
+ if (over_the_wire_frame_id <= kLowRangeThreshold) {
+ // Wrap-around detected.
+ range_ = kLowRange;
+ ++frame_id_wrap_count_;
+ // Frame triggering wrap-around so wrap count should be incremented as
+ // as well to match |frame_id_wrap_count_|.
+ ++wrap_count;
+ }
+ break;
+ }
+ return (wrap_count << 8) + over_the_wire_frame_id;
+ }
+
+ private:
+ enum Range { kLowRange, kMiddleRange, kHighRange, };
+
+ static const uint8 kLowRangeThreshold = 63;
+ static const uint8 kHighRangeThreshold = 192;
+ static const uint32 kStartFrameId = UINT32_C(0xffffffff);
+
+ bool first_;
+ uint32 frame_id_wrap_count_;
+ Range range_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameIdWrapHelper);
+};
+
+inline uint32 GetVideoRtpTimestamp(const base::TimeTicks& time_ticks) {
+ base::TimeTicks zero_time;
+ base::TimeDelta recorded_delta = time_ticks - zero_time;
+ // Timestamp is in 90 KHz for video.
+ return static_cast<uint32>(recorded_delta.InMilliseconds() * 90);
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_DEFINES_H_
diff --git a/chromium/media/cast/transport/cast_transport_sender.h b/chromium/media/cast/transport/cast_transport_sender.h
new file mode 100644
index 00000000000..e88f2f4f098
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_sender.h
@@ -0,0 +1,113 @@
+// 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.
+
+// This is the main interface for the cast transport sender. It accepts encoded
+// frames (both audio and video), encrypts their encoded data, packetizes them
+// and feeds them into a transport (e.g., UDP).
+
+// Construction of the Cast Sender and the Cast Transport Sender should be done
+// in the following order:
+// 1. Create CastTransportSender.
+// 2. Create CastSender (accepts CastTransportSender as an input).
+// 3. Call CastTransportSender::SetPacketReceiver to ensure that the packets
+// received by the CastTransportSender will be sent to the CastSender.
+// Steps 3 can be done interchangeably.
+
+// Destruction: The CastTransportSender is assumed to be valid as long as the
+// CastSender is alive. Therefore the CastSender should be destructed before the
+// CastTransportSender.
+// This also works when the CastSender acts as a receiver for the RTCP packets
+// due to the weak pointers in the ReceivedPacket method in cast_sender_impl.cc.
+
+#ifndef MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_SENDER_H_
+#define MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_SENDER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_defines.h"
+
+namespace net {
+class NetLog;
+} // namespace net
+
+namespace media {
+namespace cast {
+namespace transport {
+
+// Following the initialization of either audio or video an initialization
+// status will be sent via this callback.
+typedef base::Callback<void(CastTransportStatus status)>
+ CastTransportStatusCallback;
+
+typedef base::Callback<void(const std::vector<PacketEvent>&)>
+ BulkRawEventsCallback;
+
+// The application should only trigger this class from the transport thread.
+class CastTransportSender : public base::NonThreadSafe {
+ public:
+ static scoped_ptr<CastTransportSender> Create(
+ net::NetLog* net_log,
+ base::TickClock* clock,
+ const net::IPEndPoint& remote_end_point,
+ const CastTransportStatusCallback& status_callback,
+ const BulkRawEventsCallback& raw_events_callback,
+ base::TimeDelta raw_events_callback_interval,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner);
+
+ virtual ~CastTransportSender() {}
+
+ // Audio/Video initialization.
+ // Encoded frames cannot be transmitted until the relevant initialize method
+ // is called. Usually called by CastSender.
+ virtual void InitializeAudio(const CastTransportAudioConfig& config) = 0;
+
+ virtual void InitializeVideo(const CastTransportVideoConfig& config) = 0;
+
+ // Sets the Cast packet receiver. Should be called after creation on the
+ // Cast sender. Packets won't be received until this function is called.
+ virtual void SetPacketReceiver(
+ const PacketReceiverCallback& packet_receiver) = 0;
+
+ // The following two functions handle the encoded media frames (audio and
+ // video) to be processed.
+ // Frames will be encrypted, packetized and transmitted to the network.
+ virtual void InsertCodedAudioFrame(const EncodedFrame& audio_frame) = 0;
+ virtual void InsertCodedVideoFrame(const EncodedFrame& video_frame) = 0;
+
+ // Builds an RTCP packet and sends it to the network.
+ // |ntp_seconds|, |ntp_fraction| and |rtp_timestamp| are used in the
+ // RTCP Sender Report.
+ virtual void SendRtcpFromRtpSender(uint32 packet_type_flags,
+ uint32 ntp_seconds,
+ uint32 ntp_fraction,
+ uint32 rtp_timestamp,
+ const RtcpDlrrReportBlock& dlrr,
+ uint32 sending_ssrc,
+ const std::string& c_name) = 0;
+
+ // Retransmission request.
+ // |missing_packets| includes the list of frames and packets in each
+ // frame to be re-transmitted.
+ // If |cancel_rtx_if_not_in_list| is used as an optimization to cancel
+ // pending re-transmission requests of packets not listed in
+ // |missing_packets|. If the requested packet(s) were sent recently
+ // (how long is specified by |dedupe_window|) then this re-transmit
+ // will be ignored.
+ virtual void ResendPackets(
+ bool is_audio,
+ const MissingFramesAndPacketsMap& missing_packets,
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) = 0;
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_SENDER_H_
diff --git a/chromium/media/cast/transport/cast_transport_sender_impl.cc b/chromium/media/cast/transport/cast_transport_sender_impl.cc
new file mode 100644
index 00000000000..6fd848f27bf
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_sender_impl.cc
@@ -0,0 +1,212 @@
+// 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/transport/cast_transport_sender_impl.h"
+
+#include "base/single_thread_task_runner.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "net/base/net_util.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+scoped_ptr<CastTransportSender> CastTransportSender::Create(
+ net::NetLog* net_log,
+ base::TickClock* clock,
+ const net::IPEndPoint& remote_end_point,
+ const CastTransportStatusCallback& status_callback,
+ const BulkRawEventsCallback& raw_events_callback,
+ base::TimeDelta raw_events_callback_interval,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner) {
+ return scoped_ptr<CastTransportSender>(
+ new CastTransportSenderImpl(net_log,
+ clock,
+ remote_end_point,
+ status_callback,
+ raw_events_callback,
+ raw_events_callback_interval,
+ transport_task_runner.get(),
+ NULL));
+}
+
+CastTransportSenderImpl::CastTransportSenderImpl(
+ net::NetLog* net_log,
+ base::TickClock* clock,
+ const net::IPEndPoint& remote_end_point,
+ const CastTransportStatusCallback& status_callback,
+ const BulkRawEventsCallback& raw_events_callback,
+ base::TimeDelta raw_events_callback_interval,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner,
+ PacketSender* external_transport)
+ : clock_(clock),
+ status_callback_(status_callback),
+ transport_task_runner_(transport_task_runner),
+ transport_(external_transport ? NULL
+ : new UdpTransport(net_log,
+ transport_task_runner,
+ net::IPEndPoint(),
+ remote_end_point,
+ status_callback)),
+ logging_(),
+ pacer_(clock,
+ &logging_,
+ external_transport ? external_transport : transport_.get(),
+ transport_task_runner),
+ rtcp_builder_(&pacer_),
+ raw_events_callback_(raw_events_callback) {
+ DCHECK(clock_);
+ if (!raw_events_callback_.is_null()) {
+ DCHECK(raw_events_callback_interval > base::TimeDelta());
+ event_subscriber_.reset(new SimpleEventSubscriber);
+ logging_.AddRawEventSubscriber(event_subscriber_.get());
+ raw_events_timer_.Start(FROM_HERE,
+ raw_events_callback_interval,
+ this,
+ &CastTransportSenderImpl::SendRawEvents);
+ }
+ if (transport_) {
+ // The default DSCP value for cast is AF41. Which gives it a higher
+ // priority over other traffic.
+ transport_->SetDscp(net::DSCP_AF41);
+ }
+}
+
+CastTransportSenderImpl::~CastTransportSenderImpl() {
+ if (event_subscriber_.get())
+ logging_.RemoveRawEventSubscriber(event_subscriber_.get());
+}
+
+void CastTransportSenderImpl::InitializeAudio(
+ const CastTransportAudioConfig& config) {
+ LOG_IF(WARNING, config.rtp.config.aes_key.empty() ||
+ config.rtp.config.aes_iv_mask.empty())
+ << "Unsafe to send audio with encryption DISABLED.";
+ if (!audio_encryptor_.Initialize(config.rtp.config.aes_key,
+ config.rtp.config.aes_iv_mask)) {
+ status_callback_.Run(TRANSPORT_AUDIO_UNINITIALIZED);
+ return;
+ }
+ audio_sender_.reset(new RtpSender(clock_, transport_task_runner_, &pacer_));
+ if (audio_sender_->InitializeAudio(config)) {
+ pacer_.RegisterAudioSsrc(config.rtp.config.ssrc);
+ status_callback_.Run(TRANSPORT_AUDIO_INITIALIZED);
+ } else {
+ audio_sender_.reset();
+ status_callback_.Run(TRANSPORT_AUDIO_UNINITIALIZED);
+ }
+}
+
+void CastTransportSenderImpl::InitializeVideo(
+ const CastTransportVideoConfig& config) {
+ LOG_IF(WARNING, config.rtp.config.aes_key.empty() ||
+ config.rtp.config.aes_iv_mask.empty())
+ << "Unsafe to send video with encryption DISABLED.";
+ if (!video_encryptor_.Initialize(config.rtp.config.aes_key,
+ config.rtp.config.aes_iv_mask)) {
+ status_callback_.Run(TRANSPORT_VIDEO_UNINITIALIZED);
+ return;
+ }
+ video_sender_.reset(new RtpSender(clock_, transport_task_runner_, &pacer_));
+ if (video_sender_->InitializeVideo(config)) {
+ pacer_.RegisterVideoSsrc(config.rtp.config.ssrc);
+ status_callback_.Run(TRANSPORT_VIDEO_INITIALIZED);
+ } else {
+ video_sender_.reset();
+ status_callback_.Run(TRANSPORT_VIDEO_UNINITIALIZED);
+ }
+}
+
+void CastTransportSenderImpl::SetPacketReceiver(
+ const PacketReceiverCallback& packet_receiver) {
+ transport_->StartReceiving(packet_receiver);
+}
+
+namespace {
+void EncryptAndSendFrame(const EncodedFrame& frame,
+ TransportEncryptionHandler* encryptor,
+ RtpSender* sender) {
+ if (encryptor->initialized()) {
+ EncodedFrame encrypted_frame;
+ frame.CopyMetadataTo(&encrypted_frame);
+ if (encryptor->Encrypt(frame.frame_id, frame.data, &encrypted_frame.data)) {
+ sender->SendFrame(encrypted_frame);
+ } else {
+ LOG(ERROR) << "Encryption failed. Not sending frame with ID "
+ << frame.frame_id;
+ }
+ } else {
+ sender->SendFrame(frame);
+ }
+}
+} // namespace
+
+void CastTransportSenderImpl::InsertCodedAudioFrame(
+ const EncodedFrame& audio_frame) {
+ DCHECK(audio_sender_) << "Audio sender uninitialized";
+ EncryptAndSendFrame(audio_frame, &audio_encryptor_, audio_sender_.get());
+}
+
+void CastTransportSenderImpl::InsertCodedVideoFrame(
+ const EncodedFrame& video_frame) {
+ DCHECK(video_sender_) << "Video sender uninitialized";
+ EncryptAndSendFrame(video_frame, &video_encryptor_, video_sender_.get());
+}
+
+void CastTransportSenderImpl::SendRtcpFromRtpSender(
+ uint32 packet_type_flags,
+ uint32 ntp_seconds,
+ uint32 ntp_fraction,
+ uint32 rtp_timestamp,
+ const RtcpDlrrReportBlock& dlrr,
+ uint32 sending_ssrc,
+ const std::string& c_name) {
+ RtcpSenderInfo sender_info;
+ sender_info.ntp_seconds = ntp_seconds;
+ sender_info.ntp_fraction = ntp_fraction;
+ sender_info.rtp_timestamp = rtp_timestamp;
+ if (audio_sender_ && audio_sender_->ssrc() == sending_ssrc) {
+ sender_info.send_packet_count = audio_sender_->send_packet_count();
+ sender_info.send_octet_count = audio_sender_->send_octet_count();
+ } else if (video_sender_ && video_sender_->ssrc() == sending_ssrc) {
+ sender_info.send_packet_count = video_sender_->send_packet_count();
+ sender_info.send_octet_count = video_sender_->send_octet_count();
+ } else {
+ LOG(ERROR) << "Sending RTCP with an invalid SSRC.";
+ return;
+ }
+ rtcp_builder_.SendRtcpFromRtpSender(
+ packet_type_flags, sender_info, dlrr, sending_ssrc, c_name);
+}
+
+void CastTransportSenderImpl::ResendPackets(
+ bool is_audio,
+ const MissingFramesAndPacketsMap& missing_packets,
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) {
+ if (is_audio) {
+ DCHECK(audio_sender_) << "Audio sender uninitialized";
+ audio_sender_->ResendPackets(missing_packets,
+ cancel_rtx_if_not_in_list,
+ dedupe_window);
+ } else {
+ DCHECK(video_sender_) << "Video sender uninitialized";
+ video_sender_->ResendPackets(missing_packets,
+ cancel_rtx_if_not_in_list,
+ dedupe_window);
+ }
+}
+
+void CastTransportSenderImpl::SendRawEvents() {
+ DCHECK(event_subscriber_.get());
+ DCHECK(!raw_events_callback_.is_null());
+ std::vector<PacketEvent> packet_events;
+ event_subscriber_->GetPacketEventsAndReset(&packet_events);
+ raw_events_callback_.Run(packet_events);
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/cast_transport_sender_impl.h b/chromium/media/cast/transport/cast_transport_sender_impl.h
new file mode 100644
index 00000000000..035ef844b68
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_sender_impl.h
@@ -0,0 +1,110 @@
+// 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_TRANSPORT_CAST_TRANSPORT_IMPL_H_
+#define MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_IMPL_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_sender.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+#include "media/cast/transport/rtcp/rtcp_builder.h"
+#include "media/cast/transport/rtp_sender/rtp_sender.h"
+#include "media/cast/transport/utility/transport_encryption_handler.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+class CastTransportSenderImpl : public CastTransportSender {
+ public:
+ // external_transport is only used for testing.
+ // Note that SetPacketReceiver does not work if an external
+ // transport is provided.
+ // |raw_events_callback|: Raw events will be returned on this callback
+ // which will be invoked every |raw_events_callback_interval|.
+ // This can be a null callback, i.e. if user is not interested in raw events.
+ // |raw_events_callback_interval|: This can be |base::TimeDelta()| if
+ // |raw_events_callback| is a null callback.
+ CastTransportSenderImpl(
+ net::NetLog* net_log,
+ base::TickClock* clock,
+ const net::IPEndPoint& remote_end_point,
+ const CastTransportStatusCallback& status_callback,
+ const BulkRawEventsCallback& raw_events_callback,
+ base::TimeDelta raw_events_callback_interval,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner,
+ PacketSender* external_transport);
+
+ virtual ~CastTransportSenderImpl();
+
+ virtual void InitializeAudio(const CastTransportAudioConfig& config) OVERRIDE;
+
+ virtual void InitializeVideo(const CastTransportVideoConfig& config) OVERRIDE;
+
+ // CastTransportSender implementation.
+ virtual void SetPacketReceiver(const PacketReceiverCallback& packet_receiver)
+ OVERRIDE;
+
+ virtual void InsertCodedAudioFrame(const EncodedFrame& audio_frame) OVERRIDE;
+ virtual void InsertCodedVideoFrame(const EncodedFrame& video_frame) OVERRIDE;
+
+ virtual void SendRtcpFromRtpSender(uint32 packet_type_flags,
+ uint32 ntp_seconds,
+ uint32 ntp_fraction,
+ uint32 rtp_timestamp,
+ const RtcpDlrrReportBlock& dlrr,
+ uint32 sending_ssrc,
+ const std::string& c_name) OVERRIDE;
+
+ virtual void ResendPackets(bool is_audio,
+ const MissingFramesAndPacketsMap& missing_packets,
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window)
+ OVERRIDE;
+
+ private:
+ // If |raw_events_callback_| is non-null, calls it with events collected
+ // by |event_subscriber_| since last call.
+ void SendRawEvents();
+
+ base::TickClock* clock_; // Not owned by this class.
+ CastTransportStatusCallback status_callback_;
+ scoped_refptr<base::SingleThreadTaskRunner> transport_task_runner_;
+
+ scoped_ptr<UdpTransport> transport_;
+ LoggingImpl logging_;
+ PacedSender pacer_;
+ RtcpBuilder rtcp_builder_;
+ scoped_ptr<RtpSender> audio_sender_;
+ scoped_ptr<RtpSender> video_sender_;
+
+ // Encrypts data in EncodedFrames before they are sent. Note that it's
+ // important for the encryption to happen here, in code that would execute in
+ // the main browser process, for security reasons. This helps to mitigate
+ // the damage that could be caused by a compromised renderer process.
+ TransportEncryptionHandler audio_encryptor_;
+ TransportEncryptionHandler video_encryptor_;
+
+ // This is non-null iff |raw_events_callback_| is non-null.
+ scoped_ptr<SimpleEventSubscriber> event_subscriber_;
+ base::RepeatingTimer<CastTransportSenderImpl> raw_events_timer_;
+
+ BulkRawEventsCallback raw_events_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CastTransportSenderImpl);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_CAST_TRANSPORT_IMPL_H_
diff --git a/chromium/media/cast/transport/cast_transport_sender_impl_unittest.cc b/chromium/media/cast/transport/cast_transport_sender_impl_unittest.cc
new file mode 100644
index 00000000000..67eb39a47aa
--- /dev/null
+++ b/chromium/media/cast/transport/cast_transport_sender_impl_unittest.cc
@@ -0,0 +1,113 @@
+// 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 <gtest/gtest.h>
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_sender_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+static const int64 kStartMillisecond = INT64_C(12345678900000);
+
+class FakePacketSender : public transport::PacketSender {
+ public:
+ FakePacketSender() {}
+
+ virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE {
+ return true;
+ }
+};
+
+class CastTransportSenderImplTest : public ::testing::Test {
+ protected:
+ CastTransportSenderImplTest()
+ : num_times_callback_called_(0) {
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ task_runner_ = new test::FakeSingleThreadTaskRunner(&testing_clock_);
+ }
+
+ virtual ~CastTransportSenderImplTest() {}
+
+ void InitWithoutLogging() {
+ transport_sender_.reset(
+ new CastTransportSenderImpl(NULL,
+ &testing_clock_,
+ net::IPEndPoint(),
+ base::Bind(&UpdateCastTransportStatus),
+ BulkRawEventsCallback(),
+ base::TimeDelta(),
+ task_runner_,
+ &transport_));
+ task_runner_->RunTasks();
+ }
+
+ void InitWithLogging() {
+ transport_sender_.reset(new CastTransportSenderImpl(
+ NULL,
+ &testing_clock_,
+ net::IPEndPoint(),
+ base::Bind(&UpdateCastTransportStatus),
+ base::Bind(&CastTransportSenderImplTest::LogRawEvents,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(10),
+ task_runner_,
+ &transport_));
+ task_runner_->RunTasks();
+ }
+
+ void LogRawEvents(const std::vector<PacketEvent>& packet_events) {
+ num_times_callback_called_++;
+ if (num_times_callback_called_ == 3) {
+ run_loop_.Quit();
+ }
+ }
+
+ static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
+ }
+
+ base::SimpleTestTickClock testing_clock_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_ptr<CastTransportSenderImpl> transport_sender_;
+ FakePacketSender transport_;
+ base::MessageLoopForIO message_loop_;
+ base::RunLoop run_loop_;
+ int num_times_callback_called_;
+};
+
+TEST_F(CastTransportSenderImplTest, InitWithoutLogging) {
+ InitWithoutLogging();
+ message_loop_.PostDelayedTask(FROM_HERE,
+ run_loop_.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(50));
+ run_loop_.Run();
+ EXPECT_EQ(0, num_times_callback_called_);
+}
+
+TEST_F(CastTransportSenderImplTest, InitWithLogging) {
+ InitWithLogging();
+ message_loop_.PostDelayedTask(FROM_HERE,
+ run_loop_.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(50));
+ run_loop_.Run();
+ EXPECT_GT(num_times_callback_called_, 1);
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/net/frame_id_wrap_helper_test.cc b/chromium/media/cast/transport/frame_id_wrap_helper_test.cc
index f6b89b01d22..3a2060d3aaf 100644
--- a/chromium/media/cast/net/frame_id_wrap_helper_test.cc
+++ b/chromium/media/cast/transport/frame_id_wrap_helper_test.cc
@@ -3,10 +3,11 @@
// found in the LICENSE file.
#include <gtest/gtest.h>
-#include "media/cast/net/cast_net_defines.h"
+#include "media/cast/transport/cast_transport_defines.h"
namespace media {
namespace cast {
+namespace transport {
class FrameIdWrapHelperTest : public ::testing::Test {
protected:
@@ -14,6 +15,8 @@ class FrameIdWrapHelperTest : public ::testing::Test {
virtual ~FrameIdWrapHelperTest() {}
FrameIdWrapHelper frame_id_wrap_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameIdWrapHelperTest);
};
TEST_F(FrameIdWrapHelperTest, FirstFrame) {
@@ -23,8 +26,8 @@ TEST_F(FrameIdWrapHelperTest, FirstFrame) {
TEST_F(FrameIdWrapHelperTest, Rollover) {
uint32 new_frame_id = 0u;
for (int i = 0; i <= 256; ++i) {
- new_frame_id = frame_id_wrap_helper_.MapTo32bitsFrameId(
- static_cast<uint8>(i));
+ new_frame_id =
+ frame_id_wrap_helper_.MapTo32bitsFrameId(static_cast<uint8>(i));
}
EXPECT_EQ(256u, new_frame_id);
}
@@ -32,8 +35,8 @@ TEST_F(FrameIdWrapHelperTest, Rollover) {
TEST_F(FrameIdWrapHelperTest, OutOfOrder) {
uint32 new_frame_id = 0u;
for (int i = 0; i < 255; ++i) {
- new_frame_id = frame_id_wrap_helper_.MapTo32bitsFrameId(
- static_cast<uint8>(i));
+ new_frame_id =
+ frame_id_wrap_helper_.MapTo32bitsFrameId(static_cast<uint8>(i));
}
EXPECT_EQ(254u, new_frame_id);
new_frame_id = frame_id_wrap_helper_.MapTo32bitsFrameId(0u);
@@ -44,5 +47,6 @@ TEST_F(FrameIdWrapHelperTest, OutOfOrder) {
EXPECT_EQ(257u, new_frame_id);
}
+} // namespace transport
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/net/pacing/mock_paced_packet_sender.cc b/chromium/media/cast/transport/pacing/mock_paced_packet_sender.cc
index 6caf8f6390e..5e325f02335 100644
--- a/chromium/media/cast/net/pacing/mock_paced_packet_sender.cc
+++ b/chromium/media/cast/transport/pacing/mock_paced_packet_sender.cc
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/cast/net/pacing/mock_paced_packet_sender.h"
+#include "media/cast/transport/pacing/mock_paced_packet_sender.h"
namespace media {
namespace cast {
+namespace transport {
-MockPacedPacketSender::MockPacedPacketSender() {
-}
+MockPacedPacketSender::MockPacedPacketSender() {}
-MockPacedPacketSender::~MockPacedPacketSender() {
-}
+MockPacedPacketSender::~MockPacedPacketSender() {}
+} // namespace transport
} // namespace cast
} // namespace media
diff --git a/chromium/media/cast/transport/pacing/mock_paced_packet_sender.h b/chromium/media/cast/transport/pacing/mock_paced_packet_sender.h
new file mode 100644
index 00000000000..20b76470351
--- /dev/null
+++ b/chromium/media/cast/transport/pacing/mock_paced_packet_sender.h
@@ -0,0 +1,31 @@
+// 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_TRANSPORT_PACING_MOCK_PACED_PACKET_SENDER_H_
+#define MEDIA_CAST_TRANSPORT_PACING_MOCK_PACED_PACKET_SENDER_H_
+
+#include "media/cast/transport/pacing/paced_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+class MockPacedPacketSender : public PacedPacketSender {
+ public:
+ MockPacedPacketSender();
+ virtual ~MockPacedPacketSender();
+
+ MOCK_METHOD1(SendPackets, bool(const SendPacketVector& packets));
+ MOCK_METHOD2(ResendPackets, bool(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window));
+ MOCK_METHOD2(SendRtcpPacket, bool(unsigned int ssrc, PacketRef packet));
+ MOCK_METHOD1(CancelSendingPacket, void(const PacketKey& packet_key));
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_PACING_MOCK_PACED_PACKET_SENDER_H_
diff --git a/chromium/media/cast/transport/pacing/paced_sender.cc b/chromium/media/cast/transport/pacing/paced_sender.cc
new file mode 100644
index 00000000000..20cbde85be9
--- /dev/null
+++ b/chromium/media/cast/transport/pacing/paced_sender.cc
@@ -0,0 +1,260 @@
+// 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/transport/pacing/paced_sender.h"
+
+#include "base/big_endian.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+namespace {
+
+static const int64 kPacingIntervalMs = 10;
+// Each frame will be split into no more than kPacingMaxBurstsPerFrame
+// bursts of packets.
+static const size_t kPacingMaxBurstsPerFrame = 3;
+static const size_t kTargetBurstSize = 10;
+static const size_t kMaxBurstSize = 20;
+static const size_t kMaxDedupeWindowMs = 500;
+
+} // namespace
+
+// static
+PacketKey PacedPacketSender::MakePacketKey(const base::TimeTicks& ticks,
+ uint32 ssrc,
+ uint16 packet_id) {
+ return std::make_pair(ticks, std::make_pair(ssrc, packet_id));
+}
+
+PacedSender::PacedSender(
+ base::TickClock* clock,
+ LoggingImpl* logging,
+ PacketSender* transport,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner)
+ : clock_(clock),
+ logging_(logging),
+ transport_(transport),
+ transport_task_runner_(transport_task_runner),
+ audio_ssrc_(0),
+ video_ssrc_(0),
+ max_burst_size_(kTargetBurstSize),
+ next_max_burst_size_(kTargetBurstSize),
+ next_next_max_burst_size_(kTargetBurstSize),
+ current_burst_size_(0),
+ state_(State_Unblocked),
+ weak_factory_(this) {
+}
+
+PacedSender::~PacedSender() {}
+
+void PacedSender::RegisterAudioSsrc(uint32 audio_ssrc) {
+ audio_ssrc_ = audio_ssrc;
+}
+
+void PacedSender::RegisterVideoSsrc(uint32 video_ssrc) {
+ video_ssrc_ = video_ssrc;
+}
+
+bool PacedSender::SendPackets(const SendPacketVector& packets) {
+ if (packets.empty()) {
+ return true;
+ }
+ for (size_t i = 0; i < packets.size(); i++) {
+ packet_list_[packets[i].first] =
+ make_pair(PacketType_Normal, packets[i].second);
+ }
+ if (state_ == State_Unblocked) {
+ SendStoredPackets();
+ }
+ return true;
+}
+
+bool PacedSender::ResendPackets(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window) {
+ if (packets.empty()) {
+ return true;
+ }
+ base::TimeTicks now = clock_->NowTicks();
+ for (size_t i = 0; i < packets.size(); i++) {
+ std::map<PacketKey, base::TimeTicks>::const_iterator j =
+ sent_time_.find(packets[i].first);
+
+ if (j != sent_time_.end() && now - j->second < dedupe_window) {
+ LogPacketEvent(packets[i].second->data, PACKET_RTX_REJECTED);
+ continue;
+ }
+
+ packet_list_[packets[i].first] =
+ make_pair(PacketType_Resend, packets[i].second);
+ }
+ if (state_ == State_Unblocked) {
+ SendStoredPackets();
+ }
+ return true;
+}
+
+bool PacedSender::SendRtcpPacket(uint32 ssrc, PacketRef packet) {
+ if (state_ == State_TransportBlocked) {
+ packet_list_[PacedPacketSender::MakePacketKey(base::TimeTicks(), ssrc, 0)] =
+ make_pair(PacketType_RTCP, packet);
+ } else {
+ // We pass the RTCP packets straight through.
+ if (!transport_->SendPacket(
+ packet,
+ base::Bind(&PacedSender::SendStoredPackets,
+ weak_factory_.GetWeakPtr()))) {
+ state_ = State_TransportBlocked;
+ }
+
+ }
+ return true;
+}
+
+void PacedSender::CancelSendingPacket(const PacketKey& packet_key) {
+ packet_list_.erase(packet_key);
+}
+
+PacketRef PacedSender::GetNextPacket(PacketType* packet_type,
+ PacketKey* packet_key) {
+ std::map<PacketKey, std::pair<PacketType, PacketRef> >::iterator i;
+ i = packet_list_.begin();
+ DCHECK(i != packet_list_.end());
+ *packet_type = i->second.first;
+ *packet_key = i->first;
+ PacketRef ret = i->second.second;
+ packet_list_.erase(i);
+ return ret;
+}
+
+bool PacedSender::empty() const {
+ return packet_list_.empty();
+}
+
+size_t PacedSender::size() const {
+ return packet_list_.size();
+}
+
+// This function can be called from three places:
+// 1. User called one of the Send* functions and we were in an unblocked state.
+// 2. state_ == State_TransportBlocked and the transport is calling us to
+// let us know that it's ok to send again.
+// 3. state_ == State_BurstFull and there are still packets to send. In this
+// case we called PostDelayedTask on this function to start a new burst.
+void PacedSender::SendStoredPackets() {
+ State previous_state = state_;
+ state_ = State_Unblocked;
+ if (empty()) {
+ return;
+ }
+
+ base::TimeTicks now = clock_->NowTicks();
+ // I don't actually trust that PostDelayTask(x - now) will mean that
+ // now >= x when the call happens, so check if the previous state was
+ // State_BurstFull too.
+ if (now >= burst_end_ || previous_state == State_BurstFull) {
+ // Start a new burst.
+ current_burst_size_ = 0;
+ burst_end_ = now + base::TimeDelta::FromMilliseconds(kPacingIntervalMs);
+
+ // The goal here is to try to send out the queued packets over the next
+ // three bursts, while trying to keep the burst size below 10 if possible.
+ // We have some evidence that sending more than 12 packets in a row doesn't
+ // work very well, but we don't actually know why yet. Sending out packets
+ // sooner is better than sending out packets later as that gives us more
+ // time to re-send them if needed. So if we have less than 30 packets, just
+ // send 10 at a time. If we have less than 60 packets, send n / 3 at a time.
+ // if we have more than 60, we send 20 at a time. 20 packets is ~24Mbit/s
+ // which is more bandwidth than the cast library should need, and sending
+ // out more data per second is unlikely to be helpful.
+ size_t max_burst_size = std::min(
+ kMaxBurstSize,
+ std::max(kTargetBurstSize, size() / kPacingMaxBurstsPerFrame));
+
+ // If the queue is long, issue a warning. Try to limit the number of
+ // warnings issued by only issuing the warning when the burst size
+ // grows. Otherwise we might get 100 warnings per second.
+ if (max_burst_size > next_next_max_burst_size_ && size() > 100) {
+ LOG(WARNING) << "Packet queue is very long:" << size();
+ }
+
+ max_burst_size_ = std::max(next_max_burst_size_, max_burst_size);
+ next_max_burst_size_ = std::max(next_next_max_burst_size_, max_burst_size);
+ next_next_max_burst_size_ = max_burst_size;
+ }
+
+ base::Closure cb = base::Bind(&PacedSender::SendStoredPackets,
+ weak_factory_.GetWeakPtr());
+ while (!empty()) {
+ if (current_burst_size_ >= max_burst_size_) {
+ transport_task_runner_->PostDelayedTask(FROM_HERE,
+ cb,
+ burst_end_ - now);
+ state_ = State_BurstFull;
+ return;
+ }
+ PacketType packet_type;
+ PacketKey packet_key;
+ PacketRef packet = GetNextPacket(&packet_type, &packet_key);
+ sent_time_[packet_key] = now;
+ sent_time_buffer_[packet_key] = now;
+
+ switch (packet_type) {
+ case PacketType_Resend:
+ LogPacketEvent(packet->data, PACKET_RETRANSMITTED);
+ break;
+ case PacketType_Normal:
+ LogPacketEvent(packet->data, PACKET_SENT_TO_NETWORK);
+ break;
+ case PacketType_RTCP:
+ break;
+ }
+ if (!transport_->SendPacket(packet, cb)) {
+ state_ = State_TransportBlocked;
+ return;
+ }
+ current_burst_size_++;
+ }
+ // Keep ~0.5 seconds of data (1000 packets)
+ if (sent_time_buffer_.size() >=
+ kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs) {
+ sent_time_.swap(sent_time_buffer_);
+ sent_time_buffer_.clear();
+ }
+ DCHECK_LE(sent_time_buffer_.size(),
+ kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs);
+ DCHECK_LE(sent_time_.size(),
+ 2 * kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs);
+ state_ = State_Unblocked;
+}
+
+void PacedSender::LogPacketEvent(const Packet& packet, CastLoggingEvent event) {
+ // Get SSRC from packet and compare with the audio_ssrc / video_ssrc to see
+ // if the packet is audio or video.
+ DCHECK_GE(packet.size(), 12u);
+ base::BigEndianReader reader(reinterpret_cast<const char*>(&packet[8]), 4);
+ uint32 ssrc;
+ bool success = reader.ReadU32(&ssrc);
+ DCHECK(success);
+ bool is_audio;
+ if (ssrc == audio_ssrc_) {
+ is_audio = true;
+ } else if (ssrc == video_ssrc_) {
+ is_audio = false;
+ } else {
+ DVLOG(3) << "Got unknown ssrc " << ssrc << " when logging packet event";
+ return;
+ }
+
+ EventMediaType media_type = is_audio ? AUDIO_EVENT : VIDEO_EVENT;
+ logging_->InsertSinglePacketEvent(clock_->NowTicks(), event, media_type,
+ packet);
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/pacing/paced_sender.h b/chromium/media/cast/transport/pacing/paced_sender.h
new file mode 100644
index 00000000000..9fc0c8b8b85
--- /dev/null
+++ b/chromium/media/cast/transport/pacing/paced_sender.h
@@ -0,0 +1,147 @@
+// 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_TRANSPORT_PACING_PACED_SENDER_H_
+#define MEDIA_CAST_TRANSPORT_PACING_PACED_SENDER_H_
+
+#include <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/transport/udp_transport.h"
+
+namespace media {
+namespace cast {
+
+class LoggingImpl;
+
+namespace transport {
+
+// Use std::pair for free comparison operators.
+// { capture_time, ssrc, packet_id }
+// The PacketKey is designed to meet two criteria:
+// 1. When we re-send the same packet again, we can use the packet key
+// to identify it so that we can de-duplicate packets in the queue.
+// 2. The sort order of the PacketKey determines the order that packets
+// are sent out. Using the capture_time as the first member basically
+// means that older packets are sent first.
+typedef std::pair<base::TimeTicks, std::pair<uint32, uint16> > PacketKey;
+typedef std::vector<std::pair<PacketKey, PacketRef> > SendPacketVector;
+
+// We have this pure virtual class to enable mocking.
+class PacedPacketSender {
+ public:
+ virtual bool SendPackets(const SendPacketVector& packets) = 0;
+ virtual bool ResendPackets(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window) = 0;
+ virtual bool SendRtcpPacket(uint32 ssrc, PacketRef packet) = 0;
+ virtual void CancelSendingPacket(const PacketKey& packet_key) = 0;
+
+ virtual ~PacedPacketSender() {}
+
+ static PacketKey MakePacketKey(const base::TimeTicks& ticks,
+ uint32 ssrc,
+ uint16 packet_id);
+};
+
+class PacedSender : public PacedPacketSender,
+ public base::NonThreadSafe,
+ public base::SupportsWeakPtr<PacedSender> {
+ public:
+ // The |external_transport| should only be used by the Cast receiver and for
+ // testing.
+ PacedSender(
+ base::TickClock* clock,
+ LoggingImpl* logging,
+ PacketSender* external_transport,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner);
+
+ virtual ~PacedSender();
+
+ // These must be called before non-RTCP packets are sent.
+ void RegisterAudioSsrc(uint32 audio_ssrc);
+ void RegisterVideoSsrc(uint32 video_ssrc);
+
+ // PacedPacketSender implementation.
+ virtual bool SendPackets(const SendPacketVector& packets) OVERRIDE;
+ virtual bool ResendPackets(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window) OVERRIDE;
+ virtual bool SendRtcpPacket(uint32 ssrc, PacketRef packet) OVERRIDE;
+ virtual void CancelSendingPacket(const PacketKey& packet_key) OVERRIDE;
+
+ private:
+ // Actually sends the packets to the transport.
+ void SendStoredPackets();
+ void LogPacketEvent(const Packet& packet, CastLoggingEvent event);
+
+ enum PacketType {
+ PacketType_RTCP,
+ PacketType_Resend,
+ PacketType_Normal
+ };
+ enum State {
+ // In an unblocked state, we can send more packets.
+ // We have to check the current time against |burst_end_| to see if we are
+ // appending to the current burst or if we can start a new one.
+ State_Unblocked,
+ // In this state, we are waiting for a callback from the udp transport.
+ // This happens when the OS-level buffer is full. Once we receive the
+ // callback, we go to State_Unblocked and see if we can write more packets
+ // to the current burst. (Or the next burst if enough time has passed.)
+ State_TransportBlocked,
+ // Once we've written enough packets for a time slice, we go into this
+ // state and PostDelayTask a call to ourselves to wake up when we can
+ // send more data.
+ State_BurstFull
+ };
+
+ bool empty() const;
+ size_t size() const;
+
+ // Returns the next packet to send. RTCP packets have highest priority,
+ // resend packets have second highest priority and then comes everything
+ // else.
+ PacketRef GetNextPacket(PacketType* packet_type,
+ PacketKey* packet_key);
+
+ base::TickClock* const clock_; // Not owned by this class.
+ LoggingImpl* const logging_; // Not owned by this class.
+ PacketSender* transport_; // Not owned by this class.
+ scoped_refptr<base::SingleThreadTaskRunner> transport_task_runner_;
+ uint32 audio_ssrc_;
+ uint32 video_ssrc_;
+ std::map<PacketKey, std::pair<PacketType, PacketRef> > packet_list_;
+ std::map<PacketKey, base::TimeTicks> sent_time_;
+ std::map<PacketKey, base::TimeTicks> sent_time_buffer_;
+
+ // Maximum burst size for the next three bursts.
+ size_t max_burst_size_;
+ size_t next_max_burst_size_;
+ size_t next_next_max_burst_size_;
+ // Number of packets already sent in the current burst.
+ size_t current_burst_size_;
+ // This is when the current burst ends.
+ base::TimeTicks burst_end_;
+
+ State state_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<PacedSender> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PacedSender);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_PACING_PACED_SENDER_H_
diff --git a/chromium/media/cast/transport/pacing/paced_sender_unittest.cc b/chromium/media/cast/transport/pacing/paced_sender_unittest.cc
new file mode 100644
index 00000000000..5e24fca4b56
--- /dev/null
+++ b/chromium/media/cast/transport/pacing/paced_sender_unittest.cc
@@ -0,0 +1,351 @@
+// 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 <stdint.h>
+
+#include "base/big_endian.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+using testing::_;
+
+static const uint8 kValue = 123;
+static const size_t kSize1 = 100;
+static const size_t kSize2 = 101;
+static const size_t kSize3 = 102;
+static const size_t kSize4 = 103;
+static const size_t kNackSize = 104;
+static const int64 kStartMillisecond = INT64_C(12345678900000);
+static const uint32 kVideoSsrc = 0x1234;
+static const uint32 kAudioSsrc = 0x5678;
+
+class TestPacketSender : public PacketSender {
+ public:
+ TestPacketSender() {}
+
+ virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE {
+ EXPECT_FALSE(expected_packet_size_.empty());
+ size_t expected_packet_size = expected_packet_size_.front();
+ expected_packet_size_.pop_front();
+ EXPECT_EQ(expected_packet_size, packet->data.size());
+ return true;
+ }
+
+ void AddExpectedSize(int expected_packet_size, int repeat_count) {
+ for (int i = 0; i < repeat_count; ++i) {
+ expected_packet_size_.push_back(expected_packet_size);
+ }
+ }
+
+ public:
+ std::list<int> expected_packet_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
+};
+
+class PacedSenderTest : public ::testing::Test {
+ protected:
+ PacedSenderTest() {
+ logging_.AddRawEventSubscriber(&subscriber_);
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ task_runner_ = new test::FakeSingleThreadTaskRunner(&testing_clock_);
+ paced_sender_.reset(new PacedSender(
+ &testing_clock_, &logging_, &mock_transport_, task_runner_));
+ paced_sender_->RegisterAudioSsrc(kAudioSsrc);
+ paced_sender_->RegisterVideoSsrc(kVideoSsrc);
+ }
+
+ virtual ~PacedSenderTest() {
+ logging_.RemoveRawEventSubscriber(&subscriber_);
+ }
+
+ static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
+ NOTREACHED();
+ }
+
+ SendPacketVector CreateSendPacketVector(size_t packet_size,
+ int num_of_packets_in_frame,
+ bool audio) {
+ DCHECK_GE(packet_size, 12u);
+ SendPacketVector packets;
+ base::TimeTicks frame_tick = testing_clock_.NowTicks();
+ // Advance the clock so that we don't get the same frame_tick
+ // next time this function is called.
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(1));
+ for (int i = 0; i < num_of_packets_in_frame; ++i) {
+ PacketKey key = PacedPacketSender::MakePacketKey(
+ frame_tick,
+ audio ? kAudioSsrc : kVideoSsrc, // ssrc
+ i);
+
+ PacketRef packet(new base::RefCountedData<Packet>);
+ packet->data.resize(packet_size, kValue);
+ // Write ssrc to packet so that it can be recognized as a
+ // "video frame" for logging purposes.
+ base::BigEndianWriter writer(
+ reinterpret_cast<char*>(&packet->data[8]), 4);
+ bool success = writer.WriteU32(audio ? kAudioSsrc : kVideoSsrc);
+ DCHECK(success);
+ packets.push_back(std::make_pair(key, packet));
+ }
+ return packets;
+ }
+
+ // Use this function to drain the packet list in PacedSender without having
+ // to test the pacing implementation details.
+ bool RunUntilEmpty(int max_tries) {
+ for (int i = 0; i < max_tries; i++) {
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+ task_runner_->RunTasks();
+ if (mock_transport_.expected_packet_size_.empty())
+ return true;
+ i++;
+ }
+
+ return mock_transport_.expected_packet_size_.empty();
+ }
+
+ LoggingImpl logging_;
+ SimpleEventSubscriber subscriber_;
+ base::SimpleTestTickClock testing_clock_;
+ TestPacketSender mock_transport_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_ptr<PacedSender> paced_sender_;
+
+ DISALLOW_COPY_AND_ASSIGN(PacedSenderTest);
+};
+
+TEST_F(PacedSenderTest, PassThroughRtcp) {
+ mock_transport_.AddExpectedSize(kSize1, 2);
+ SendPacketVector packets = CreateSendPacketVector(kSize1, 1, true);
+
+ EXPECT_TRUE(paced_sender_->SendPackets(packets));
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets, base::TimeDelta()));
+
+ mock_transport_.AddExpectedSize(kSize2, 1);
+ Packet tmp(kSize2, kValue);
+ EXPECT_TRUE(paced_sender_->SendRtcpPacket(
+ 1,
+ new base::RefCountedData<Packet>(tmp)));
+}
+
+TEST_F(PacedSenderTest, BasicPace) {
+ int num_of_packets = 27;
+ SendPacketVector packets = CreateSendPacketVector(kSize1,
+ num_of_packets,
+ false);
+
+ mock_transport_.AddExpectedSize(kSize1, 10);
+ EXPECT_TRUE(paced_sender_->SendPackets(packets));
+
+ // Check that we get the next burst.
+ mock_transport_.AddExpectedSize(kSize1, 10);
+
+ base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(10);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // If we call process too early make sure we don't send any packets.
+ timeout = base::TimeDelta::FromMilliseconds(5);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // Check that we get the next burst.
+ mock_transport_.AddExpectedSize(kSize1, 7);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // Check that we don't get any more packets.
+ EXPECT_TRUE(RunUntilEmpty(3));
+
+ std::vector<PacketEvent> packet_events;
+ subscriber_.GetPacketEventsAndReset(&packet_events);
+ EXPECT_EQ(num_of_packets, static_cast<int>(packet_events.size()));
+ int sent_to_network_event_count = 0;
+ for (std::vector<PacketEvent>::iterator it = packet_events.begin();
+ it != packet_events.end();
+ ++it) {
+ if (it->type == PACKET_SENT_TO_NETWORK)
+ sent_to_network_event_count++;
+ else
+ FAIL() << "Got unexpected event type " << CastLoggingToString(it->type);
+ }
+ EXPECT_EQ(num_of_packets, sent_to_network_event_count);
+}
+
+TEST_F(PacedSenderTest, PaceWithNack) {
+ // Testing what happen when we get multiple NACK requests for a fully lost
+ // frames just as we sent the first packets in a frame.
+ int num_of_packets_in_frame = 12;
+ int num_of_packets_in_nack = 12;
+
+ SendPacketVector nack_packets =
+ CreateSendPacketVector(kNackSize, num_of_packets_in_nack, false);
+
+ SendPacketVector first_frame_packets =
+ CreateSendPacketVector(kSize1, num_of_packets_in_frame, false);
+
+ SendPacketVector second_frame_packets =
+ CreateSendPacketVector(kSize2, num_of_packets_in_frame, true);
+
+ // Check that the first burst of the frame go out on the wire.
+ mock_transport_.AddExpectedSize(kSize1, 10);
+ EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
+
+ // Add first NACK request.
+ EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, base::TimeDelta()));
+
+ // Check that we get the first NACK burst.
+ mock_transport_.AddExpectedSize(kNackSize, 10);
+ base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(10);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // Add second NACK request.
+ EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, base::TimeDelta()));
+
+ // Check that we get the next NACK burst.
+ mock_transport_.AddExpectedSize(kNackSize, 10);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // End of NACK plus two packets from the oldest frame.
+ // Note that two of the NACKs have been de-duped.
+ mock_transport_.AddExpectedSize(kNackSize, 2);
+ mock_transport_.AddExpectedSize(kSize1, 2);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // Add second frame.
+ // Make sure we don't delay the second frame due to the previous packets.
+ mock_transport_.AddExpectedSize(kSize2, 10);
+ EXPECT_TRUE(paced_sender_->SendPackets(second_frame_packets));
+
+ // Last packets of frame 2.
+ mock_transport_.AddExpectedSize(kSize2, 2);
+ testing_clock_.Advance(timeout);
+ task_runner_->RunTasks();
+
+ // No more packets.
+ EXPECT_TRUE(RunUntilEmpty(5));
+
+ std::vector<PacketEvent> packet_events;
+ subscriber_.GetPacketEventsAndReset(&packet_events);
+ int expected_video_network_event_count = num_of_packets_in_frame;
+ int expected_video_retransmitted_event_count = 2 * num_of_packets_in_nack;
+ expected_video_retransmitted_event_count -= 2; // 2 packets deduped
+ int expected_audio_network_event_count = num_of_packets_in_frame;
+ EXPECT_EQ(expected_video_network_event_count +
+ expected_video_retransmitted_event_count +
+ expected_audio_network_event_count,
+ static_cast<int>(packet_events.size()));
+ int audio_network_event_count = 0;
+ int video_network_event_count = 0;
+ int video_retransmitted_event_count = 0;
+ for (std::vector<PacketEvent>::iterator it = packet_events.begin();
+ it != packet_events.end();
+ ++it) {
+ if (it->type == PACKET_SENT_TO_NETWORK) {
+ if (it->media_type == VIDEO_EVENT)
+ video_network_event_count++;
+ else
+ audio_network_event_count++;
+ } else if (it->type == PACKET_RETRANSMITTED) {
+ if (it->media_type == VIDEO_EVENT)
+ video_retransmitted_event_count++;
+ } else {
+ FAIL() << "Got unexpected event type " << CastLoggingToString(it->type);
+ }
+ }
+ EXPECT_EQ(expected_audio_network_event_count, audio_network_event_count);
+ EXPECT_EQ(expected_video_network_event_count, video_network_event_count);
+ EXPECT_EQ(expected_video_retransmitted_event_count,
+ video_retransmitted_event_count);
+}
+
+TEST_F(PacedSenderTest, PaceWith60fps) {
+ // Testing what happen when we get multiple NACK requests for a fully lost
+ // frames just as we sent the first packets in a frame.
+ int num_of_packets_in_frame = 17;
+
+ SendPacketVector first_frame_packets =
+ CreateSendPacketVector(kSize1, num_of_packets_in_frame, false);
+
+ SendPacketVector second_frame_packets =
+ CreateSendPacketVector(kSize2, num_of_packets_in_frame, false);
+
+ SendPacketVector third_frame_packets =
+ CreateSendPacketVector(kSize3, num_of_packets_in_frame, false);
+
+ SendPacketVector fourth_frame_packets =
+ CreateSendPacketVector(kSize4, num_of_packets_in_frame, false);
+
+ base::TimeDelta timeout_10ms = base::TimeDelta::FromMilliseconds(10);
+
+ // Check that the first burst of the frame go out on the wire.
+ mock_transport_.AddExpectedSize(kSize1, 10);
+ EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
+
+ mock_transport_.AddExpectedSize(kSize1, 7);
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(6));
+
+ // Add second frame, after 16 ms.
+ mock_transport_.AddExpectedSize(kSize2, 3);
+ EXPECT_TRUE(paced_sender_->SendPackets(second_frame_packets));
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(4));
+
+ mock_transport_.AddExpectedSize(kSize2, 10);
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ mock_transport_.AddExpectedSize(kSize2, 4);
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(3));
+
+ // Add third frame, after 33 ms.
+ mock_transport_.AddExpectedSize(kSize3, 6);
+ EXPECT_TRUE(paced_sender_->SendPackets(third_frame_packets));
+
+ mock_transport_.AddExpectedSize(kSize3, 10);
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(7));
+ task_runner_->RunTasks();
+
+ // Add fourth frame, after 50 ms.
+ EXPECT_TRUE(paced_sender_->SendPackets(fourth_frame_packets));
+
+ mock_transport_.AddExpectedSize(kSize3, 1);
+ mock_transport_.AddExpectedSize(kSize4, 9);
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ mock_transport_.AddExpectedSize(kSize4, 8);
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ testing_clock_.Advance(timeout_10ms);
+ task_runner_->RunTasks();
+
+ // No more packets.
+ EXPECT_TRUE(RunUntilEmpty(5));
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtcp/rtcp_builder.cc b/chromium/media/cast/transport/rtcp/rtcp_builder.cc
new file mode 100644
index 00000000000..b8875fc96bd
--- /dev/null
+++ b/chromium/media/cast/transport/rtcp/rtcp_builder.cc
@@ -0,0 +1,197 @@
+// 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/transport/rtcp/rtcp_builder.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+RtcpBuilder::RtcpBuilder(PacedSender* const outgoing_transport)
+ : transport_(outgoing_transport),
+ ssrc_(0) {
+}
+
+RtcpBuilder::~RtcpBuilder() {}
+
+void RtcpBuilder::SendRtcpFromRtpSender(
+ uint32 packet_type_flags,
+ const RtcpSenderInfo& sender_info,
+ const RtcpDlrrReportBlock& dlrr,
+ uint32 sending_ssrc,
+ const std::string& c_name) {
+ if (packet_type_flags & kRtcpRr ||
+ packet_type_flags & kRtcpPli ||
+ packet_type_flags & kRtcpRrtr ||
+ packet_type_flags & kRtcpCast ||
+ packet_type_flags & kRtcpReceiverLog ||
+ packet_type_flags & kRtcpRpsi ||
+ packet_type_flags & kRtcpRemb ||
+ packet_type_flags & kRtcpNack) {
+ NOTREACHED() << "Invalid argument";
+ }
+ ssrc_ = sending_ssrc;
+ c_name_ = c_name;
+ PacketRef packet(new base::RefCountedData<Packet>);
+ packet->data.reserve(kMaxIpPacketSize);
+ if (packet_type_flags & kRtcpSr) {
+ if (!BuildSR(sender_info, &packet->data)) return;
+ if (!BuildSdec(&packet->data)) return;
+ }
+ if (packet_type_flags & kRtcpBye) {
+ if (!BuildBye(&packet->data)) return;
+ }
+ if (packet_type_flags & kRtcpDlrr) {
+ if (!BuildDlrrRb(dlrr, &packet->data)) return;
+ }
+ if (packet->data.empty())
+ return; // Sanity - don't send empty packets.
+
+ transport_->SendRtcpPacket(ssrc_, packet);
+}
+
+bool RtcpBuilder::BuildSR(const RtcpSenderInfo& sender_info,
+ Packet* packet) const {
+ // Sender report.
+ size_t start_size = packet->size();
+ if (start_size + 52 > kMaxIpPacketSize) {
+ DLOG(FATAL) << "Not enough buffer space";
+ return false;
+ }
+
+ uint16 number_of_rows = 6;
+ packet->resize(start_size + 28);
+
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 28);
+ big_endian_writer.WriteU8(0x80);
+ big_endian_writer.WriteU8(kPacketTypeSenderReport);
+ big_endian_writer.WriteU16(number_of_rows);
+ big_endian_writer.WriteU32(ssrc_);
+ big_endian_writer.WriteU32(sender_info.ntp_seconds);
+ big_endian_writer.WriteU32(sender_info.ntp_fraction);
+ big_endian_writer.WriteU32(sender_info.rtp_timestamp);
+ big_endian_writer.WriteU32(sender_info.send_packet_count);
+ big_endian_writer.WriteU32(static_cast<uint32>(sender_info.send_octet_count));
+ return true;
+}
+
+bool RtcpBuilder::BuildSdec(Packet* packet) const {
+ size_t start_size = packet->size();
+ if (start_size + 12 + c_name_.length() > kMaxIpPacketSize) {
+ DLOG(FATAL) << "Not enough buffer space";
+ return false;
+ }
+
+ // SDES Source Description.
+ packet->resize(start_size + 10);
+
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 10);
+ // We always need to add one SDES CNAME.
+ big_endian_writer.WriteU8(0x80 + 1);
+ big_endian_writer.WriteU8(kPacketTypeSdes);
+
+ // Handle SDES length later on.
+ uint32 sdes_length_position = static_cast<uint32>(start_size) + 3;
+ big_endian_writer.WriteU16(0);
+ big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
+ big_endian_writer.WriteU8(1); // CNAME = 1
+ big_endian_writer.WriteU8(static_cast<uint8>(c_name_.length()));
+
+ size_t sdes_length = 10 + c_name_.length();
+ packet->insert(packet->end(), c_name_.c_str(),
+ c_name_.c_str() + c_name_.length());
+
+ size_t padding = 0;
+
+ // We must have a zero field even if we have an even multiple of 4 bytes.
+ if ((packet->size() % 4) == 0) {
+ padding++;
+ packet->push_back(0);
+ }
+ while ((packet->size() % 4) != 0) {
+ padding++;
+ packet->push_back(0);
+ }
+ sdes_length += padding;
+
+ // In 32-bit words minus one and we don't count the header.
+ uint8 buffer_length = static_cast<uint8>((sdes_length / 4) - 1);
+ (*packet)[sdes_length_position] = buffer_length;
+ return true;
+}
+
+bool RtcpBuilder::BuildBye(Packet* packet) const {
+ size_t start_size = packet->size();
+ if (start_size + 8 > kMaxIpPacketSize) {
+ DLOG(FATAL) << "Not enough buffer space";
+ return false;
+ }
+
+ packet->resize(start_size + 8);
+
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 8);
+ big_endian_writer.WriteU8(0x80 + 1);
+ big_endian_writer.WriteU8(kPacketTypeBye);
+ big_endian_writer.WriteU16(1); // Length.
+ big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
+ return true;
+}
+
+/*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |V=2|P|reserved | PT=XR=207 | length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | BT=5 | reserved | block length |
+ +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ | SSRC_1 (SSRC of first receiver) | sub-
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
+ | last RR (LRR) | 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | delay since last RR (DLRR) |
+ +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+*/
+bool RtcpBuilder::BuildDlrrRb(const RtcpDlrrReportBlock& dlrr,
+ Packet* packet) const {
+ size_t start_size = packet->size();
+ if (start_size + 24 > kMaxIpPacketSize) {
+ DLOG(FATAL) << "Not enough buffer space";
+ return false;
+ }
+
+ packet->resize(start_size + 24);
+
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 24);
+ big_endian_writer.WriteU8(0x80);
+ big_endian_writer.WriteU8(kPacketTypeXr);
+ big_endian_writer.WriteU16(5); // Length.
+ big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
+ big_endian_writer.WriteU8(5); // Add block type.
+ big_endian_writer.WriteU8(0); // Add reserved.
+ big_endian_writer.WriteU16(3); // Block length.
+ big_endian_writer.WriteU32(ssrc_); // Add the media (received RTP) SSRC.
+ big_endian_writer.WriteU32(dlrr.last_rr);
+ big_endian_writer.WriteU32(dlrr.delay_since_last_rr);
+ return true;
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtcp/rtcp_builder.h b/chromium/media/cast/transport/rtcp/rtcp_builder.h
new file mode 100644
index 00000000000..f095ae9ee54
--- /dev/null
+++ b/chromium/media/cast/transport/rtcp/rtcp_builder.h
@@ -0,0 +1,49 @@
+// 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_TRANSPORT_RTCP_RTCP_BUILDER_H_
+#define MEDIA_CAST_TRANSPORT_RTCP_RTCP_BUILDER_H_
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+class RtcpBuilder {
+ public:
+ explicit RtcpBuilder(PacedSender* const paced_packet_sender);
+
+ virtual ~RtcpBuilder();
+
+ void SendRtcpFromRtpSender(uint32 packet_type_flags,
+ const RtcpSenderInfo& sender_info,
+ const RtcpDlrrReportBlock& dlrr,
+ uint32 ssrc,
+ const std::string& c_name);
+
+ private:
+ bool BuildSR(const RtcpSenderInfo& sender_info, Packet* packet) const;
+ bool BuildSdec(Packet* packet) const;
+ bool BuildBye(Packet* packet) const;
+ bool BuildDlrrRb(const RtcpDlrrReportBlock& dlrr,
+ Packet* packet) const;
+
+ PacedSender* const transport_; // Not owned by this class.
+ uint32 ssrc_;
+ std::string c_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpBuilder);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_RTCP_RTCP_BUILDER_H_
diff --git a/chromium/media/cast/transport/rtcp/rtcp_builder_unittest.cc b/chromium/media/cast/transport/rtcp/rtcp_builder_unittest.cc
new file mode 100644
index 00000000000..0322612f27e
--- /dev/null
+++ b/chromium/media/cast/transport/rtcp/rtcp_builder_unittest.cc
@@ -0,0 +1,164 @@
+// 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 "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/rtcp/rtcp_utility.h"
+#include "media/cast/rtcp/test_rtcp_packet_builder.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+#include "media/cast/transport/rtcp/rtcp_builder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+namespace {
+static const uint32 kSendingSsrc = 0x12345678;
+static const std::string kCName("test@10.1.1.1");
+} // namespace
+
+class TestRtcpTransport : public PacedPacketSender {
+ public:
+ TestRtcpTransport()
+ : expected_packet_length_(0),
+ packet_count_(0) {
+ }
+
+ virtual bool SendRtcpPacket(const Packet& packet) OVERRIDE {
+ EXPECT_EQ(expected_packet_length_, packet.size());
+ EXPECT_EQ(0, memcmp(expected_packet_, &(packet[0]), packet.size()));
+ packet_count_++;
+ return true;
+ }
+
+ virtual bool SendPackets(const PacketList& packets) OVERRIDE {
+ return false;
+ }
+
+ virtual bool ResendPackets(const PacketList& packets) OVERRIDE {
+ return false;
+ }
+
+ void SetExpectedRtcpPacket(const uint8* rtcp_buffer, size_t length) {
+ expected_packet_length_ = length;
+ memcpy(expected_packet_, rtcp_buffer, length);
+ }
+
+ int packet_count() const { return packet_count_; }
+
+ private:
+ uint8 expected_packet_[kMaxIpPacketSize];
+ size_t expected_packet_length_;
+ int packet_count_;
+};
+
+class RtcpBuilderTest : public ::testing::Test {
+ protected:
+ RtcpBuilderTest()
+ : task_runner_(new test::FakeSingleThreadTaskRunner(&testing_clock_)),
+ cast_environment_(new CastEnvironment(&testing_clock_, task_runner_,
+ task_runner_, task_runner_, task_runner_, task_runner_,
+ GetDefaultCastSenderLoggingConfig())),
+ rtcp_builder_(new RtcpBuilder(&test_transport_, kSendingSsrc, kCName)) {
+ }
+
+ base::SimpleTestTickClock testing_clock_;
+ TestRtcpTransport test_transport_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ scoped_ptr<RtcpBuilder> rtcp_builder_;
+};
+
+TEST_F(RtcpBuilderTest, RtcpSenderReport) {
+ RtcpSenderInfo sender_info;
+ sender_info.ntp_seconds = kNtpHigh;
+ sender_info.ntp_fraction = kNtpLow;
+ sender_info.rtp_timestamp = kRtpTimestamp;
+ sender_info.send_packet_count = kSendPacketCount;
+ sender_info.send_octet_count = kSendOctetCount;
+
+ // Sender report + c_name.
+ TestRtcpPacketBuilder p;
+ p.AddSr(kSendingSsrc, 0);
+ p.AddSdesCname(kSendingSsrc, kCName);
+ test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+
+ rtcp_builder_->SendRtcpFromRtpSender(RtcpBuilder::kRtcpSr,
+ &sender_info,
+ NULL,
+ NULL,
+ kSendingSsrc,
+ kCName);
+
+ EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpBuilderTest, RtcpSenderReportWithDlrr) {
+ RtcpSenderInfo sender_info;
+ sender_info.ntp_seconds = kNtpHigh;
+ sender_info.ntp_fraction = kNtpLow;
+ sender_info.rtp_timestamp = kRtpTimestamp;
+ sender_info.send_packet_count = kSendPacketCount;
+ sender_info.send_octet_count = kSendOctetCount;
+
+ // Sender report + c_name + dlrr.
+ TestRtcpPacketBuilder p1;
+ p1.AddSr(kSendingSsrc, 0);
+ p1.AddSdesCname(kSendingSsrc, kCName);
+ p1.AddXrHeader(kSendingSsrc);
+ p1.AddXrDlrrBlock(kSendingSsrc);
+ test_transport_.SetExpectedRtcpPacket(p1.Packet(), p1.Length());
+
+ RtcpDlrrReportBlock dlrr_rb;
+ dlrr_rb.last_rr = kLastRr;
+ dlrr_rb.delay_since_last_rr = kDelayLastRr;
+
+ rtcp_builder_->SendRtcpFromRtpSender(
+ RtcpBuilder::kRtcpSr | RtcpBuilder::kRtcpDlrr,
+ &sender_info,
+ &dlrr_rb,
+ NULL,
+ kSendingSsrc,
+ kCName);
+
+ EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpBuilderTest, RtcpSenderReportWithDlrr) {
+ RtcpSenderInfo sender_info;
+ sender_info.ntp_seconds = kNtpHigh;
+ sender_info.ntp_fraction = kNtpLow;
+ sender_info.rtp_timestamp = kRtpTimestamp;
+ sender_info.send_packet_count = kSendPacketCount;
+ sender_info.send_octet_count = kSendOctetCount;
+
+ // Sender report + c_name + dlrr + sender log.
+ TestRtcpPacketBuilder p;
+ p.AddSr(kSendingSsrc, 0);
+ p.AddSdesCname(kSendingSsrc, kCName);
+ p.AddXrHeader(kSendingSsrc);
+ p.AddXrDlrrBlock(kSendingSsrc);
+
+ test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+
+ RtcpDlrrReportBlock dlrr_rb;
+ dlrr_rb.last_rr = kLastRr;
+ dlrr_rb.delay_since_last_rr = kDelayLastRr;
+
+ rtcp_builder_->SendRtcpFromRtpSender(
+ RtcpBuilder::kRtcpSr | RtcpBuilder::kRtcpDlrr |
+ RtcpBuilder::kRtcpSenderLog,
+ &sender_info,
+ &dlrr_rb,
+ kSendingSsrc,
+ kCName);
+
+ EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.cc b/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.cc
new file mode 100644
index 00000000000..a748baa27ab
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.cc
@@ -0,0 +1,65 @@
+// 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/transport/rtp_sender/packet_storage/packet_storage.h"
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+PacketStorage::PacketStorage(size_t stored_frames)
+ : max_stored_frames_(stored_frames),
+ first_frame_id_in_list_(0),
+ last_frame_id_in_list_(0) {
+}
+
+PacketStorage::~PacketStorage() {
+}
+
+bool PacketStorage::IsValid() const {
+ return max_stored_frames_ > 0 &&
+ static_cast<int>(max_stored_frames_) <= kMaxUnackedFrames;
+}
+
+size_t PacketStorage::GetNumberOfStoredFrames() const {
+ return frames_.size();
+}
+
+void PacketStorage::StoreFrame(uint32 frame_id,
+ const SendPacketVector& packets) {
+ if (frames_.empty()) {
+ first_frame_id_in_list_ = frame_id;
+ } else {
+ // Make sure frame IDs are consecutive.
+ DCHECK_EQ(last_frame_id_in_list_ + 1, frame_id);
+ }
+
+ // Save new frame to the end of the list.
+ last_frame_id_in_list_ = frame_id;
+ frames_.push_back(packets);
+
+ // Evict the oldest frame if the list is too long.
+ if (frames_.size() > max_stored_frames_) {
+ frames_.pop_front();
+ ++first_frame_id_in_list_;
+ }
+}
+
+const SendPacketVector* PacketStorage::GetFrame8(uint8 frame_id_8bits) const {
+ // The requested frame ID has only 8-bits so convert the first frame ID
+ // in list to match.
+ uint8 index_8bits = first_frame_id_in_list_ & 0xFF;
+ index_8bits = frame_id_8bits - index_8bits;
+ if (index_8bits >= frames_.size())
+ return NULL;
+ return &(frames_[index_8bits]);
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.h b/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.h
new file mode 100644
index 00000000000..037ead1edf6
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage.h
@@ -0,0 +1,62 @@
+// 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_TRANSPORT_RTP_SENDER_PACKET_STORAGE_PACKET_STORAGE_H_
+#define MEDIA_CAST_TRANSPORT_RTP_SENDER_PACKET_STORAGE_PACKET_STORAGE_H_
+
+#include <deque>
+#include <list>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+// Stores a list of frames. Each frame consists a list of packets.
+typedef std::deque<SendPacketVector> FrameQueue;
+
+class PacketStorage {
+ public:
+ explicit PacketStorage(size_t stored_frames);
+ virtual ~PacketStorage();
+
+ // Returns true if this class is configured correctly.
+ // (stored frames > 0 && stored_frames < kMaxStoredFrames)
+ bool IsValid() const;
+
+ // Store all of the packets for a frame.
+ void StoreFrame(uint32 frame_id, const SendPacketVector& packets);
+
+ // Returns a list of packets for a frame indexed by a 8-bits ID.
+ // It is the lowest 8 bits of a frame ID.
+ // Returns NULL if the frame cannot be found.
+ const SendPacketVector* GetFrame8(uint8 frame_id_8bits) const;
+
+ // Get the number of stored frames.
+ size_t GetNumberOfStoredFrames() const;
+
+ private:
+ const size_t max_stored_frames_;
+ FrameQueue frames_;
+ uint32 first_frame_id_in_list_;
+ uint32 last_frame_id_in_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(PacketStorage);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_RTP_SENDER_PACKET_STORAGE_PACKET_STORAGE_H_
diff --git a/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage_unittest.cc b/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage_unittest.cc
new file mode 100644
index 00000000000..298942c80a5
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/packet_storage/packet_storage_unittest.cc
@@ -0,0 +1,115 @@
+// 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/transport/rtp_sender/packet_storage/packet_storage.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+static size_t kStoredFrames = 10;
+
+// Generate |number_of_frames| and store into |*storage|.
+// First frame has 1 packet, second frame has 2 packets, etc.
+static void StoreFrames(size_t number_of_frames,
+ uint32 first_frame_id,
+ PacketStorage* storage) {
+ const base::TimeTicks kTicks;
+ const int kSsrc = 1;
+ for (size_t i = 0; i < number_of_frames; ++i) {
+ SendPacketVector packets;
+ // First frame has 1 packet, second frame has 2 packets, etc.
+ const size_t kNumberOfPackets = i + 1;
+ for (size_t j = 0; j < kNumberOfPackets; ++j) {
+ Packet test_packet(1, 0);
+ packets.push_back(
+ std::make_pair(
+ PacedPacketSender::MakePacketKey(kTicks, kSsrc, j),
+ new base::RefCountedData<Packet>(test_packet)));
+ }
+ storage->StoreFrame(first_frame_id, packets);
+ ++first_frame_id;
+ }
+}
+
+TEST(PacketStorageTest, NumberOfStoredFrames) {
+ PacketStorage storage(kStoredFrames);
+
+ uint32 frame_id = 0;
+ frame_id = ~frame_id; // The maximum value of uint32.
+ StoreFrames(200, frame_id, &storage);
+ EXPECT_EQ(kStoredFrames, storage.GetNumberOfStoredFrames());
+}
+
+TEST(PacketStorageTest, GetFrameWrapAround8bits) {
+ PacketStorage storage(kStoredFrames);
+
+ const uint32 kFirstFrameId = 250;
+ StoreFrames(kStoredFrames, kFirstFrameId, &storage);
+ EXPECT_EQ(kStoredFrames, storage.GetNumberOfStoredFrames());
+
+ // Expect we get the correct frames by looking at the number of
+ // packets.
+ uint32 frame_id = kFirstFrameId;
+ for (size_t i = 0; i < kStoredFrames; ++i) {
+ ASSERT_TRUE(storage.GetFrame8(frame_id));
+ EXPECT_EQ(i + 1, storage.GetFrame8(frame_id)->size());
+ ++frame_id;
+ }
+}
+
+TEST(PacketStorageTest, GetFrameWrapAround32bits) {
+ PacketStorage storage(kStoredFrames);
+
+ // First frame ID is close to the maximum value of uint32.
+ uint32 first_frame_id = 0xffffffff - 5;
+ StoreFrames(kStoredFrames, first_frame_id, &storage);
+ EXPECT_EQ(kStoredFrames, storage.GetNumberOfStoredFrames());
+
+ // Expect we get the correct frames by looking at the number of
+ // packets.
+ uint32 frame_id = first_frame_id;
+ for (size_t i = 0; i < kStoredFrames; ++i) {
+ ASSERT_TRUE(storage.GetFrame8(frame_id));
+ EXPECT_EQ(i + 1, storage.GetFrame8(frame_id)->size());
+ ++frame_id;
+ }
+}
+
+TEST(PacketStorageTest, GetFrameTooOld) {
+ PacketStorage storage(kStoredFrames);
+
+ // First frame ID is close to the maximum value of uint32.
+ uint32 first_frame_id = 0xffffffff - 5;
+
+ // Store two times the capacity.
+ StoreFrames(2 * kStoredFrames, first_frame_id, &storage);
+ EXPECT_EQ(kStoredFrames, storage.GetNumberOfStoredFrames());
+
+ uint32 frame_id = first_frame_id;
+ // Old frames are evicted.
+ for (size_t i = 0; i < kStoredFrames; ++i) {
+ EXPECT_FALSE(storage.GetFrame8(frame_id));
+ ++frame_id;
+ }
+ // Check recent frames are there.
+ for (size_t i = 0; i < kStoredFrames; ++i) {
+ ASSERT_TRUE(storage.GetFrame8(frame_id));
+ EXPECT_EQ(kStoredFrames + i + 1,
+ storage.GetFrame8(frame_id)->size());
+ ++frame_id;
+ }
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.cc b/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.cc
new file mode 100644
index 00000000000..d40f99f1446
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.cc
@@ -0,0 +1,137 @@
+// 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/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+static const uint16 kCommonRtpHeaderLength = 12;
+static const uint16 kCastRtpHeaderLength = 7;
+static const uint8 kCastKeyFrameBitMask = 0x80;
+static const uint8 kCastReferenceFrameIdBitMask = 0x40;
+static const uint8 kRtpMarkerBitMask = 0x80;
+
+RtpPacketizerConfig::RtpPacketizerConfig()
+ : audio(false),
+ payload_type(-1),
+ max_payload_length(kMaxIpPacketSize - 28), // Default is IP-v4/UDP.
+ sequence_number(0),
+ frequency(8000),
+ ssrc(0),
+ channels(0) {}
+
+RtpPacketizerConfig::~RtpPacketizerConfig() {}
+
+RtpPacketizer::RtpPacketizer(PacedSender* const transport,
+ PacketStorage* packet_storage,
+ RtpPacketizerConfig rtp_packetizer_config)
+ : config_(rtp_packetizer_config),
+ transport_(transport),
+ packet_storage_(packet_storage),
+ sequence_number_(config_.sequence_number),
+ rtp_timestamp_(0),
+ packet_id_(0),
+ send_packet_count_(0),
+ send_octet_count_(0) {
+ DCHECK(transport) << "Invalid argument";
+}
+
+RtpPacketizer::~RtpPacketizer() {}
+
+uint16 RtpPacketizer::NextSequenceNumber() {
+ ++sequence_number_;
+ return sequence_number_ - 1;
+}
+
+void RtpPacketizer::SendFrameAsPackets(const EncodedFrame& frame) {
+ uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength;
+ uint16 max_length = config_.max_payload_length - rtp_header_length - 1;
+ rtp_timestamp_ = frame.rtp_timestamp;
+
+ // Split the payload evenly (round number up).
+ size_t num_packets = (frame.data.size() + max_length) / max_length;
+ size_t payload_length = (frame.data.size() + num_packets) / num_packets;
+ DCHECK_LE(payload_length, max_length) << "Invalid argument";
+
+ SendPacketVector packets;
+
+ size_t remaining_size = frame.data.size();
+ std::string::const_iterator data_iter = frame.data.begin();
+ while (remaining_size > 0) {
+ PacketRef packet(new base::RefCountedData<Packet>);
+
+ if (remaining_size < payload_length) {
+ payload_length = remaining_size;
+ }
+ remaining_size -= payload_length;
+ BuildCommonRTPheader(
+ &packet->data, remaining_size == 0, frame.rtp_timestamp);
+
+ // Build Cast header.
+ // TODO(miu): Should we always set the ref frame bit and the ref_frame_id?
+ DCHECK_NE(frame.dependency, EncodedFrame::UNKNOWN_DEPENDENCY);
+ packet->data.push_back(
+ ((frame.dependency == EncodedFrame::KEY) ? kCastKeyFrameBitMask : 0) |
+ kCastReferenceFrameIdBitMask);
+ packet->data.push_back(static_cast<uint8>(frame.frame_id));
+ size_t start_size = packet->data.size();
+ packet->data.resize(start_size + 4);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&(packet->data[start_size])), 4);
+ big_endian_writer.WriteU16(packet_id_);
+ big_endian_writer.WriteU16(static_cast<uint16>(num_packets - 1));
+ packet->data.push_back(static_cast<uint8>(frame.referenced_frame_id));
+
+ // Copy payload data.
+ packet->data.insert(packet->data.end(),
+ data_iter,
+ data_iter + payload_length);
+ data_iter += payload_length;
+
+ const PacketKey key =
+ PacedPacketSender::MakePacketKey(frame.reference_time,
+ config_.ssrc,
+ packet_id_++);
+ packets.push_back(make_pair(key, packet));
+
+ // Update stats.
+ ++send_packet_count_;
+ send_octet_count_ += payload_length;
+ }
+ DCHECK(packet_id_ == num_packets) << "Invalid state";
+
+ packet_storage_->StoreFrame(frame.frame_id, packets);
+
+ // Send to network.
+ transport_->SendPackets(packets);
+
+ // Prepare for next frame.
+ packet_id_ = 0;
+}
+
+void RtpPacketizer::BuildCommonRTPheader(Packet* packet,
+ bool marker_bit,
+ uint32 time_stamp) {
+ packet->push_back(0x80);
+ packet->push_back(static_cast<uint8>(config_.payload_type) |
+ (marker_bit ? kRtpMarkerBitMask : 0));
+ size_t start_size = packet->size();
+ packet->resize(start_size + 10);
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>(&((*packet)[start_size])), 10);
+ big_endian_writer.WriteU16(sequence_number_);
+ big_endian_writer.WriteU32(time_stamp);
+ big_endian_writer.WriteU32(config_.ssrc);
+ ++sequence_number_;
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h b/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h
new file mode 100644
index 00000000000..ebdbf010183
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h
@@ -0,0 +1,86 @@
+// 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_TRANSPORT_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
+#define MEDIA_CAST_TRANSPORT_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
+
+#include <cmath>
+#include <list>
+#include <map>
+
+#include "base/time/time.h"
+#include "media/cast/transport/rtp_sender/packet_storage/packet_storage.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace media {
+namespace cast {
+
+namespace transport {
+
+class PacedSender;
+
+struct RtpPacketizerConfig {
+ RtpPacketizerConfig();
+ ~RtpPacketizerConfig();
+
+ // General.
+ bool audio;
+ int payload_type;
+ uint16 max_payload_length;
+ uint16 sequence_number;
+ int frequency;
+
+ // SSRC.
+ unsigned int ssrc;
+
+ // Video.
+ VideoCodec video_codec;
+
+ // Audio.
+ uint8 channels;
+ AudioCodec audio_codec;
+};
+
+// This object is only called from the main cast thread.
+// This class break encoded audio and video frames into packets and add an RTP
+// header to each packet.
+class RtpPacketizer {
+ public:
+ RtpPacketizer(PacedSender* const transport,
+ PacketStorage* packet_storage,
+ RtpPacketizerConfig rtp_packetizer_config);
+ ~RtpPacketizer();
+
+ void SendFrameAsPackets(const EncodedFrame& frame);
+
+ // Return the next sequence number, and increment by one. Enables unique
+ // incremental sequence numbers for every packet (including retransmissions).
+ uint16 NextSequenceNumber();
+
+ size_t send_packet_count() const { return send_packet_count_; }
+ size_t send_octet_count() const { return send_octet_count_; }
+
+ private:
+ void BuildCommonRTPheader(Packet* packet, bool marker_bit, uint32 time_stamp);
+
+ RtpPacketizerConfig config_;
+ PacedSender* const transport_; // Not owned by this class.
+ PacketStorage* packet_storage_;
+
+ uint16 sequence_number_;
+ uint32 rtp_timestamp_;
+ uint16 packet_id_;
+
+ size_t send_packet_count_;
+ size_t send_octet_count_;
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
diff --git a/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc b/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
new file mode 100644
index 00000000000..64def4ce7fa
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
@@ -0,0 +1,175 @@
+// 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/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+
+#include <stdint.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+#include "media/cast/transport/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/transport/rtp_sender/rtp_packetizer/test/rtp_header_parser.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+namespace {
+static const int kPayload = 127;
+static const uint32 kTimestampMs = 10;
+static const uint16 kSeqNum = 33;
+static const int kMaxPacketLength = 1500;
+static const int kSsrc = 0x12345;
+static const unsigned int kFrameSize = 5000;
+static const uint32 kStartFrameId = UINT32_C(0xffffffff);
+}
+
+class TestRtpPacketTransport : public PacketSender {
+ public:
+ explicit TestRtpPacketTransport(RtpPacketizerConfig config)
+ : config_(config),
+ sequence_number_(kSeqNum),
+ packets_sent_(0),
+ expected_number_of_packets_(0),
+ expected_packet_id_(0),
+ expected_frame_id_(0),
+ expectd_rtp_timestamp_(0) {}
+
+ void VerifyRtpHeader(const RtpCastTestHeader& rtp_header) {
+ VerifyCommonRtpHeader(rtp_header);
+ VerifyCastRtpHeader(rtp_header);
+ }
+
+ void VerifyCommonRtpHeader(const RtpCastTestHeader& rtp_header) {
+ EXPECT_EQ(kPayload, rtp_header.payload_type);
+ EXPECT_EQ(sequence_number_, rtp_header.sequence_number);
+ EXPECT_EQ(expectd_rtp_timestamp_, rtp_header.rtp_timestamp);
+ EXPECT_EQ(config_.ssrc, rtp_header.ssrc);
+ EXPECT_EQ(0, rtp_header.num_csrcs);
+ }
+
+ void VerifyCastRtpHeader(const RtpCastTestHeader& rtp_header) {
+ EXPECT_FALSE(rtp_header.is_key_frame);
+ EXPECT_EQ(expected_frame_id_, rtp_header.frame_id);
+ EXPECT_EQ(expected_packet_id_, rtp_header.packet_id);
+ EXPECT_EQ(expected_number_of_packets_ - 1, rtp_header.max_packet_id);
+ EXPECT_TRUE(rtp_header.is_reference);
+ EXPECT_EQ(expected_frame_id_ - 1u, rtp_header.reference_frame_id);
+ }
+
+ virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE {
+ ++packets_sent_;
+ RtpHeaderParser parser(packet->data.data(), packet->data.size());
+ RtpCastTestHeader rtp_header;
+ parser.Parse(&rtp_header);
+ VerifyRtpHeader(rtp_header);
+ ++sequence_number_;
+ ++expected_packet_id_;
+ return true;
+ }
+
+ size_t number_of_packets_received() const { return packets_sent_; }
+
+ void set_expected_number_of_packets(size_t expected_number_of_packets) {
+ expected_number_of_packets_ = expected_number_of_packets;
+ }
+
+ void set_rtp_timestamp(uint32 rtp_timestamp) {
+ expectd_rtp_timestamp_ = rtp_timestamp;
+ }
+
+ RtpPacketizerConfig config_;
+ uint32 sequence_number_;
+ size_t packets_sent_;
+ size_t number_of_packets_;
+ size_t expected_number_of_packets_;
+ // Assuming packets arrive in sequence.
+ int expected_packet_id_;
+ uint32 expected_frame_id_;
+ uint32 expectd_rtp_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRtpPacketTransport);
+};
+
+class RtpPacketizerTest : public ::testing::Test {
+ protected:
+ RtpPacketizerTest()
+ : task_runner_(new test::FakeSingleThreadTaskRunner(&testing_clock_)),
+ video_frame_(),
+ packet_storage_(200) {
+ config_.sequence_number = kSeqNum;
+ config_.ssrc = kSsrc;
+ config_.payload_type = kPayload;
+ config_.max_payload_length = kMaxPacketLength;
+ transport_.reset(new TestRtpPacketTransport(config_));
+ pacer_.reset(new PacedSender(
+ &testing_clock_, &logging_, transport_.get(), task_runner_));
+ pacer_->RegisterVideoSsrc(config_.ssrc);
+ rtp_packetizer_.reset(new RtpPacketizer(
+ pacer_.get(), &packet_storage_, config_));
+ video_frame_.dependency = EncodedFrame::DEPENDENT;
+ video_frame_.frame_id = 0;
+ video_frame_.referenced_frame_id = kStartFrameId;
+ video_frame_.data.assign(kFrameSize, 123);
+ video_frame_.rtp_timestamp =
+ GetVideoRtpTimestamp(testing_clock_.NowTicks());
+ }
+
+ void RunTasks(int during_ms) {
+ for (int i = 0; i < during_ms; ++i) {
+ // Call process the timers every 1 ms.
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(1));
+ task_runner_->RunTasks();
+ }
+ }
+
+ base::SimpleTestTickClock testing_clock_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ EncodedFrame video_frame_;
+ PacketStorage packet_storage_;
+ RtpPacketizerConfig config_;
+ scoped_ptr<TestRtpPacketTransport> transport_;
+ LoggingImpl logging_;
+ scoped_ptr<PacedSender> pacer_;
+ scoped_ptr<RtpPacketizer> rtp_packetizer_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtpPacketizerTest);
+};
+
+TEST_F(RtpPacketizerTest, SendStandardPackets) {
+ size_t expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
+ transport_->set_expected_number_of_packets(expected_num_of_packets);
+ transport_->set_rtp_timestamp(video_frame_.rtp_timestamp);
+
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kTimestampMs));
+ video_frame_.reference_time = testing_clock_.NowTicks();
+ rtp_packetizer_->SendFrameAsPackets(video_frame_);
+ RunTasks(33 + 1);
+ EXPECT_EQ(expected_num_of_packets, transport_->number_of_packets_received());
+}
+
+TEST_F(RtpPacketizerTest, Stats) {
+ EXPECT_FALSE(rtp_packetizer_->send_packet_count());
+ EXPECT_FALSE(rtp_packetizer_->send_octet_count());
+ // Insert packets at varying lengths.
+ size_t expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
+ transport_->set_expected_number_of_packets(expected_num_of_packets);
+ transport_->set_rtp_timestamp(video_frame_.rtp_timestamp);
+
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(kTimestampMs));
+ video_frame_.reference_time = testing_clock_.NowTicks();
+ rtp_packetizer_->SendFrameAsPackets(video_frame_);
+ RunTasks(33 + 1);
+ EXPECT_EQ(expected_num_of_packets, rtp_packetizer_->send_packet_count());
+ EXPECT_EQ(kFrameSize, rtp_packetizer_->send_octet_count());
+ EXPECT_EQ(expected_num_of_packets, transport_->number_of_packets_received());
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtp_sender/rtp_sender.cc b/chromium/media/cast/transport/rtp_sender/rtp_sender.cc
new file mode 100644
index 00000000000..b807b347576
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/rtp_sender.cc
@@ -0,0 +1,150 @@
+// 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/transport/rtp_sender/rtp_sender.h"
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+namespace {
+
+// If there is only one referecne to the packet then copy the
+// reference and return.
+// Otherwise return a deep copy of the packet.
+PacketRef FastCopyPacket(const PacketRef& packet) {
+ if (packet->HasOneRef())
+ return packet;
+ return make_scoped_refptr(new base::RefCountedData<Packet>(packet->data));
+}
+
+} // namespace
+
+RtpSender::RtpSender(
+ base::TickClock* clock,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner,
+ PacedSender* const transport)
+ : clock_(clock),
+ transport_(transport),
+ transport_task_runner_(transport_task_runner),
+ weak_factory_(this) {
+ // Randomly set sequence number start value.
+ config_.sequence_number = base::RandInt(0, 65535);
+}
+
+RtpSender::~RtpSender() {}
+
+bool RtpSender::InitializeAudio(const CastTransportAudioConfig& config) {
+ storage_.reset(new PacketStorage(config.rtp.max_outstanding_frames));
+ if (!storage_->IsValid()) {
+ return false;
+ }
+ config_.audio = true;
+ config_.ssrc = config.rtp.config.ssrc;
+ config_.payload_type = config.rtp.config.payload_type;
+ config_.frequency = config.frequency;
+ config_.audio_codec = config.codec;
+ packetizer_.reset(new RtpPacketizer(transport_, storage_.get(), config_));
+ return true;
+}
+
+bool RtpSender::InitializeVideo(const CastTransportVideoConfig& config) {
+ storage_.reset(new PacketStorage(config.rtp.max_outstanding_frames));
+ if (!storage_->IsValid()) {
+ return false;
+ }
+ config_.audio = false;
+ config_.ssrc = config.rtp.config.ssrc;
+ config_.payload_type = config.rtp.config.payload_type;
+ config_.frequency = kVideoFrequency;
+ config_.video_codec = config.codec;
+ packetizer_.reset(new RtpPacketizer(transport_, storage_.get(), config_));
+ return true;
+}
+
+void RtpSender::SendFrame(const EncodedFrame& frame) {
+ DCHECK(packetizer_);
+ packetizer_->SendFrameAsPackets(frame);
+}
+
+void RtpSender::ResendPackets(
+ const MissingFramesAndPacketsMap& missing_frames_and_packets,
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) {
+ DCHECK(storage_);
+ // Iterate over all frames in the list.
+ for (MissingFramesAndPacketsMap::const_iterator it =
+ missing_frames_and_packets.begin();
+ it != missing_frames_and_packets.end();
+ ++it) {
+ SendPacketVector packets_to_resend;
+ uint8 frame_id = it->first;
+ // Set of packets that the receiver wants us to re-send.
+ // If empty, we need to re-send all packets for this frame.
+ const PacketIdSet& missing_packet_set = it->second;
+
+ bool resend_all = missing_packet_set.find(kRtcpCastAllPacketsLost) !=
+ missing_packet_set.end();
+ bool resend_last = missing_packet_set.find(kRtcpCastLastPacket) !=
+ missing_packet_set.end();
+
+ const SendPacketVector* stored_packets = storage_->GetFrame8(frame_id);
+ if (!stored_packets)
+ continue;
+
+ for (SendPacketVector::const_iterator it = stored_packets->begin();
+ it != stored_packets->end(); ++it) {
+ const PacketKey& packet_key = it->first;
+ const uint16 packet_id = packet_key.second.second;
+
+ // Should we resend the packet?
+ bool resend = resend_all;
+
+ // Should we resend it because it's in the missing_packet_set?
+ if (!resend &&
+ missing_packet_set.find(packet_id) != missing_packet_set.end()) {
+ resend = true;
+ }
+
+ // If we were asked to resend the last packet, check if it's the
+ // last packet.
+ if (!resend && resend_last && (it + 1) == stored_packets->end()) {
+ resend = true;
+ }
+
+ if (resend) {
+ // Resend packet to the network.
+ VLOG(3) << "Resend " << static_cast<int>(frame_id) << ":"
+ << packet_id;
+ // Set a unique incremental sequence number for every packet.
+ PacketRef packet_copy = FastCopyPacket(it->second);
+ UpdateSequenceNumber(&packet_copy->data);
+ packets_to_resend.push_back(std::make_pair(packet_key, packet_copy));
+ } else if (cancel_rtx_if_not_in_list) {
+ transport_->CancelSendingPacket(it->first);
+ }
+ }
+ transport_->ResendPackets(packets_to_resend, dedupe_window);
+ }
+}
+
+void RtpSender::UpdateSequenceNumber(Packet* packet) {
+ // TODO(miu): This is an abstraction violation. This needs to be a part of
+ // the overall packet (de)serialization consolidation.
+ static const int kByteOffsetToSequenceNumber = 2;
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>((&packet->front()) + kByteOffsetToSequenceNumber),
+ sizeof(uint16));
+ big_endian_writer.WriteU16(packetizer_->NextSequenceNumber());
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/rtp_sender/rtp_sender.h b/chromium/media/cast/transport/rtp_sender/rtp_sender.h
new file mode 100644
index 00000000000..e65326abf16
--- /dev/null
+++ b/chromium/media/cast/transport/rtp_sender/rtp_sender.h
@@ -0,0 +1,85 @@
+// 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.
+
+// This file contains the interface to the cast RTP sender.
+
+#ifndef MEDIA_CAST_TRANSPORT_RTP_SENDER_RTP_SENDER_H_
+#define MEDIA_CAST_TRANSPORT_RTP_SENDER_RTP_SENDER_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/memory/weak_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/transport/cast_transport_sender.h"
+#include "media/cast/transport/pacing/paced_sender.h"
+#include "media/cast/transport/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/transport/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+
+namespace media {
+namespace cast {
+
+namespace transport {
+
+// This object is only called from the main cast thread.
+// This class handles splitting encoded audio and video frames into packets and
+// add an RTP header to each packet. The sent packets are stored until they are
+// acknowledged by the remote peer or timed out.
+class RtpSender {
+ public:
+ RtpSender(
+ base::TickClock* clock,
+ const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner,
+ PacedSender* const transport);
+
+ ~RtpSender();
+
+ // Initialize audio stack. Audio must be initialized prior to sending encoded
+ // audio frames. Returns false if configuration is invalid.
+ bool InitializeAudio(const CastTransportAudioConfig& config);
+
+ // Initialize video stack. Video must be initialized prior to sending encoded
+ // video frames. Returns false if configuration is invalid.
+ bool InitializeVideo(const CastTransportVideoConfig& config);
+
+ void SendFrame(const EncodedFrame& frame);
+
+ void ResendPackets(const MissingFramesAndPacketsMap& missing_packets,
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window);
+
+ size_t send_packet_count() const {
+ return packetizer_ ? packetizer_->send_packet_count() : 0;
+ }
+ size_t send_octet_count() const {
+ return packetizer_ ? packetizer_->send_octet_count() : 0;
+ }
+ uint32 ssrc() const { return config_.ssrc; }
+
+ private:
+ void UpdateSequenceNumber(Packet* packet);
+
+ base::TickClock* clock_; // Not owned by this class.
+ RtpPacketizerConfig config_;
+ scoped_ptr<RtpPacketizer> packetizer_;
+ scoped_ptr<PacketStorage> storage_;
+ PacedSender* const transport_;
+ scoped_refptr<base::SingleThreadTaskRunner> transport_task_runner_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<RtpSender> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtpSender);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_RTP_SENDER_RTP_SENDER_H_
diff --git a/chromium/media/cast/transport/transport/udp_transport.cc b/chromium/media/cast/transport/transport/udp_transport.cc
new file mode 100644
index 00000000000..9669b17d438
--- /dev/null
+++ b/chromium/media/cast/transport/transport/udp_transport.cc
@@ -0,0 +1,242 @@
+// 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/transport/transport/udp_transport.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/rand_callback.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+namespace {
+const int kMaxPacketSize = 1500;
+
+bool IsEmpty(const net::IPEndPoint& addr) {
+ net::IPAddressNumber empty_addr(addr.address().size());
+ return std::equal(
+ empty_addr.begin(), empty_addr.end(), addr.address().begin()) &&
+ !addr.port();
+}
+
+bool IsEqual(const net::IPEndPoint& addr1, const net::IPEndPoint& addr2) {
+ return addr1.port() == addr2.port() && std::equal(addr1.address().begin(),
+ addr1.address().end(),
+ addr2.address().begin());
+}
+} // namespace
+
+UdpTransport::UdpTransport(
+ net::NetLog* net_log,
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_thread_proxy,
+ const net::IPEndPoint& local_end_point,
+ const net::IPEndPoint& remote_end_point,
+ const CastTransportStatusCallback& status_callback)
+ : io_thread_proxy_(io_thread_proxy),
+ local_addr_(local_end_point),
+ remote_addr_(remote_end_point),
+ udp_socket_(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND,
+ net::RandIntCallback(),
+ net_log,
+ net::NetLog::Source())),
+ send_pending_(false),
+ receive_pending_(false),
+ client_connected_(false),
+ next_dscp_value_(net::DSCP_NO_CHANGE),
+ status_callback_(status_callback),
+ weak_factory_(this) {
+ DCHECK(!IsEmpty(local_end_point) || !IsEmpty(remote_end_point));
+}
+
+UdpTransport::~UdpTransport() {}
+
+void UdpTransport::StartReceiving(
+ const PacketReceiverCallback& packet_receiver) {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+
+ packet_receiver_ = packet_receiver;
+ udp_socket_->AllowAddressReuse();
+ udp_socket_->SetMulticastLoopbackMode(true);
+ if (!IsEmpty(local_addr_)) {
+ if (udp_socket_->Bind(local_addr_) < 0) {
+ status_callback_.Run(TRANSPORT_SOCKET_ERROR);
+ LOG(ERROR) << "Failed to bind local address.";
+ return;
+ }
+ } else if (!IsEmpty(remote_addr_)) {
+ if (udp_socket_->Connect(remote_addr_) < 0) {
+ status_callback_.Run(TRANSPORT_SOCKET_ERROR);
+ LOG(ERROR) << "Failed to connect to remote address.";
+ return;
+ }
+ client_connected_ = true;
+ } else {
+ NOTREACHED() << "Either local or remote address has to be defined.";
+ }
+
+ ScheduleReceiveNextPacket();
+}
+
+void UdpTransport::SetDscp(net::DiffServCodePoint dscp) {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+ next_dscp_value_ = dscp;
+}
+
+void UdpTransport::ScheduleReceiveNextPacket() {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+ if (!packet_receiver_.is_null() && !receive_pending_) {
+ receive_pending_ = true;
+ io_thread_proxy_->PostTask(FROM_HERE,
+ base::Bind(&UdpTransport::ReceiveNextPacket,
+ weak_factory_.GetWeakPtr(),
+ net::ERR_IO_PENDING));
+ }
+}
+
+void UdpTransport::ReceiveNextPacket(int length_or_status) {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+
+ // Loop while UdpSocket is delivering data synchronously. When it responds
+ // with a "pending" status, break and expect this method to be called back in
+ // the future when a packet is ready.
+ while (true) {
+ if (length_or_status == net::ERR_IO_PENDING) {
+ next_packet_.reset(new Packet(kMaxPacketSize));
+ recv_buf_ = new net::WrappedIOBuffer(
+ reinterpret_cast<char*>(&next_packet_->front()));
+ length_or_status = udp_socket_->RecvFrom(
+ recv_buf_,
+ kMaxPacketSize,
+ &recv_addr_,
+ base::Bind(&UdpTransport::ReceiveNextPacket,
+ weak_factory_.GetWeakPtr()));
+ if (length_or_status == net::ERR_IO_PENDING) {
+ receive_pending_ = true;
+ return;
+ }
+ }
+
+ // Note: At this point, either a packet is ready or an error has occurred.
+ if (length_or_status < 0) {
+ VLOG(1) << "Failed to receive packet: Status code is "
+ << length_or_status;
+ status_callback_.Run(TRANSPORT_SOCKET_ERROR);
+ receive_pending_ = false;
+ return;
+ }
+
+ // Confirm the packet has come from the expected remote address; otherwise,
+ // ignore it. If this is the first packet being received and no remote
+ // address has been set, set the remote address and expect all future
+ // packets to come from the same one.
+ // TODO(hubbe): We should only do this if the caller used a valid ssrc.
+ if (IsEmpty(remote_addr_)) {
+ remote_addr_ = recv_addr_;
+ VLOG(1) << "Setting remote address from first received packet: "
+ << remote_addr_.ToString();
+ } else if (!IsEqual(remote_addr_, recv_addr_)) {
+ VLOG(1) << "Ignoring packet received from an unrecognized address: "
+ << recv_addr_.ToString() << ".";
+ length_or_status = net::ERR_IO_PENDING;
+ continue;
+ }
+
+ next_packet_->resize(length_or_status);
+ packet_receiver_.Run(next_packet_.Pass());
+ length_or_status = net::ERR_IO_PENDING;
+ }
+}
+
+bool UdpTransport::SendPacket(PacketRef packet, const base::Closure& cb) {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+
+ DCHECK(!send_pending_);
+ if (send_pending_) {
+ VLOG(1) << "Cannot send because of pending IO.";
+ return true;
+ }
+
+ if (next_dscp_value_ != net::DSCP_NO_CHANGE) {
+ int result = udp_socket_->SetDiffServCodePoint(next_dscp_value_);
+ if (result != net::OK) {
+ LOG(ERROR) << "Unable to set DSCP: " << next_dscp_value_
+ << " to socket; Error: " << result;
+ }
+ // Don't change DSCP in next send.
+ next_dscp_value_ = net::DSCP_NO_CHANGE;
+ }
+
+ scoped_refptr<net::IOBuffer> buf =
+ new net::WrappedIOBuffer(reinterpret_cast<char*>(&packet->data.front()));
+
+ int result;
+ base::Callback<void(int)> callback = base::Bind(&UdpTransport::OnSent,
+ weak_factory_.GetWeakPtr(),
+ buf,
+ packet,
+ cb);
+ if (client_connected_) {
+ // If we called Connect() before we must call Write() instead of
+ // SendTo(). Otherwise on some platforms we might get
+ // ERR_SOCKET_IS_CONNECTED.
+ result = udp_socket_->Write(buf,
+ static_cast<int>(packet->data.size()),
+ callback);
+ } else if (!IsEmpty(remote_addr_)) {
+ result = udp_socket_->SendTo(buf,
+ static_cast<int>(packet->data.size()),
+ remote_addr_,
+ callback);
+ } else {
+ return true;
+ }
+
+ if (result == net::ERR_IO_PENDING) {
+ send_pending_ = true;
+ return false;
+ } else if (result < 0) {
+ LOG(ERROR) << "Failed to send packet: " << result << ".";
+ status_callback_.Run(TRANSPORT_SOCKET_ERROR);
+ return true;
+ } else {
+ // Successful send, re-start reading if needed.
+ ScheduleReceiveNextPacket();
+ return true;
+ }
+}
+
+void UdpTransport::OnSent(const scoped_refptr<net::IOBuffer>& buf,
+ PacketRef packet,
+ const base::Closure& cb,
+ int result) {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+
+ send_pending_ = false;
+ if (result < 0) {
+ LOG(ERROR) << "Failed to send packet: " << result << ".";
+ status_callback_.Run(TRANSPORT_SOCKET_ERROR);
+ } else {
+ // Successful send, re-start reading if needed.
+ ScheduleReceiveNextPacket();
+ }
+
+ if (!cb.is_null()) {
+ cb.Run();
+ }
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/transport/udp_transport.h b/chromium/media/cast/transport/transport/udp_transport.h
new file mode 100644
index 00000000000..1a568501d5f
--- /dev/null
+++ b/chromium/media/cast/transport/transport/udp_transport.h
@@ -0,0 +1,97 @@
+// 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_TRANSPORT_TRANSPORT_UDP_TRANSPORT_H_
+#define MEDIA_CAST_TRANSPORT_TRANSPORT_UDP_TRANSPORT_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_sender.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
+#include "net/udp/udp_socket.h"
+
+namespace net {
+class IOBuffer;
+class IPEndPoint;
+class NetLog;
+} // namespace net
+
+namespace media {
+namespace cast {
+namespace transport {
+
+// This class implements UDP transport mechanism for Cast.
+class UdpTransport : public PacketSender {
+ public:
+ // Construct a UDP transport.
+ // All methods must be called on |io_thread_proxy|.
+ // |local_end_point| specifies the address and port to bind and listen
+ // to incoming packets. If the value is 0.0.0.0:0 then a bind is not
+ // performed.
+ // |remote_end_point| specifies the address and port to send packets
+ // to. If the value is 0.0.0.0:0 the the end point is set to the source
+ // address of the first packet received.
+ UdpTransport(
+ net::NetLog* net_log,
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_thread_proxy,
+ const net::IPEndPoint& local_end_point,
+ const net::IPEndPoint& remote_end_point,
+ const CastTransportStatusCallback& status_callback);
+ virtual ~UdpTransport();
+
+ // Start receiving packets. Packets are submitted to |packet_receiver|.
+ void StartReceiving(const PacketReceiverCallback& packet_receiver);
+
+ // Set a new DSCP value to the socket. The value will be set right before
+ // the next send.
+ void SetDscp(net::DiffServCodePoint dscp);
+
+ // PacketSender implementations.
+ virtual bool SendPacket(PacketRef packet,
+ const base::Closure& cb) OVERRIDE;
+
+ private:
+ // Requests and processes packets from |udp_socket_|. This method is called
+ // once with |length_or_status| set to net::ERR_IO_PENDING to start receiving
+ // packets. Thereafter, it is called with some other value as the callback
+ // response from UdpSocket::RecvFrom().
+ void ReceiveNextPacket(int length_or_status);
+
+ // Schedule packet receiving, if needed.
+ void ScheduleReceiveNextPacket();
+
+ void OnSent(const scoped_refptr<net::IOBuffer>& buf,
+ PacketRef packet,
+ const base::Closure& cb,
+ int result);
+
+ const scoped_refptr<base::SingleThreadTaskRunner> io_thread_proxy_;
+ const net::IPEndPoint local_addr_;
+ net::IPEndPoint remote_addr_;
+ const scoped_ptr<net::UDPSocket> udp_socket_;
+ bool send_pending_;
+ bool receive_pending_;
+ bool client_connected_;
+ net::DiffServCodePoint next_dscp_value_;
+ scoped_ptr<Packet> next_packet_;
+ scoped_refptr<net::WrappedIOBuffer> recv_buf_;
+ net::IPEndPoint recv_addr_;
+ PacketReceiverCallback packet_receiver_;
+ const CastTransportStatusCallback status_callback_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<UdpTransport> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(UdpTransport);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_TRANSPORT_UDP_TRANSPORT_H_
diff --git a/chromium/media/cast/transport/transport/udp_transport_unittest.cc b/chromium/media/cast/transport/transport/udp_transport_unittest.cc
new file mode 100644
index 00000000000..26879492f05
--- /dev/null
+++ b/chromium/media/cast/transport/transport/udp_transport_unittest.cc
@@ -0,0 +1,100 @@
+// 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/transport/transport/udp_transport.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "media/cast/test/utility/net_utility.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+class MockPacketReceiver {
+ public:
+ MockPacketReceiver(const base::Closure& callback)
+ : packet_callback_(callback) {}
+
+ void ReceivedPacket(scoped_ptr<Packet> packet) {
+ packet_ = std::string(packet->size(), '\0');
+ std::copy(packet->begin(), packet->end(), packet_.begin());
+ packet_callback_.Run();
+ }
+
+ std::string packet() const { return packet_; }
+ transport::PacketReceiverCallback packet_receiver() {
+ return base::Bind(&MockPacketReceiver::ReceivedPacket,
+ base::Unretained(this));
+ }
+
+ private:
+ std::string packet_;
+ base::Closure packet_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockPacketReceiver);
+};
+
+void SendPacket(UdpTransport* transport, Packet packet) {
+ base::Closure cb;
+ transport->SendPacket(new base::RefCountedData<Packet>(packet), cb);
+}
+
+static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
+ NOTREACHED();
+}
+
+TEST(UdpTransport, SendAndReceive) {
+ base::MessageLoopForIO message_loop;
+
+ net::IPEndPoint free_local_port1 = test::GetFreeLocalPort();
+ net::IPEndPoint free_local_port2 = test::GetFreeLocalPort();
+ net::IPAddressNumber empty_addr_number;
+ net::ParseIPLiteralToNumber("0.0.0.0", &empty_addr_number);
+
+ UdpTransport send_transport(NULL,
+ message_loop.message_loop_proxy(),
+ free_local_port1,
+ free_local_port2,
+ base::Bind(&UpdateCastTransportStatus));
+ UdpTransport recv_transport(NULL,
+ message_loop.message_loop_proxy(),
+ free_local_port2,
+ net::IPEndPoint(empty_addr_number, 0),
+ base::Bind(&UpdateCastTransportStatus));
+
+ Packet packet;
+ packet.push_back('t');
+ packet.push_back('e');
+ packet.push_back('s');
+ packet.push_back('t');
+
+ base::RunLoop run_loop;
+ MockPacketReceiver receiver1(run_loop.QuitClosure());
+ MockPacketReceiver receiver2(
+ base::Bind(&SendPacket, &recv_transport, packet));
+ send_transport.StartReceiving(receiver1.packet_receiver());
+ recv_transport.StartReceiving(receiver2.packet_receiver());
+
+ base::Closure cb;
+ send_transport.SendPacket(new base::RefCountedData<Packet>(packet), cb);
+ run_loop.Run();
+ EXPECT_TRUE(
+ std::equal(packet.begin(), packet.end(), receiver1.packet().begin()));
+ EXPECT_TRUE(
+ std::equal(packet.begin(), packet.end(), receiver2.packet().begin()));
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/utility/transport_encryption_handler.cc b/chromium/media/cast/transport/utility/transport_encryption_handler.cc
new file mode 100644
index 00000000000..89db2cf95b3
--- /dev/null
+++ b/chromium/media/cast/transport/utility/transport_encryption_handler.cc
@@ -0,0 +1,76 @@
+// 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/transport/utility/transport_encryption_handler.h"
+
+#include "base/logging.h"
+#include "crypto/encryptor.h"
+#include "crypto/symmetric_key.h"
+#include "media/cast/transport/cast_transport_defines.h"
+
+namespace media {
+namespace cast {
+namespace transport {
+
+TransportEncryptionHandler::TransportEncryptionHandler()
+ : key_(), encryptor_(), iv_mask_(), initialized_(false) {}
+
+TransportEncryptionHandler::~TransportEncryptionHandler() {}
+
+bool TransportEncryptionHandler::Initialize(std::string aes_key,
+ std::string aes_iv_mask) {
+ initialized_ = false;
+ if (aes_iv_mask.size() == kAesKeySize && aes_key.size() == kAesKeySize) {
+ iv_mask_ = aes_iv_mask;
+ key_.reset(
+ crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, aes_key));
+ encryptor_.reset(new crypto::Encryptor());
+ encryptor_->Init(key_.get(), crypto::Encryptor::CTR, std::string());
+ initialized_ = true;
+ } else if (aes_iv_mask.size() != 0 || aes_key.size() != 0) {
+ DCHECK_EQ(aes_iv_mask.size(), 0u)
+ << "Invalid Crypto configuration: aes_iv_mask.size";
+ DCHECK_EQ(aes_key.size(), 0u)
+ << "Invalid Crypto configuration: aes_key.size";
+ return false;
+ }
+ return true;
+}
+
+bool TransportEncryptionHandler::Encrypt(uint32 frame_id,
+ const base::StringPiece& data,
+ std::string* encrypted_data) {
+ if (!initialized_)
+ return false;
+ if (!encryptor_->SetCounter(GetAesNonce(frame_id, iv_mask_))) {
+ NOTREACHED() << "Failed to set counter";
+ return false;
+ }
+ if (!encryptor_->Encrypt(data, encrypted_data)) {
+ NOTREACHED() << "Encrypt error";
+ return false;
+ }
+ return true;
+}
+
+bool TransportEncryptionHandler::Decrypt(uint32 frame_id,
+ const base::StringPiece& ciphertext,
+ std::string* plaintext) {
+ if (!initialized_) {
+ return false;
+ }
+ if (!encryptor_->SetCounter(transport::GetAesNonce(frame_id, iv_mask_))) {
+ NOTREACHED() << "Failed to set counter";
+ return false;
+ }
+ if (!encryptor_->Decrypt(ciphertext, plaintext)) {
+ VLOG(1) << "Decryption error";
+ return false;
+ }
+ return true;
+}
+
+} // namespace transport
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/transport/utility/transport_encryption_handler.h b/chromium/media/cast/transport/utility/transport_encryption_handler.h
new file mode 100644
index 00000000000..06d0e3f34d6
--- /dev/null
+++ b/chromium/media/cast/transport/utility/transport_encryption_handler.h
@@ -0,0 +1,58 @@
+// 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_TRANSPORT_TRANSPORT_UTILITY_ENCRYPTION_HANDLER_H_
+#define MEDIA_CAST_TRANSPORT_TRANSPORT_UTILITY_ENCRYPTION_HANDLER_H_
+
+// Helper class to handle encryption for the Cast Transport library.
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/non_thread_safe.h"
+
+namespace crypto {
+class Encryptor;
+class SymmetricKey;
+}
+
+namespace media {
+namespace cast {
+namespace transport {
+
+class TransportEncryptionHandler : public base::NonThreadSafe {
+ public:
+ TransportEncryptionHandler();
+ ~TransportEncryptionHandler();
+
+ bool Initialize(std::string aes_key, std::string aes_iv_mask);
+
+ bool Encrypt(uint32 frame_id,
+ const base::StringPiece& data,
+ std::string* encrypted_data);
+
+ bool Decrypt(uint32 frame_id,
+ const base::StringPiece& ciphertext,
+ std::string* plaintext);
+
+ // TODO(miu): This naming is very misleading. It should be called
+ // is_activated() since Initialize() without keys (i.e., cypto is disabled)
+ // may have succeeded.
+ bool initialized() const { return initialized_; }
+
+ private:
+ scoped_ptr<crypto::SymmetricKey> key_;
+ scoped_ptr<crypto::Encryptor> encryptor_;
+ std::string iv_mask_;
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransportEncryptionHandler);
+};
+
+} // namespace transport
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_TRANSPORT_TRANSPORT_UTILITY_ENCRYPTION_HANDLER_H_
diff --git a/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.cc b/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.cc
deleted file mode 100644
index 10fcb85d36e..00000000000
--- a/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.cc
+++ /dev/null
@@ -1,106 +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/codecs/vp8/vp8_decoder.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "media/base/video_frame.h"
-#include "media/base/video_util.h"
-#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
-#include "ui/gfx/size.h"
-
-namespace media {
-namespace cast {
-
-void LogFrameDecodedEvent(CastEnvironment* const cast_environment,
- uint32 frame_id) {
-// TODO(mikhal): Sort out passing of rtp_timestamp.
-// cast_environment->Logging()->InsertFrameEvent(kVideoFrameDecoded,
-// 0, frame_id);
-}
-
-Vp8Decoder::Vp8Decoder(scoped_refptr<CastEnvironment> cast_environment)
- : decoder_(new vpx_dec_ctx_t()),
- cast_environment_(cast_environment) {
- // Make sure that we initialize the decoder from the correct thread.
- cast_environment_->PostTask(CastEnvironment::VIDEO_DECODER, FROM_HERE,
- base::Bind(&Vp8Decoder::InitDecoder, base::Unretained(this)));
-}
-
-Vp8Decoder::~Vp8Decoder() {}
-
-void Vp8Decoder::InitDecoder() {
- vpx_codec_dec_cfg_t cfg;
- // Initializing to use one core.
- cfg.threads = 1;
- vpx_codec_flags_t flags = VPX_CODEC_USE_POSTPROC;
-
- if (vpx_codec_dec_init(decoder_.get(), vpx_codec_vp8_dx(), &cfg, flags)) {
- DCHECK(false) << "VP8 decode error";
- }
-}
-
-bool Vp8Decoder::Decode(const EncodedVideoFrame* encoded_frame,
- const base::TimeTicks render_time,
- const VideoFrameDecodedCallback& frame_decoded_cb) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::VIDEO_DECODER));
- const int frame_id_int = static_cast<int>(encoded_frame->frame_id);
- VLOG(1) << "VP8 decode frame:" << frame_id_int
- << " sized:" << encoded_frame->data.size();
-
- if (encoded_frame->data.empty()) return false;
-
- vpx_codec_iter_t iter = NULL;
- vpx_image_t* img;
- if (vpx_codec_decode(
- decoder_.get(),
- reinterpret_cast<const uint8*>(encoded_frame->data.data()),
- static_cast<unsigned int>(encoded_frame->data.size()),
- 0,
- 1 /* real time*/)) {
- VLOG(1) << "Failed to decode VP8 frame.";
- return false;
- }
-
- img = vpx_codec_get_frame(decoder_.get(), &iter);
- if (img == NULL) {
- VLOG(1) << "Skip rendering VP8 frame:" << frame_id_int;
- return false;
- }
-
- gfx::Size visible_size(img->d_w, img->d_h);
- gfx::Size full_size(img->stride[VPX_PLANE_Y], img->d_h);
- DCHECK(VideoFrame::IsValidConfig(VideoFrame::I420, visible_size,
- gfx::Rect(visible_size), full_size));
- // Temp timing setting - will sort out timing in a follow up cl.
- scoped_refptr<VideoFrame> decoded_frame =
- VideoFrame::CreateFrame(VideoFrame::I420, visible_size,
- gfx::Rect(visible_size), full_size, base::TimeDelta());
-
- // Copy each plane individually (need to account for stride).
- // TODO(mikhal): Eliminate copy once http://crbug.com/321856 is resolved.
- CopyPlane(VideoFrame::kYPlane, img->planes[VPX_PLANE_Y],
- img->stride[VPX_PLANE_Y], img->d_h, decoded_frame.get());
- CopyPlane(VideoFrame::kUPlane, img->planes[VPX_PLANE_U],
- img->stride[VPX_PLANE_U], (img->d_h + 1) / 2, decoded_frame.get());
- CopyPlane(VideoFrame::kVPlane, img->planes[VPX_PLANE_V],
- img->stride[VPX_PLANE_V], (img->d_h + 1) / 2, decoded_frame.get());
-
- // Log:: Decoding complete (should be called from the main thread).
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, base::Bind(
- LogFrameDecodedEvent, cast_environment_,encoded_frame->frame_id));
-
- VLOG(1) << "Decoded frame " << frame_id_int;
- // Frame decoded - return frame to the user via callback.
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(frame_decoded_cb, decoded_frame, render_time));
-
- return true;
-}
-
-} // namespace cast
-} // namespace media
-
diff --git a/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.gyp b/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.gyp
deleted file mode 100644
index 4bc9434d2d2..00000000000
--- a/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.gyp
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of the source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_vp8_decoder',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/libvpx/',
- ],
- 'sources': [
- 'vp8_decoder.cc',
- 'vp8_decoder.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:base',
- '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.h b/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.h
deleted file mode 100644
index 6a93c41abc9..00000000000
--- a/chromium/media/cast/video_receiver/codecs/vp8/vp8_decoder.h
+++ /dev/null
@@ -1,46 +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_RTP_RECEVIER_CODECS_VP8_VP8_DECODER_H_
-#define MEDIA_CAST_RTP_RECEVIER_CODECS_VP8_VP8_DECODER_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/cast_receiver.h"
-#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
-
-typedef struct vpx_codec_ctx vpx_dec_ctx_t;
-
-// TODO(mikhal): Look into reusing VpxVideoDecoder.
-namespace media {
-namespace cast {
-
-// This class is not thread safe; it's only called from the cast video decoder
-// thread.
-class Vp8Decoder : public base::NonThreadSafe {
- public:
- explicit Vp8Decoder(scoped_refptr<CastEnvironment> cast_environment);
- ~Vp8Decoder();
-
- // Decode frame - The decoded frame will be passed via the callback.
- // Will return false in case of error, and then it's up to the caller to
- // release the memory.
- // Ownership of the encoded_frame does not pass to the Vp8Decoder.
- bool Decode(const EncodedVideoFrame* encoded_frame,
- const base::TimeTicks render_time,
- const VideoFrameDecodedCallback& frame_decoded_cb);
-
- private:
- // Initialize the decoder.
- void InitDecoder();
- scoped_ptr<vpx_dec_ctx_t> decoder_;
- scoped_refptr<CastEnvironment> cast_environment_;
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_RTP_RECEVIER_CODECS_VP8_VP8_DECODER_H_
diff --git a/chromium/media/cast/video_receiver/video_decoder.cc b/chromium/media/cast/video_receiver/video_decoder.cc
deleted file mode 100644
index 360cdaa36e9..00000000000
--- a/chromium/media/cast/video_receiver/video_decoder.cc
+++ /dev/null
@@ -1,44 +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_decoder.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "media/cast/video_receiver/codecs/vp8/vp8_decoder.h"
-
-namespace media {
-namespace cast {
-
-VideoDecoder::VideoDecoder(const VideoReceiverConfig& video_config,
- scoped_refptr<CastEnvironment> cast_environment)
- : codec_(video_config.codec),
- vp8_decoder_() {
- switch (video_config.codec) {
- case kVp8:
- vp8_decoder_.reset(new Vp8Decoder(cast_environment));
- break;
- case kH264:
- NOTIMPLEMENTED();
- break;
- case kExternalVideo:
- DCHECK(false) << "Invalid codec";
- break;
- }
-}
-
-VideoDecoder::~VideoDecoder() {}
-
-bool VideoDecoder::DecodeVideoFrame(const EncodedVideoFrame* encoded_frame,
- const base::TimeTicks render_time,
- const VideoFrameDecodedCallback&
- frame_decoded_cb) {
- DCHECK(encoded_frame->codec == codec_) << "Invalid codec";
- DCHECK_GT(encoded_frame->data.size(), GG_UINT64_C(0)) << "Empty video frame";
- return vp8_decoder_->Decode(encoded_frame, render_time, frame_decoded_cb);
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/video_receiver/video_decoder.h b/chromium/media/cast/video_receiver/video_decoder.h
deleted file mode 100644
index 97a8a62cc70..00000000000
--- a/chromium/media/cast/video_receiver/video_decoder.h
+++ /dev/null
@@ -1,43 +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_VIDEO_RECEIVER_VIDEO_DECODER_H_
-#define MEDIA_CAST_VIDEO_RECEIVER_VIDEO_DECODER_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/cast_receiver.h"
-
-namespace media {
-namespace cast {
-
-class Vp8Decoder;
-class VideoFrame;
-
-// This class is not thread safe; it's only called from the cast video decoder
-// thread.
-class VideoDecoder : public base::NonThreadSafe {
- public:
- VideoDecoder(const VideoReceiverConfig& video_config,
- scoped_refptr<CastEnvironment> cast_environment);
- virtual ~VideoDecoder();
-
- // Decode a video frame. Decoded (raw) frame will be returned via the
- // provided callback
- bool DecodeVideoFrame(const EncodedVideoFrame* encoded_frame,
- const base::TimeTicks render_time,
- const VideoFrameDecodedCallback& frame_decoded_cb);
-
- private:
- VideoCodec codec_;
- scoped_ptr<Vp8Decoder> vp8_decoder_;
-
- DISALLOW_COPY_AND_ASSIGN(VideoDecoder);
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_VIDEO_RECEIVER_VIDEO_DECODER_H_
diff --git a/chromium/media/cast/video_receiver/video_decoder_unittest.cc b/chromium/media/cast/video_receiver/video_decoder_unittest.cc
deleted file mode 100644
index 6405d1d7bee..00000000000
--- a/chromium/media/cast/video_receiver/video_decoder_unittest.cc
+++ /dev/null
@@ -1,94 +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 "base/bind.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/time/tick_clock.h"
-#include "media/cast/cast_config.h"
-#include "media/cast/cast_defines.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/cast_receiver.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "media/cast/video_receiver/video_decoder.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-using testing::_;
-
-// Random frame size for testing.
-const int kFrameSize = 2345;
-static const int64 kStartMillisecond = GG_INT64_C(1245);
-
-namespace {
-class DecodeTestFrameCallback :
- public base::RefCountedThreadSafe<DecodeTestFrameCallback> {
- public:
- DecodeTestFrameCallback() {}
-
- void DecodeComplete(const scoped_refptr<media::VideoFrame>& decoded_frame,
- const base::TimeTicks& render_time) {}
- protected:
- virtual ~DecodeTestFrameCallback() {}
- private:
- friend class base::RefCountedThreadSafe<DecodeTestFrameCallback>;
-};
-} // namespace
-
-class VideoDecoderTest : public ::testing::Test {
- protected:
- VideoDecoderTest()
- : task_runner_(new test::FakeTaskRunner(&testing_clock_)),
- cast_environment_(new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig())),
- test_callback_(new DecodeTestFrameCallback()) {
- // Configure to vp8.
- config_.codec = kVp8;
- config_.use_external_decoder = false;
- decoder_.reset(new VideoDecoder(config_, cast_environment_));
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- }
-
- virtual ~VideoDecoderTest() {}
-
- scoped_ptr<VideoDecoder> decoder_;
- VideoReceiverConfig config_;
- base::SimpleTestTickClock testing_clock_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_refptr<DecodeTestFrameCallback> test_callback_;
-};
-
-// TODO(pwestin): EXPECT_DEATH tests can not pass valgrind.
-TEST_F(VideoDecoderTest, DISABLED_SizeZero) {
- EncodedVideoFrame encoded_frame;
- base::TimeTicks render_time;
- encoded_frame.codec = kVp8;
- EXPECT_DEATH(
- decoder_->DecodeVideoFrame(
- &encoded_frame, render_time,
- base::Bind(&DecodeTestFrameCallback::DecodeComplete, test_callback_)),
- "Empty frame");
-}
-
-// TODO(pwestin): EXPECT_DEATH tests can not pass valgrind.
-TEST_F(VideoDecoderTest, DISABLED_InvalidCodec) {
- EncodedVideoFrame encoded_frame;
- base::TimeTicks render_time;
- encoded_frame.data.assign(kFrameSize, 0);
- encoded_frame.codec = kExternalVideo;
- EXPECT_DEATH(
- decoder_->DecodeVideoFrame(&encoded_frame, render_time, base::Bind(
- &DecodeTestFrameCallback::DecodeComplete, test_callback_)),
- "Invalid codec");
-}
-
-// TODO(pwestin): Test decoding a real frame.
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/video_receiver/video_receiver.cc b/chromium/media/cast/video_receiver/video_receiver.cc
deleted file mode 100644
index 98bed1fc699..00000000000
--- a/chromium/media/cast/video_receiver/video_receiver.cc
+++ /dev/null
@@ -1,465 +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/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "crypto/encryptor.h"
-#include "crypto/symmetric_key.h"
-#include "media/cast/cast_defines.h"
-#include "media/cast/framer/framer.h"
-#include "media/cast/video_receiver/video_decoder.h"
-
-namespace media {
-namespace cast {
-
-const int64 kMinSchedulingDelayMs = 1;
-
-static const int64 kMinTimeBetweenOffsetUpdatesMs = 2000;
-static const int kTimeOffsetFilter = 8;
-static const int64_t kMinProcessIntervalMs = 5;
-
-// Local implementation of RtpData (defined in rtp_rtcp_defines.h).
-// Used to pass payload data into the video receiver.
-class LocalRtpVideoData : public RtpData {
- public:
- explicit LocalRtpVideoData(VideoReceiver* video_receiver)
- : video_receiver_(video_receiver) {}
-
- virtual ~LocalRtpVideoData() {}
-
- virtual void OnReceivedPayloadData(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader* rtp_header) OVERRIDE {
- video_receiver_->IncomingParsedRtpPacket(payload_data, payload_size,
- *rtp_header);
- }
-
- private:
- VideoReceiver* video_receiver_;
-};
-
-// Local implementation of RtpPayloadFeedback (defined in rtp_defines.h)
-// Used to convey cast-specific feedback from receiver to sender.
-// Callback triggered by the Framer (cast message builder).
-class LocalRtpVideoFeedback : public RtpPayloadFeedback {
- public:
- explicit LocalRtpVideoFeedback(VideoReceiver* video_receiver)
- : video_receiver_(video_receiver) {
- }
-
- virtual void CastFeedback(const RtcpCastMessage& cast_message) OVERRIDE {
- video_receiver_->CastFeedback(cast_message);
- }
-
- private:
- VideoReceiver* video_receiver_;
-};
-
-// Local implementation of RtpReceiverStatistics (defined by rtcp.h).
-// Used to pass statistics data from the RTP module to the RTCP module.
-class LocalRtpReceiverStatistics : public RtpReceiverStatistics {
- public:
- explicit LocalRtpReceiverStatistics(RtpReceiver* rtp_receiver)
- : rtp_receiver_(rtp_receiver) {
- }
-
- virtual void GetStatistics(uint8* fraction_lost,
- uint32* cumulative_lost, // 24 bits valid.
- uint32* extended_high_sequence_number,
- uint32* jitter) OVERRIDE {
- rtp_receiver_->GetStatistics(fraction_lost,
- cumulative_lost,
- extended_high_sequence_number,
- jitter);
- }
-
- private:
- RtpReceiver* rtp_receiver_;
-};
-
-VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment,
- const VideoReceiverConfig& video_config,
- PacedPacketSender* const packet_sender)
- : cast_environment_(cast_environment),
- codec_(video_config.codec),
- target_delay_delta_(
- base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)),
- frame_delay_(base::TimeDelta::FromMilliseconds(
- 1000 / video_config.max_frame_rate)),
- incoming_payload_callback_(new LocalRtpVideoData(this)),
- incoming_payload_feedback_(new LocalRtpVideoFeedback(this)),
- rtp_receiver_(cast_environment_->Clock(), NULL, &video_config,
- incoming_payload_callback_.get()),
- rtp_video_receiver_statistics_(
- new LocalRtpReceiverStatistics(&rtp_receiver_)),
- time_incoming_packet_updated_(false),
- incoming_rtp_timestamp_(0),
- weak_factory_(this) {
- int max_unacked_frames = video_config.rtp_max_delay_ms *
- video_config.max_frame_rate / 1000;
- DCHECK(max_unacked_frames) << "Invalid argument";
-
- if (video_config.aes_iv_mask.size() == kAesKeySize &&
- video_config.aes_key.size() == kAesKeySize) {
- iv_mask_ = video_config.aes_iv_mask;
- crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
- crypto::SymmetricKey::AES, video_config.aes_key);
- decryptor_.reset(new crypto::Encryptor());
- decryptor_->Init(key, crypto::Encryptor::CTR, std::string());
- } else if (video_config.aes_iv_mask.size() != 0 ||
- video_config.aes_key.size() != 0) {
- DCHECK(false) << "Invalid crypto configuration";
- }
-
- framer_.reset(new Framer(cast_environment->Clock(),
- incoming_payload_feedback_.get(),
- video_config.incoming_ssrc,
- video_config.decoder_faster_than_max_frame_rate,
- max_unacked_frames));
- if (!video_config.use_external_decoder) {
- video_decoder_.reset(new VideoDecoder(video_config, cast_environment));
- }
-
- rtcp_.reset(
- new Rtcp(cast_environment_,
- NULL,
- packet_sender,
- NULL,
- rtp_video_receiver_statistics_.get(),
- video_config.rtcp_mode,
- base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
- video_config.feedback_ssrc,
- video_config.incoming_ssrc,
- video_config.rtcp_c_name));
-}
-
-VideoReceiver::~VideoReceiver() {}
-
-void VideoReceiver::InitializeTimers() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- ScheduleNextRtcpReport();
- ScheduleNextCastMessage();
-}
-
-void VideoReceiver::GetRawVideoFrame(
- const VideoFrameDecodedCallback& callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- GetEncodedVideoFrame(base::Bind(&VideoReceiver::DecodeVideoFrame,
- base::Unretained(this), callback));
-}
-
-// Called when we have a frame to decode.
-void VideoReceiver::DecodeVideoFrame(
- const VideoFrameDecodedCallback& callback,
- scoped_ptr<EncodedVideoFrame> encoded_frame,
- const base::TimeTicks& render_time) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- // Hand the ownership of the encoded frame to the decode thread.
- cast_environment_->PostTask(CastEnvironment::VIDEO_DECODER, FROM_HERE,
- base::Bind(&VideoReceiver::DecodeVideoFrameThread, base::Unretained(this),
- base::Passed(&encoded_frame), render_time, callback));
-}
-
-// Utility function to run the decoder on a designated decoding thread.
-void VideoReceiver::DecodeVideoFrameThread(
- scoped_ptr<EncodedVideoFrame> encoded_frame,
- const base::TimeTicks render_time,
- const VideoFrameDecodedCallback& frame_decoded_callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::VIDEO_DECODER));
- DCHECK(video_decoder_);
-
- if (!(video_decoder_->DecodeVideoFrame(encoded_frame.get(), render_time,
- frame_decoded_callback))) {
- // This will happen if we decide to decode but not show a frame.
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoReceiver::GetRawVideoFrame, base::Unretained(this),
- frame_decoded_callback));
- }
-}
-
-bool VideoReceiver::DecryptVideoFrame(
- scoped_ptr<EncodedVideoFrame>* video_frame) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(decryptor_) << "Invalid state";
-
- if (!decryptor_->SetCounter(GetAesNonce((*video_frame)->frame_id,
- iv_mask_))) {
- NOTREACHED() << "Failed to set counter";
- return false;
- }
- std::string decrypted_video_data;
- if (!decryptor_->Decrypt((*video_frame)->data, &decrypted_video_data)) {
- VLOG(1) << "Decryption error";
- // Give up on this frame, release it from jitter buffer.
- framer_->ReleaseFrame((*video_frame)->frame_id);
- return false;
- }
- (*video_frame)->data.swap(decrypted_video_data);
- return true;
-}
-
-// Called from the main cast thread.
-void VideoReceiver::GetEncodedVideoFrame(
- const VideoFrameEncodedCallback& callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame());
- uint32 rtp_timestamp = 0;
- bool next_frame = false;
-
- if (!framer_->GetEncodedVideoFrame(encoded_frame.get(), &rtp_timestamp,
- &next_frame)) {
- // We have no video frames. Wait for new packet(s).
- queued_encoded_callbacks_.push_back(callback);
- return;
- }
-
- if (decryptor_ && !DecryptVideoFrame(&encoded_frame)) {
- // Logging already done.
- queued_encoded_callbacks_.push_back(callback);
- return;
- }
-
- base::TimeTicks render_time;
- if (PullEncodedVideoFrame(rtp_timestamp, next_frame, &encoded_frame,
- &render_time)) {
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(callback, base::Passed(&encoded_frame), render_time));
- } else {
- // We have a video frame; however we are missing packets and we have time
- // to wait for new packet(s).
- queued_encoded_callbacks_.push_back(callback);
- }
-}
-
-// Should we pull the encoded video frame from the framer? decided by if this is
-// the next frame or we are running out of time and have to pull the following
-// frame.
-// If the frame is too old to be rendered we set the don't show flag in the
-// video bitstream where possible.
-bool VideoReceiver::PullEncodedVideoFrame(uint32 rtp_timestamp,
- bool next_frame, scoped_ptr<EncodedVideoFrame>* encoded_frame,
- base::TimeTicks* render_time) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
- *render_time = GetRenderTime(now, rtp_timestamp);
-
- // TODO(mikhal): Store actual render time and not diff.
- cast_environment_->Logging()->InsertFrameEventWithDelay(kVideoRenderDelay,
- rtp_timestamp, (*encoded_frame)->frame_id, now - *render_time);
-
- // Minimum time before a frame is due to be rendered before we pull it for
- // decode.
- base::TimeDelta min_wait_delta = frame_delay_;
- base::TimeDelta time_until_render = *render_time - now;
- if (!next_frame && (time_until_render > min_wait_delta)) {
- // Example:
- // We have decoded frame 1 and we have received the complete frame 3, but
- // not frame 2. If we still have time before frame 3 should be rendered we
- // will wait for 2 to arrive, however if 2 never show up this timer will hit
- // and we will pull out frame 3 for decoding and rendering.
- base::TimeDelta time_until_release = time_until_render - min_wait_delta;
- cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoReceiver::PlayoutTimeout, weak_factory_.GetWeakPtr()),
- time_until_release);
- VLOG(1) << "Wait before releasing frame "
- << static_cast<int>((*encoded_frame)->frame_id)
- << " time " << time_until_release.InMilliseconds();
- return false;
- }
-
- base::TimeDelta dont_show_timeout_delta =
- base::TimeDelta::FromMilliseconds(-kDontShowTimeoutMs);
- if (codec_ == kVp8 && time_until_render < dont_show_timeout_delta) {
- (*encoded_frame)->data[0] &= 0xef;
- VLOG(1) << "Don't show frame "
- << static_cast<int>((*encoded_frame)->frame_id)
- << " time_until_render:" << time_until_render.InMilliseconds();
- } else {
- VLOG(1) << "Show frame "
- << static_cast<int>((*encoded_frame)->frame_id)
- << " time_until_render:" << time_until_render.InMilliseconds();
- }
- // We have a copy of the frame, release this one.
- framer_->ReleaseFrame((*encoded_frame)->frame_id);
- (*encoded_frame)->codec = codec_;
- return true;
-}
-
-void VideoReceiver::PlayoutTimeout() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (queued_encoded_callbacks_.empty()) return;
-
- uint32 rtp_timestamp = 0;
- bool next_frame = false;
- scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame());
-
- if (!framer_->GetEncodedVideoFrame(encoded_frame.get(), &rtp_timestamp,
- &next_frame)) {
- // We have no video frames. Wait for new packet(s).
- // Since the application can post multiple VideoFrameEncodedCallback and
- // we only check the next frame to play out we might have multiple timeout
- // events firing after each other; however this should be a rare event.
- VLOG(1) << "Failed to retrieved a complete frame at this point in time";
- return;
- }
- VLOG(1) << "PlayoutTimeout retrieved frame "
- << static_cast<int>(encoded_frame->frame_id);
-
- if (decryptor_ && !DecryptVideoFrame(&encoded_frame)) {
- // Logging already done.
- return;
- }
-
- base::TimeTicks render_time;
- if (PullEncodedVideoFrame(rtp_timestamp, next_frame, &encoded_frame,
- &render_time)) {
- if (!queued_encoded_callbacks_.empty()) {
- VideoFrameEncodedCallback callback = queued_encoded_callbacks_.front();
- queued_encoded_callbacks_.pop_front();
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(callback, base::Passed(&encoded_frame), render_time));
- }
- } else {
- // We have a video frame; however we are missing packets and we have time
- // to wait for new packet(s).
- }
-}
-
-base::TimeTicks VideoReceiver::GetRenderTime(base::TimeTicks now,
- uint32 rtp_timestamp) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- // Senders time in ms when this frame was captured.
- // Note: the senders clock and our local clock might not be synced.
- base::TimeTicks rtp_timestamp_in_ticks;
-
- if (time_offset_.InMilliseconds() == 0) {
- if (!rtcp_->RtpTimestampInSenderTime(kVideoFrequency,
- incoming_rtp_timestamp_,
- &rtp_timestamp_in_ticks)) {
- // We have not received any RTCP to sync the stream play it out as soon as
- // possible.
- return now;
- }
- time_offset_ = time_incoming_packet_ - rtp_timestamp_in_ticks;
- } else if (time_incoming_packet_updated_) {
- if (rtcp_->RtpTimestampInSenderTime(kVideoFrequency,
- incoming_rtp_timestamp_,
- &rtp_timestamp_in_ticks)) {
- // Time to update the time_offset.
- base::TimeDelta time_offset =
- time_incoming_packet_ - rtp_timestamp_in_ticks;
- time_offset_ = ((kTimeOffsetFilter - 1) * time_offset_ + time_offset)
- / kTimeOffsetFilter;
- }
- }
- // Reset |time_incoming_packet_updated_| to enable a future measurement.
- time_incoming_packet_updated_ = false;
- if (!rtcp_->RtpTimestampInSenderTime(kVideoFrequency,
- rtp_timestamp,
- &rtp_timestamp_in_ticks)) {
- // This can fail if we have not received any RTCP packets in a long time.
- return now;
- }
- return (rtp_timestamp_in_ticks + time_offset_ + target_delay_delta_);
-}
-
-void VideoReceiver::IncomingPacket(const uint8* packet, size_t length,
- const base::Closure callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (Rtcp::IsRtcpPacket(packet, length)) {
- rtcp_->IncomingRtcpPacket(packet, length);
- } else {
- rtp_receiver_.ReceivedPacket(packet, length);
- }
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
-}
-
-void VideoReceiver::IncomingParsedRtpPacket(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader& rtp_header) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
-
- base::TimeTicks now = cast_environment_->Clock()->NowTicks();
- if (time_incoming_packet_.is_null() || now - time_incoming_packet_ >
- base::TimeDelta::FromMilliseconds(kMinTimeBetweenOffsetUpdatesMs)) {
- if (time_incoming_packet_.is_null()) InitializeTimers();
- incoming_rtp_timestamp_ = rtp_header.webrtc.header.timestamp;
- time_incoming_packet_ = now;
- time_incoming_packet_updated_ = true;
- }
-
- cast_environment_->Logging()->InsertPacketEvent(kPacketReceived,
- rtp_header.webrtc.header.timestamp, rtp_header.frame_id,
- rtp_header.packet_id, rtp_header.max_packet_id, payload_size);
-
- bool complete = framer_->InsertPacket(payload_data, payload_size, rtp_header);
-
- if (!complete) return; // Video frame not complete; wait for more packets.
- if (queued_encoded_callbacks_.empty()) return; // No pending callback.
-
- VideoFrameEncodedCallback callback = queued_encoded_callbacks_.front();
- queued_encoded_callbacks_.pop_front();
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoReceiver::GetEncodedVideoFrame,
- weak_factory_.GetWeakPtr(), callback));
-}
-
-// 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));
- // TODO(pwestin): wire up log messages.
- rtcp_->SendRtcpFromRtpReceiver(&cast_message, NULL);
- time_last_sent_cast_message_= cast_environment_->Clock()->NowTicks();
-}
-
-// 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();
-}
-
-// Schedule the next RTCP report to be sent back to the sender.
-void VideoReceiver::ScheduleNextRtcpReport() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- base::TimeDelta time_to_next = rtcp_->TimeToSendNextRtcpReport() -
- cast_environment_->Clock()->NowTicks();
-
- 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/chromium/media/cast/video_receiver/video_receiver.gypi b/chromium/media/cast/video_receiver/video_receiver.gypi
deleted file mode 100644
index e1a9902872e..00000000000
--- a/chromium/media/cast/video_receiver/video_receiver.gypi
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of the source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
- 'targets': [
- {
- 'target_name': 'cast_video_receiver',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- '<(DEPTH)/third_party/webrtc',
- ],
- 'sources': [
- 'video_decoder.h',
- 'video_decoder.cc',
- 'video_receiver.h',
- 'video_receiver.cc',
- ], # source
- 'dependencies': [
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- 'framer/framer.gyp:cast_framer',
- 'video_receiver/codecs/vp8/vp8_decoder.gyp:cast_vp8_decoder',
- 'rtp_receiver/rtp_receiver.gyp:cast_rtp_receiver',
- ],
- },
- ],
-}
-
-
diff --git a/chromium/media/cast/video_receiver/video_receiver.h b/chromium/media/cast/video_receiver/video_receiver.h
deleted file mode 100644
index fbc3653a514..00000000000
--- a/chromium/media/cast/video_receiver/video_receiver.h
+++ /dev/null
@@ -1,133 +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_VIDEO_RECEIVER_VIDEO_RECEIVER_H_
-#define MEDIA_CAST_VIDEO_RECEIVER_VIDEO_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/cast_config.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/cast_receiver.h"
-#include "media/cast/rtcp/rtcp.h"
-#include "media/cast/rtp_receiver/rtp_receiver.h"
-#include "media/cast/rtp_receiver/rtp_receiver_defines.h"
-
-namespace crypto {
- class Encryptor;
-}
-
-namespace media {
-namespace cast {
-
-class Framer;
-class LocalRtpVideoData;
-class LocalRtpVideoFeedback;
-class PacedPacketSender;
-class PeerVideoReceiver;
-class Rtcp;
-class RtpReceiverStatistics;
-class VideoDecoder;
-
-// Should only be called from the Main cast thread.
-class VideoReceiver : public base::NonThreadSafe,
- public base::SupportsWeakPtr<VideoReceiver> {
- public:
- VideoReceiver(scoped_refptr<CastEnvironment> cast_environment,
- const VideoReceiverConfig& video_config,
- PacedPacketSender* const packet_sender);
-
- virtual ~VideoReceiver();
-
- // Request a raw frame. Will return frame via callback when available.
- void GetRawVideoFrame(const VideoFrameDecodedCallback& callback);
-
- // Request an encoded frame. Will return frame via callback when available.
- void GetEncodedVideoFrame(const VideoFrameEncodedCallback& callback);
-
- // Insert a RTP packet to the video receiver.
- void IncomingPacket(const uint8* packet, size_t length,
- const base::Closure callback);
-
- protected:
- void IncomingParsedRtpPacket(const uint8* payload_data,
- size_t payload_size,
- const RtpCastHeader& rtp_header);
-
- void DecodeVideoFrameThread(
- scoped_ptr<EncodedVideoFrame> encoded_frame,
- const base::TimeTicks render_time,
- const VideoFrameDecodedCallback& frame_decoded_callback);
-
- private:
- friend class LocalRtpVideoData;
- friend class LocalRtpVideoFeedback;
-
- void CastFeedback(const RtcpCastMessage& cast_message);
-
- void DecodeVideoFrame(const VideoFrameDecodedCallback& callback,
- scoped_ptr<EncodedVideoFrame> encoded_frame,
- const base::TimeTicks& render_time);
-
- bool DecryptVideoFrame(scoped_ptr<EncodedVideoFrame>* video_frame);
-
- bool PullEncodedVideoFrame(uint32 rtp_timestamp,
- bool next_frame,
- scoped_ptr<EncodedVideoFrame>* encoded_frame,
- base::TimeTicks* render_time);
-
- void PlayoutTimeout();
-
- // Returns Render time based on current time and the rtp timestamp.
- base::TimeTicks GetRenderTime(base::TimeTicks now, uint32 rtp_timestamp);
-
- void InitializeTimers();
-
- // Schedule timing for the next cast message.
- void ScheduleNextCastMessage();
-
- // Schedule timing for the next RTCP report.
- void ScheduleNextRtcpReport();
-
- // Actually send the next cast message.
- void SendNextCastMessage();
-
- // Actually send the next RTCP report.
- void SendNextRtcpReport();
-
- scoped_ptr<VideoDecoder> video_decoder_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_ptr<Framer> framer_;
- const VideoCodec codec_;
- base::TimeDelta target_delay_delta_;
- base::TimeDelta frame_delay_;
- scoped_ptr<LocalRtpVideoData> incoming_payload_callback_;
- scoped_ptr<LocalRtpVideoFeedback> incoming_payload_feedback_;
- RtpReceiver rtp_receiver_;
- scoped_ptr<Rtcp> rtcp_;
- scoped_ptr<RtpReceiverStatistics> rtp_video_receiver_statistics_;
- base::TimeTicks time_last_sent_cast_message_;
- base::TimeDelta time_offset_; // Sender-receiver offset estimation.
- scoped_ptr<crypto::Encryptor> decryptor_;
- std::string iv_mask_;
- std::list<VideoFrameEncodedCallback> queued_encoded_callbacks_;
- bool time_incoming_packet_updated_;
- base::TimeTicks time_incoming_packet_;
- uint32 incoming_rtp_timestamp_;
-
- base::WeakPtrFactory<VideoReceiver> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(VideoReceiver);
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_VIDEO_RECEIVER_VIDEO_RECEIVER_H_
diff --git a/chromium/media/cast/video_receiver/video_receiver_unittest.cc b/chromium/media/cast/video_receiver/video_receiver_unittest.cc
deleted file mode 100644
index 8001ac430d6..00000000000
--- a/chromium/media/cast/video_receiver/video_receiver_unittest.cc
+++ /dev/null
@@ -1,169 +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 "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/net/pacing/mock_paced_packet_sender.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "media/cast/video_receiver/video_receiver.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-static const int kPacketSize = 1500;
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
-
-namespace media {
-namespace cast {
-
-using testing::_;
-
-namespace {
-// Was thread counted thread safe.
-class TestVideoReceiverCallback :
- public base::RefCountedThreadSafe<TestVideoReceiverCallback> {
- public:
- TestVideoReceiverCallback()
- : num_called_(0) {}
-
- // TODO(mikhal): Set and check expectations.
- void DecodeComplete(const scoped_refptr<media::VideoFrame>& video_frame,
- const base::TimeTicks& render_time) {
- ++num_called_;
- }
-
- void FrameToDecode(scoped_ptr<EncodedVideoFrame> video_frame,
- const base::TimeTicks& render_time) {
- EXPECT_TRUE(video_frame->key_frame);
- EXPECT_EQ(kVp8, video_frame->codec);
- ++num_called_;
- }
-
- int number_times_called() const { return num_called_;}
-
- protected:
- virtual ~TestVideoReceiverCallback() {}
-
- private:
- friend class base::RefCountedThreadSafe<TestVideoReceiverCallback>;
-
- int num_called_;
-};
-} // namespace
-
-class PeerVideoReceiver : public VideoReceiver {
- public:
- PeerVideoReceiver(scoped_refptr<CastEnvironment> cast_environment,
- const VideoReceiverConfig& video_config,
- PacedPacketSender* const packet_sender)
- : VideoReceiver(cast_environment, video_config, packet_sender) {
- }
- using VideoReceiver::IncomingParsedRtpPacket;
-};
-
-
-class VideoReceiverTest : public ::testing::Test {
- protected:
- VideoReceiverTest() {
- // Configure to use vp8 software implementation.
- config_.codec = kVp8;
- config_.use_external_decoder = false;
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
- receiver_.reset(new
- PeerVideoReceiver(cast_environment_, config_, &mock_transport_));
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
- video_receiver_callback_ = new TestVideoReceiverCallback();
- }
-
- 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 = 0;
- rtp_header_.packet_id = 0;
- rtp_header_.max_packet_id = 0;
- rtp_header_.is_reference = false;
- rtp_header_.reference_frame_id = 0;
- }
-
- MockPacedPacketSender mock_transport_;
- VideoReceiverConfig config_;
- scoped_ptr<PeerVideoReceiver> receiver_;
- std::vector<uint8> payload_;
- RtpCastHeader rtp_header_;
- base::SimpleTestTickClock testing_clock_;
-
- scoped_refptr<test::FakeTaskRunner> task_runner_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_refptr<TestVideoReceiverCallback> video_receiver_callback_;
-};
-
-TEST_F(VideoReceiverTest, GetOnePacketEncodedframe) {
- EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).WillRepeatedly(
- testing::Return(true));
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
-
- VideoFrameEncodedCallback frame_to_decode_callback =
- base::Bind(&TestVideoReceiverCallback::FrameToDecode,
- video_receiver_callback_);
-
- receiver_->GetEncodedVideoFrame(frame_to_decode_callback);
- task_runner_->RunTasks();
- EXPECT_EQ(video_receiver_callback_->number_times_called(), 1);
-}
-
-TEST_F(VideoReceiverTest, MultiplePackets) {
- EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).WillRepeatedly(
- testing::Return(true));
- rtp_header_.max_packet_id = 2;
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
- ++rtp_header_.packet_id;
- ++rtp_header_.webrtc.header.sequenceNumber;
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
- ++rtp_header_.packet_id;
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
-
- VideoFrameEncodedCallback frame_to_decode_callback =
- base::Bind(&TestVideoReceiverCallback::FrameToDecode,
- video_receiver_callback_);
-
- receiver_->GetEncodedVideoFrame(frame_to_decode_callback);
-
- task_runner_->RunTasks();
- EXPECT_EQ(video_receiver_callback_->number_times_called(), 1);
-}
-
-TEST_F(VideoReceiverTest, GetOnePacketRawframe) {
- EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).WillRepeatedly(
- testing::Return(true));
- receiver_->IncomingParsedRtpPacket(payload_.data(), payload_.size(),
- rtp_header_);
- // Decode error - requires legal input.
- VideoFrameDecodedCallback frame_decoded_callback =
- base::Bind(&TestVideoReceiverCallback::DecodeComplete,
- video_receiver_callback_);
- receiver_->GetRawVideoFrame(frame_decoded_callback);
- task_runner_->RunTasks();
- EXPECT_EQ(video_receiver_callback_->number_times_called(), 0);
-}
-
-// TODO(pwestin): add encoded frames.
-
-} // namespace cast
-} // namespace media
-
-
diff --git a/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.cc b/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.cc
index 099be63a2c8..c7374babd19 100644
--- a/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.cc
+++ b/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
#include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
namespace media {
@@ -18,31 +19,31 @@ namespace cast {
static const uint32 kMinIntra = 300;
+static int ComputeMaxNumOfRepeatedBuffes(int max_unacked_frames) {
+ if (max_unacked_frames > kNumberOfVp8VideoBuffers)
+ return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers;
+
+ return 0;
+}
+
Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
- uint8 max_unacked_frames)
+ int max_unacked_frames)
: cast_config_(video_config),
use_multiple_video_buffers_(
cast_config_.max_number_of_video_buffers_used ==
kNumberOfVp8VideoBuffers),
max_number_of_repeated_buffers_in_a_row_(
- (max_unacked_frames > kNumberOfVp8VideoBuffers) ?
- ((max_unacked_frames - 1) / kNumberOfVp8VideoBuffers) : 0),
- config_(new vpx_codec_enc_cfg_t()),
- encoder_(new vpx_codec_ctx_t()),
- // Creating a wrapper to the image - setting image data to NULL. Actual
- // pointer will be set during encode. Setting align to 1, as it is
- // meaningless (actual memory is not allocated).
- raw_image_(vpx_img_wrap(NULL, IMG_FMT_I420, video_config.width,
- video_config.height, 1, NULL)),
+ ComputeMaxNumOfRepeatedBuffes(max_unacked_frames)),
key_frame_requested_(true),
- timestamp_(0),
+ first_frame_received_(false),
last_encoded_frame_id_(kStartFrameId),
number_of_repeated_buffers_(0) {
// TODO(pwestin): we need to figure out how to synchronize the acking with the
// internal state of the encoder, ideally the encoder will tell if we can
// send another frame.
DCHECK(!use_multiple_video_buffers_ ||
- max_number_of_repeated_buffers_in_a_row_ == 0) << "Invalid config";
+ max_number_of_repeated_buffers_in_a_row_ == 0)
+ << "Invalid config";
// VP8 have 3 buffers available for prediction, with
// max_number_of_video_buffers_used set to 1 we maximize the coding efficiency
@@ -52,21 +53,37 @@ Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
// propagation.
DCHECK(cast_config_.max_number_of_video_buffers_used == 1 ||
cast_config_.max_number_of_video_buffers_used ==
- kNumberOfVp8VideoBuffers) << "Invalid argument";
+ kNumberOfVp8VideoBuffers)
+ << "Invalid argument";
- for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
- acked_frame_buffers_[i] = true;
- used_buffers_frame_id_[i] = kStartFrameId;
- }
- InitEncode(video_config.number_of_cores);
+ thread_checker_.DetachFromThread();
}
Vp8Encoder::~Vp8Encoder() {
- vpx_codec_destroy(encoder_);
+ vpx_codec_destroy(encoder_.get());
vpx_img_free(raw_image_);
}
-void Vp8Encoder::InitEncode(int number_of_cores) {
+void Vp8Encoder::Initialize() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ config_.reset(new vpx_codec_enc_cfg_t());
+ encoder_.reset(new vpx_codec_ctx_t());
+
+ // Creating a wrapper to the image - setting image data to NULL. Actual
+ // pointer will be set during encode. Setting align to 1, as it is
+ // meaningless (actual memory is not allocated).
+ raw_image_ = vpx_img_wrap(
+ NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL);
+
+ for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
+ acked_frame_buffers_[i] = true;
+ used_buffers_frame_id_[i] = kStartFrameId;
+ }
+ InitEncode(cast_config_.number_of_encode_threads);
+}
+
+void Vp8Encoder::InitEncode(int number_of_encode_threads) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Populate encoder configuration with default values.
if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) {
DCHECK(false) << "Invalid return value";
@@ -85,17 +102,11 @@ void Vp8Encoder::InitEncode(int number_of_cores) {
// codec requirements.
config_->g_error_resilient = 1;
}
-
- if (cast_config_.width * cast_config_.height > 640 * 480
- && number_of_cores >= 2) {
- config_->g_threads = 2; // 2 threads for qHD/HD.
- } else {
- config_->g_threads = 1; // 1 thread for VGA or less.
- }
+ config_->g_threads = number_of_encode_threads;
// Rate control settings.
- // TODO(pwestin): revisit these constants. Currently identical to webrtc.
- config_->rc_dropframe_thresh = 30;
+ // Never allow the encoder to drop frame internally.
+ config_->rc_dropframe_thresh = 0;
config_->rc_end_usage = VPX_CBR;
config_->g_pass = VPX_RC_ONE_PASS;
config_->rc_resize_allowed = 0;
@@ -110,19 +121,22 @@ void Vp8Encoder::InitEncode(int number_of_cores) {
// set the maximum target size of any key-frame.
uint32 rc_max_intra_target = MaxIntraTarget(config_->rc_buf_optimal_sz);
vpx_codec_flags_t flags = 0;
- // TODO(mikhal): Tune settings.
- if (vpx_codec_enc_init(encoder_, vpx_codec_vp8_cx(), config_.get(), flags)) {
- DCHECK(false) << "Invalid return value";
+ if (vpx_codec_enc_init(
+ encoder_.get(), vpx_codec_vp8_cx(), config_.get(), flags)) {
+ DCHECK(false) << "vpx_codec_enc_init() failed.";
+ encoder_.reset();
+ return;
}
- vpx_codec_control(encoder_, VP8E_SET_STATIC_THRESHOLD, 1);
- vpx_codec_control(encoder_, VP8E_SET_NOISE_SENSITIVITY, 0);
- vpx_codec_control(encoder_, VP8E_SET_CPUUSED, -6);
- vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT,
- rc_max_intra_target);
+ vpx_codec_control(encoder_.get(), VP8E_SET_STATIC_THRESHOLD, 1);
+ vpx_codec_control(encoder_.get(), VP8E_SET_NOISE_SENSITIVITY, 0);
+ vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, -6);
+ vpx_codec_control(
+ encoder_.get(), VP8E_SET_MAX_INTRA_BITRATE_PCT, rc_max_intra_target);
}
bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
- EncodedVideoFrame* encoded_image) {
+ transport::EncodedFrame* encoded_image) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Image in vpx_image_t format.
// Input image is const. VP8's raw image is not defined as const.
raw_image_->planes[PLANE_Y] =
@@ -142,8 +156,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
if (key_frame_requested_) {
flags = VPX_EFLAG_FORCE_KF;
// Self reference.
- latest_frame_id_to_reference =
- static_cast<uint8>(last_encoded_frame_id_ + 1);
+ latest_frame_id_to_reference = last_encoded_frame_id_ + 1;
// We can pick any buffer as buffer_to_update since we update
// them all.
buffer_to_update = kLastBuffer;
@@ -157,45 +170,63 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
// Note: The duration does not reflect the real time between frames. This is
// done to keep the encoder happy.
+ //
+ // TODO(miu): This is a semi-hack. We should consider using
+ // |video_frame->timestamp()| instead.
uint32 duration = kVideoFrequency / cast_config_.max_frame_rate;
- if (vpx_codec_encode(encoder_, raw_image_, timestamp_, duration, flags,
- VPX_DL_REALTIME)) {
+
+ // Note: Timestamp here is used for bitrate calculation. The absolute value
+ // is not important.
+ if (!first_frame_received_) {
+ first_frame_received_ = true;
+ first_frame_timestamp_ = video_frame->timestamp();
+ }
+
+ vpx_codec_pts_t timestamp =
+ (video_frame->timestamp() - first_frame_timestamp_).InMicroseconds() *
+ kVideoFrequency / base::Time::kMicrosecondsPerSecond;
+
+ if (vpx_codec_encode(encoder_.get(),
+ raw_image_,
+ timestamp,
+ duration,
+ flags,
+ VPX_DL_REALTIME) != VPX_CODEC_OK) {
+ LOG(ERROR) << "Failed to encode for once.";
return false;
}
- timestamp_ += duration;
// Get encoded frame.
- const vpx_codec_cx_pkt_t *pkt = NULL;
+ const vpx_codec_cx_pkt_t* pkt = NULL;
vpx_codec_iter_t iter = NULL;
- size_t total_size = 0;
- while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) {
- if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
- total_size += pkt->data.frame.sz;
- encoded_image->data.reserve(total_size);
- encoded_image->data.insert(
- encoded_image->data.end(),
- static_cast<const uint8*>(pkt->data.frame.buf),
- static_cast<const uint8*>(pkt->data.frame.buf) +
- pkt->data.frame.sz);
- if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
- encoded_image->key_frame = true;
- } else {
- encoded_image->key_frame = false;
- }
- }
+ bool is_key_frame = false;
+ while ((pkt = vpx_codec_get_cx_data(encoder_.get(), &iter)) != NULL) {
+ if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
+ continue;
+ encoded_image->data.assign(
+ static_cast<const uint8*>(pkt->data.frame.buf),
+ static_cast<const uint8*>(pkt->data.frame.buf) + pkt->data.frame.sz);
+ is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY);
+ break; // Done, since all data is provided in one CX_FRAME_PKT packet.
}
// Don't update frame_id for zero size frames.
- if (total_size == 0) return true;
+ if (encoded_image->data.empty())
+ return true;
// Populate the encoded frame.
- encoded_image->codec = kVp8;
- encoded_image->last_referenced_frame_id = latest_frame_id_to_reference;
encoded_image->frame_id = ++last_encoded_frame_id_;
+ if (is_key_frame) {
+ encoded_image->dependency = transport::EncodedFrame::KEY;
+ encoded_image->referenced_frame_id = encoded_image->frame_id;
+ } else {
+ encoded_image->dependency = transport::EncodedFrame::DEPENDENT;
+ encoded_image->referenced_frame_id = latest_frame_id_to_reference;
+ }
- VLOG(1) << "VP8 encoded frame:" << static_cast<int>(encoded_image->frame_id)
- << " sized:" << total_size;
+ DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id
+ << ", sized:" << encoded_image->data.size();
- if (encoded_image->key_frame) {
+ if (is_key_frame) {
key_frame_requested_ = false;
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
@@ -215,12 +246,14 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
}
void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
- if (!use_multiple_video_buffers_) return;
+ if (!use_multiple_video_buffers_)
+ return;
// We need to reference something.
DCHECK(acked_frame_buffers_[kAltRefBuffer] ||
acked_frame_buffers_[kGoldenBuffer] ||
- acked_frame_buffers_[kLastBuffer]) << "Invalid state";
+ acked_frame_buffers_[kLastBuffer])
+ << "Invalid state";
if (!acked_frame_buffers_[kAltRefBuffer]) {
*flags |= VP8_EFLAG_NO_REF_ARF;
@@ -234,7 +267,8 @@ void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
}
uint32 Vp8Encoder::GetLatestFrameIdToReference() {
- if (!use_multiple_video_buffers_) return last_encoded_frame_id_;
+ if (!use_multiple_video_buffers_)
+ return last_encoded_frame_id_;
int latest_frame_id_to_reference = -1;
if (acked_frame_buffers_[kAltRefBuffer]) {
@@ -265,9 +299,12 @@ uint32 Vp8Encoder::GetLatestFrameIdToReference() {
}
Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
+ if (!use_multiple_video_buffers_)
+ return kNoBuffer;
+
// Update at most one buffer, except for key-frames.
- Vp8Buffers buffer_to_update;
+ Vp8Buffers buffer_to_update = kNoBuffer;
if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) {
// TODO(pwestin): experiment with this. The issue with only this change is
// that we can end up with only 4 frames in flight when we expect 6.
@@ -299,7 +336,8 @@ Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
vpx_codec_flags_t* flags) {
- if (!use_multiple_video_buffers_) return;
+ if (!use_multiple_video_buffers_)
+ return;
// Update at most one buffer, except for key-frames.
switch (buffer_to_update) {
@@ -325,19 +363,23 @@ void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
}
void Vp8Encoder::UpdateRates(uint32 new_bitrate) {
+ DCHECK(thread_checker_.CalledOnValidThread());
uint32 new_bitrate_kbit = new_bitrate / 1000;
- if (config_->rc_target_bitrate == new_bitrate_kbit) return;
+ if (config_->rc_target_bitrate == new_bitrate_kbit)
+ return;
config_->rc_target_bitrate = new_bitrate_kbit;
// Update encoder context.
- if (vpx_codec_enc_config_set(encoder_, config_.get())) {
+ if (vpx_codec_enc_config_set(encoder_.get(), config_.get())) {
DCHECK(false) << "Invalid return value";
}
}
void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
- if (!use_multiple_video_buffers_) return;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!use_multiple_video_buffers_)
+ return;
VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
@@ -348,6 +390,7 @@ void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
}
void Vp8Encoder::GenerateKeyFrame() {
+ DCHECK(thread_checker_.CalledOnValidThread());
key_frame_requested_ = true;
}
@@ -362,7 +405,7 @@ uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const {
float scale_parameter = 0.5;
uint32 target_pct = optimal_buffer_size_ms * scale_parameter *
- cast_config_.max_frame_rate / 10;
+ cast_config_.max_frame_rate / 10;
// Don't go below 3 times the per frame bandwidth.
return std::max(target_pct, kMinIntra);
diff --git a/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.gypi b/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.gypi
deleted file mode 100644
index fa9c2944a15..00000000000
--- a/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.gypi
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- 'targets': [
- {
- 'target_name': 'cast_vp8_encoder',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- ],
- 'sources': [
- 'vp8_encoder.cc',
- 'vp8_encoder.h',
- ], # source
- 'dependencies': [
- '<(DEPTH)/ui/gfx/gfx.gyp:gfx',
- '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.h b/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.h
index d09cc27dabc..2421cf15114 100644
--- a/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.h
+++ b/chromium/media/cast/video_sender/codecs/vp8/vp8_encoder.h
@@ -7,7 +7,10 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
#include "media/cast/cast_config.h"
+#include "media/cast/video_sender/software_video_encoder.h"
#include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
namespace media {
@@ -22,24 +25,27 @@ namespace cast {
const int kNumberOfVp8VideoBuffers = 3;
-class Vp8Encoder {
+class Vp8Encoder : public SoftwareVideoEncoder {
public:
- Vp8Encoder(const VideoSenderConfig& video_config,
- uint8 max_unacked_frames);
+ Vp8Encoder(const VideoSenderConfig& video_config, int max_unacked_frames);
- ~Vp8Encoder();
+ virtual ~Vp8Encoder();
+
+ // Initialize the encoder before Encode() can be called. This method
+ // must be called on the thread that Encode() is called.
+ virtual void Initialize() OVERRIDE;
// Encode a raw image (as a part of a video stream).
- bool Encode(const scoped_refptr<media::VideoFrame>& video_frame,
- EncodedVideoFrame* encoded_image);
+ virtual bool Encode(const scoped_refptr<media::VideoFrame>& video_frame,
+ transport::EncodedFrame* encoded_image) OVERRIDE;
// Update the encoder with a new target bit rate.
- void UpdateRates(uint32 new_bitrate);
+ virtual void UpdateRates(uint32 new_bitrate) OVERRIDE;
// Set the next frame to be a key frame.
- void GenerateKeyFrame();
+ virtual void GenerateKeyFrame() OVERRIDE;
- void LatestFrameIdToReference(uint32 frame_id);
+ virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
private:
enum Vp8Buffers {
@@ -73,16 +79,22 @@ class Vp8Encoder {
// VP8 internal objects.
scoped_ptr<vpx_codec_enc_cfg_t> config_;
- vpx_enc_ctx_t* encoder_;
+ scoped_ptr<vpx_enc_ctx_t> encoder_;
vpx_image_t* raw_image_;
bool key_frame_requested_;
- int64 timestamp_;
+ bool first_frame_received_;
+ base::TimeDelta first_frame_timestamp_;
uint32 last_encoded_frame_id_;
uint32 used_buffers_frame_id_[kNumberOfVp8VideoBuffers];
bool acked_frame_buffers_[kNumberOfVp8VideoBuffers];
Vp8Buffers last_used_vp8_buffer_;
int number_of_repeated_buffers_;
+
+ // This is bound to the thread where Initialize() is called.
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(Vp8Encoder);
};
} // namespace cast
diff --git a/chromium/media/cast/video_sender/external_video_encoder.cc b/chromium/media/cast/video_sender/external_video_encoder.cc
new file mode 100644
index 00000000000..ca30bcd47af
--- /dev/null
+++ b/chromium/media/cast/video_sender/external_video_encoder.cc
@@ -0,0 +1,436 @@
+// 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_sender/external_video_encoder.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_util.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/video/video_encode_accelerator.h"
+
+namespace media {
+namespace cast {
+class LocalVideoEncodeAcceleratorClient;
+} // namespace cast
+} // namespace media
+
+namespace {
+static const size_t kOutputBufferCount = 3;
+
+void LogFrameEncodedEvent(
+ const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
+ base::TimeTicks event_time,
+ media::cast::RtpTimestamp rtp_timestamp,
+ uint32 frame_id) {
+ cast_environment->Logging()->InsertFrameEvent(
+ event_time, media::cast::FRAME_ENCODED, media::cast::VIDEO_EVENT,
+ rtp_timestamp, frame_id);
+}
+
+// Proxy this call to ExternalVideoEncoder on the cast main thread.
+void ProxyCreateVideoEncodeAccelerator(
+ const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
+ const base::WeakPtr<media::cast::ExternalVideoEncoder>& weak_ptr,
+ const media::cast::CreateVideoEncodeMemoryCallback&
+ create_video_encode_mem_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea) {
+ cast_environment->PostTask(
+ media::cast::CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(
+ &media::cast::ExternalVideoEncoder::OnCreateVideoEncodeAccelerator,
+ weak_ptr,
+ create_video_encode_mem_cb,
+ encoder_task_runner,
+ base::Passed(&vea)));
+}
+} // namespace
+
+namespace media {
+namespace cast {
+
+// Container for the associated data of a video frame being processed.
+struct EncodedFrameReturnData {
+ EncodedFrameReturnData(base::TimeTicks c_time,
+ VideoEncoder::FrameEncodedCallback callback) {
+ capture_time = c_time;
+ frame_encoded_callback = callback;
+ }
+ base::TimeTicks capture_time;
+ VideoEncoder::FrameEncodedCallback frame_encoded_callback;
+};
+
+// The ExternalVideoEncoder class can be deleted directly by cast, while
+// LocalVideoEncodeAcceleratorClient stays around long enough to properly shut
+// down the VideoEncodeAccelerator.
+class LocalVideoEncodeAcceleratorClient
+ : public VideoEncodeAccelerator::Client,
+ public base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient> {
+ public:
+ LocalVideoEncodeAcceleratorClient(
+ scoped_refptr<CastEnvironment> cast_environment,
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
+ const base::WeakPtr<ExternalVideoEncoder>& weak_owner)
+ : cast_environment_(cast_environment),
+ encoder_task_runner_(encoder_task_runner),
+ video_encode_accelerator_(vea.Pass()),
+ create_video_encode_memory_cb_(create_video_encode_mem_cb),
+ weak_owner_(weak_owner),
+ last_encoded_frame_id_(kStartFrameId),
+ key_frame_encountered_(false) {
+ DCHECK(encoder_task_runner_);
+ }
+
+ // Initialize the real HW encoder.
+ void Initialize(const VideoSenderConfig& video_config) {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+
+ VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
+ switch (video_config.codec) {
+ case transport::kVp8:
+ output_profile = media::VP8PROFILE_MAIN;
+ break;
+ case transport::kH264:
+ output_profile = media::H264PROFILE_MAIN;
+ break;
+ case transport::kFakeSoftwareVideo:
+ NOTREACHED() << "Fake software video encoder cannot be external";
+ break;
+ case transport::kUnknownVideoCodec:
+ NOTREACHED() << "Video codec not specified";
+ break;
+ }
+ codec_ = video_config.codec;
+ max_frame_rate_ = video_config.max_frame_rate;
+
+ if (!video_encode_accelerator_->Initialize(
+ media::VideoFrame::I420,
+ gfx::Size(video_config.width, video_config.height),
+ output_profile,
+ video_config.start_bitrate,
+ this)) {
+ NotifyError(VideoEncodeAccelerator::kInvalidArgumentError);
+ return;
+ }
+
+ // Wait until shared memory is allocated to indicate that encoder is
+ // initialized.
+ }
+
+ // Free the HW.
+ void Destroy() {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+
+ video_encode_accelerator_.reset();
+ }
+
+ void SetBitRate(uint32 bit_rate) {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+
+ video_encode_accelerator_->RequestEncodingParametersChange(bit_rate,
+ max_frame_rate_);
+ }
+
+ void EncodeVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time,
+ bool key_frame_requested,
+ const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+
+ encoded_frame_data_storage_.push_back(
+ EncodedFrameReturnData(capture_time, frame_encoded_callback));
+
+ // BitstreamBufferReady will be called once the encoder is done.
+ video_encode_accelerator_->Encode(video_frame, key_frame_requested);
+ }
+
+ protected:
+ virtual void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ VLOG(1) << "ExternalVideoEncoder NotifyError: " << error;
+
+ video_encode_accelerator_.reset();
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&ExternalVideoEncoder::EncoderError, weak_owner_));
+ }
+
+ // Called to allocate the input and output buffers.
+ virtual void RequireBitstreamBuffers(unsigned int input_count,
+ const gfx::Size& input_coded_size,
+ size_t output_buffer_size) OVERRIDE {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(video_encode_accelerator_);
+
+ for (size_t j = 0; j < kOutputBufferCount; ++j) {
+ create_video_encode_memory_cb_.Run(
+ output_buffer_size,
+ base::Bind(&LocalVideoEncodeAcceleratorClient::OnCreateSharedMemory,
+ this));
+ }
+ }
+
+ // Encoder has encoded a frame and it's available in one of out output
+ // buffers.
+ virtual void BitstreamBufferReady(int32 bitstream_buffer_id,
+ size_t payload_size,
+ bool key_frame) OVERRIDE {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ if (bitstream_buffer_id < 0 ||
+ bitstream_buffer_id >= static_cast<int32>(output_buffers_.size())) {
+ NOTREACHED();
+ VLOG(1) << "BitstreamBufferReady(): invalid bitstream_buffer_id="
+ << bitstream_buffer_id;
+ NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
+ return;
+ }
+ base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id];
+ if (payload_size > output_buffer->mapped_size()) {
+ NOTREACHED();
+ VLOG(1) << "BitstreamBufferReady(): invalid payload_size = "
+ << payload_size;
+ NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
+ return;
+ }
+ if (key_frame)
+ key_frame_encountered_ = true;
+ if (!key_frame_encountered_) {
+ // Do not send video until we have encountered the first key frame.
+ // Save the bitstream buffer in |stream_header_| to be sent later along
+ // with the first key frame.
+ stream_header_.append(static_cast<const char*>(output_buffer->memory()),
+ payload_size);
+ } else if (!encoded_frame_data_storage_.empty()) {
+ scoped_ptr<transport::EncodedFrame> encoded_frame(
+ new transport::EncodedFrame());
+ encoded_frame->dependency = key_frame ? transport::EncodedFrame::KEY :
+ transport::EncodedFrame::DEPENDENT;
+ encoded_frame->frame_id = ++last_encoded_frame_id_;
+ if (key_frame)
+ encoded_frame->referenced_frame_id = encoded_frame->frame_id;
+ else
+ encoded_frame->referenced_frame_id = encoded_frame->frame_id - 1;
+ encoded_frame->reference_time =
+ encoded_frame_data_storage_.front().capture_time;
+ encoded_frame->rtp_timestamp =
+ GetVideoRtpTimestamp(encoded_frame->reference_time);
+ if (!stream_header_.empty()) {
+ encoded_frame->data = stream_header_;
+ stream_header_.clear();
+ }
+ encoded_frame->data.append(
+ static_cast<const char*>(output_buffer->memory()), payload_size);
+
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&LogFrameEncodedEvent,
+ cast_environment_,
+ cast_environment_->Clock()->NowTicks(),
+ encoded_frame->rtp_timestamp,
+ encoded_frame->frame_id));
+
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(encoded_frame_data_storage_.front().frame_encoded_callback,
+ base::Passed(&encoded_frame)));
+
+ encoded_frame_data_storage_.pop_front();
+ } else {
+ VLOG(1) << "BitstreamBufferReady(): no encoded frame data available";
+ }
+
+ // We need to re-add the output buffer to the encoder after we are done
+ // with it.
+ video_encode_accelerator_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
+ bitstream_buffer_id,
+ output_buffers_[bitstream_buffer_id]->handle(),
+ output_buffers_[bitstream_buffer_id]->mapped_size()));
+ }
+
+ private:
+ // Note: This method can be called on any thread.
+ void OnCreateSharedMemory(scoped_ptr<base::SharedMemory> memory) {
+ encoder_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalVideoEncodeAcceleratorClient::ReceivedSharedMemory,
+ this,
+ base::Passed(&memory)));
+ }
+
+ void ReceivedSharedMemory(scoped_ptr<base::SharedMemory> memory) {
+ DCHECK(encoder_task_runner_);
+ DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+
+ output_buffers_.push_back(memory.release());
+
+ // Wait until all requested buffers are received.
+ if (output_buffers_.size() < kOutputBufferCount)
+ return;
+
+ // Immediately provide all output buffers to the VEA.
+ for (size_t i = 0; i < output_buffers_.size(); ++i) {
+ video_encode_accelerator_->UseOutputBitstreamBuffer(
+ media::BitstreamBuffer(static_cast<int32>(i),
+ output_buffers_[i]->handle(),
+ output_buffers_[i]->mapped_size()));
+ }
+
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&ExternalVideoEncoder::EncoderInitialized, weak_owner_));
+ }
+
+ friend class base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient>;
+
+ virtual ~LocalVideoEncodeAcceleratorClient() {}
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
+ scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_;
+ const CreateVideoEncodeMemoryCallback create_video_encode_memory_cb_;
+ const base::WeakPtr<ExternalVideoEncoder> weak_owner_;
+ int max_frame_rate_;
+ transport::VideoCodec codec_;
+ uint32 last_encoded_frame_id_;
+ bool key_frame_encountered_;
+ std::string stream_header_;
+
+ // Shared memory buffers for output with the VideoAccelerator.
+ ScopedVector<base::SharedMemory> output_buffers_;
+
+ // FIFO list.
+ std::list<EncodedFrameReturnData> encoded_frame_data_storage_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalVideoEncodeAcceleratorClient);
+};
+
+ExternalVideoEncoder::ExternalVideoEncoder(
+ scoped_refptr<CastEnvironment> cast_environment,
+ const VideoSenderConfig& video_config,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb)
+ : video_config_(video_config),
+ cast_environment_(cast_environment),
+ encoder_active_(false),
+ key_frame_requested_(false),
+ weak_factory_(this) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+
+ create_vea_cb.Run(base::Bind(&ProxyCreateVideoEncodeAccelerator,
+ cast_environment,
+ weak_factory_.GetWeakPtr(),
+ create_video_encode_mem_cb));
+}
+
+ExternalVideoEncoder::~ExternalVideoEncoder() {
+ encoder_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalVideoEncodeAcceleratorClient::Destroy,
+ video_accelerator_client_));
+}
+
+void ExternalVideoEncoder::EncoderInitialized() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ encoder_active_ = true;
+}
+
+void ExternalVideoEncoder::EncoderError() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ encoder_active_ = false;
+}
+
+void ExternalVideoEncoder::OnCreateVideoEncodeAccelerator(
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ encoder_task_runner_ = encoder_task_runner;
+
+ video_accelerator_client_ =
+ new LocalVideoEncodeAcceleratorClient(cast_environment_,
+ encoder_task_runner,
+ vea.Pass(),
+ create_video_encode_mem_cb,
+ weak_factory_.GetWeakPtr());
+ encoder_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize,
+ video_accelerator_client_,
+ video_config_));
+}
+
+bool ExternalVideoEncoder::EncodeVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time,
+ const FrameEncodedCallback& frame_encoded_callback) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+
+ if (!encoder_active_)
+ return false;
+
+ encoder_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalVideoEncodeAcceleratorClient::EncodeVideoFrame,
+ video_accelerator_client_,
+ video_frame,
+ capture_time,
+ key_frame_requested_,
+ frame_encoded_callback));
+
+ key_frame_requested_ = false;
+ return true;
+}
+
+// Inform the encoder about the new target bit rate.
+void ExternalVideoEncoder::SetBitRate(int new_bit_rate) {
+ if (!encoder_active_) {
+ // If we receive SetBitRate() before VEA creation callback is invoked,
+ // cache the new bit rate in the encoder config and use the new settings
+ // to initialize VEA.
+ video_config_.start_bitrate = new_bit_rate;
+ return;
+ }
+
+ encoder_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalVideoEncodeAcceleratorClient::SetBitRate,
+ video_accelerator_client_,
+ new_bit_rate));
+}
+
+// Inform the encoder to encode the next frame as a key frame.
+void ExternalVideoEncoder::GenerateKeyFrame() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ key_frame_requested_ = true;
+}
+
+// Inform the encoder to only reference frames older or equal to frame_id;
+void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
+ // Do nothing not supported.
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/video_sender/external_video_encoder.h b/chromium/media/cast/video_sender/external_video_encoder.h
new file mode 100644
index 00000000000..29fe0c5fcdb
--- /dev/null
+++ b/chromium/media/cast/video_sender/external_video_encoder.h
@@ -0,0 +1,86 @@
+// 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_SENDER_EXTERNAL_VIDEO_ENCODER_H_
+#define MEDIA_CAST_VIDEO_SENDER_EXTERNAL_VIDEO_ENCODER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/video_sender/video_encoder.h"
+#include "media/video/video_encode_accelerator.h"
+
+namespace media {
+class VideoFrame;
+}
+
+namespace media {
+namespace cast {
+
+class LocalVideoEncodeAcceleratorClient;
+
+// This object is called external from the main cast thread and internally from
+// the video encoder thread.
+class ExternalVideoEncoder : public VideoEncoder {
+ public:
+ ExternalVideoEncoder(
+ scoped_refptr<CastEnvironment> cast_environment,
+ const VideoSenderConfig& video_config,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb);
+
+ virtual ~ExternalVideoEncoder();
+
+ // Called from the main cast thread. This function post the encode task to the
+ // video encoder thread;
+ // The video_frame must be valid until the closure callback is called.
+ // The closure callback is called from the video encoder thread as soon as
+ // the encoder is done with the frame; it does not mean that the encoded frame
+ // has been sent out.
+ // Once the encoded frame is ready the frame_encoded_callback is called.
+ virtual bool EncodeVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time,
+ const FrameEncodedCallback& frame_encoded_callback) OVERRIDE;
+
+ // The following functions are called from the main cast thread.
+ virtual void SetBitRate(int new_bit_rate) OVERRIDE;
+ virtual void GenerateKeyFrame() OVERRIDE;
+ virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
+
+ // Called when a VEA is created.
+ void OnCreateVideoEncodeAccelerator(
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea);
+
+ protected:
+ void EncoderInitialized();
+ void EncoderError();
+
+ private:
+ friend class LocalVideoEncodeAcceleratorClient;
+
+ VideoSenderConfig video_config_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+
+ bool encoder_active_;
+ bool key_frame_requested_;
+
+ scoped_refptr<LocalVideoEncodeAcceleratorClient> video_accelerator_client_;
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
+
+ // Weak pointer factory for posting back LocalVideoEncodeAcceleratorClient
+ // notifications to ExternalVideoEncoder.
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<ExternalVideoEncoder> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalVideoEncoder);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_VIDEO_SENDER_EXTERNAL_VIDEO_ENCODER_H_
diff --git a/chromium/media/cast/video_sender/external_video_encoder_unittest.cc b/chromium/media/cast/video_sender/external_video_encoder_unittest.cc
new file mode 100644
index 00000000000..853258ce30a
--- /dev/null
+++ b/chromium/media/cast/video_sender/external_video_encoder_unittest.cc
@@ -0,0 +1,191 @@
+// 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 <vector>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/video_frame.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/test/fake_video_encode_accelerator.h"
+#include "media/cast/test/utility/video_utility.h"
+#include "media/cast/video_sender/external_video_encoder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+using testing::_;
+
+namespace {
+
+void CreateVideoEncodeAccelerator(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ scoped_ptr<VideoEncodeAccelerator> fake_vea,
+ const ReceiveVideoEncodeAcceleratorCallback& callback) {
+ callback.Run(task_runner, fake_vea.Pass());
+}
+
+void CreateSharedMemory(
+ size_t size, const ReceiveVideoEncodeMemoryCallback& callback) {
+ scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
+ if (!shm->CreateAndMapAnonymous(size)) {
+ NOTREACHED();
+ return;
+ }
+ callback.Run(shm.Pass());
+}
+
+class TestVideoEncoderCallback
+ : public base::RefCountedThreadSafe<TestVideoEncoderCallback> {
+ public:
+ TestVideoEncoderCallback() {}
+
+ void SetExpectedResult(uint32 expected_frame_id,
+ uint32 expected_last_referenced_frame_id,
+ const base::TimeTicks& expected_capture_time) {
+ expected_frame_id_ = expected_frame_id;
+ expected_last_referenced_frame_id_ = expected_last_referenced_frame_id;
+ expected_capture_time_ = expected_capture_time;
+ }
+
+ void DeliverEncodedVideoFrame(
+ scoped_ptr<transport::EncodedFrame> encoded_frame) {
+ if (expected_frame_id_ == expected_last_referenced_frame_id_) {
+ EXPECT_EQ(transport::EncodedFrame::KEY, encoded_frame->dependency);
+ } else {
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT,
+ encoded_frame->dependency);
+ }
+ EXPECT_EQ(expected_frame_id_, encoded_frame->frame_id);
+ EXPECT_EQ(expected_last_referenced_frame_id_,
+ encoded_frame->referenced_frame_id);
+ EXPECT_EQ(expected_capture_time_, encoded_frame->reference_time);
+ }
+
+ protected:
+ virtual ~TestVideoEncoderCallback() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<TestVideoEncoderCallback>;
+
+ bool expected_key_frame_;
+ uint32 expected_frame_id_;
+ uint32 expected_last_referenced_frame_id_;
+ base::TimeTicks expected_capture_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVideoEncoderCallback);
+};
+} // namespace
+
+class ExternalVideoEncoderTest : public ::testing::Test {
+ protected:
+ ExternalVideoEncoderTest()
+ : test_video_encoder_callback_(new TestVideoEncoderCallback()) {
+ video_config_.rtp_config.ssrc = 1;
+ video_config_.incoming_feedback_ssrc = 2;
+ video_config_.rtp_config.payload_type = 127;
+ video_config_.use_external_encoder = true;
+ video_config_.width = 320;
+ video_config_.height = 240;
+ video_config_.max_bitrate = 5000000;
+ video_config_.min_bitrate = 1000000;
+ video_config_.start_bitrate = 2000000;
+ video_config_.max_qp = 56;
+ video_config_.min_qp = 0;
+ video_config_.max_frame_rate = 30;
+ video_config_.max_number_of_video_buffers_used = 3;
+ video_config_.codec = transport::kVp8;
+ gfx::Size size(video_config_.width, video_config_.height);
+ video_frame_ = media::VideoFrame::CreateFrame(
+ VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
+ PopulateVideoFrame(video_frame_, 123);
+
+ testing_clock_ = new base::SimpleTestTickClock();
+ task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_);
+ cast_environment_ =
+ new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_);
+
+ fake_vea_ = new test::FakeVideoEncodeAccelerator(task_runner_);
+ scoped_ptr<VideoEncodeAccelerator> fake_vea(fake_vea_);
+ video_encoder_.reset(
+ new ExternalVideoEncoder(cast_environment_,
+ video_config_,
+ base::Bind(&CreateVideoEncodeAccelerator,
+ task_runner_,
+ base::Passed(&fake_vea)),
+ base::Bind(&CreateSharedMemory)));
+ }
+
+ virtual ~ExternalVideoEncoderTest() {}
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ test::FakeVideoEncodeAccelerator* fake_vea_; // Owned by video_encoder_.
+ scoped_refptr<TestVideoEncoderCallback> test_video_encoder_callback_;
+ VideoSenderConfig video_config_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_ptr<VideoEncoder> video_encoder_;
+ scoped_refptr<media::VideoFrame> video_frame_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalVideoEncoderTest);
+};
+
+TEST_F(ExternalVideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
+ task_runner_->RunTasks(); // Run the initializer on the correct thread.
+
+ VideoEncoder::FrameEncodedCallback frame_encoded_callback =
+ base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
+ test_video_encoder_callback_.get());
+
+ base::TimeTicks capture_time;
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(0, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ for (int i = 0; i < 6; ++i) {
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(i + 1, i, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+ }
+ // We need to run the task to cleanup the GPU instance.
+ video_encoder_.reset(NULL);
+ task_runner_->RunTasks();
+}
+
+TEST_F(ExternalVideoEncoderTest, StreamHeader) {
+ task_runner_->RunTasks(); // Run the initializer on the correct thread.
+
+ VideoEncoder::FrameEncodedCallback frame_encoded_callback =
+ base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
+ test_video_encoder_callback_.get());
+
+ // Force the FakeVideoEncodeAccelerator to return a dummy non-key frame first.
+ fake_vea_->SendDummyFrameForTesting(false);
+
+ // Verify the first returned bitstream buffer is still a key frame.
+ base::TimeTicks capture_time;
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(0, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ // We need to run the task to cleanup the GPU instance.
+ video_encoder_.reset(NULL);
+ task_runner_->RunTasks();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/video_sender/fake_software_video_encoder.cc b/chromium/media/cast/video_sender/fake_software_video_encoder.cc
new file mode 100644
index 00000000000..7c5c9526419
--- /dev/null
+++ b/chromium/media/cast/video_sender/fake_software_video_encoder.cc
@@ -0,0 +1,69 @@
+// 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_sender/fake_software_video_encoder.h"
+
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "media/cast/transport/cast_transport_config.h"
+
+#ifndef OFFICIAL_BUILD
+
+namespace media {
+namespace cast {
+
+FakeSoftwareVideoEncoder::FakeSoftwareVideoEncoder(
+ const VideoSenderConfig& video_config)
+ : video_config_(video_config),
+ next_frame_is_key_(true),
+ frame_id_(0),
+ frame_id_to_reference_(0),
+ frame_size_(0) {
+}
+
+FakeSoftwareVideoEncoder::~FakeSoftwareVideoEncoder() {}
+
+void FakeSoftwareVideoEncoder::Initialize() {}
+
+bool FakeSoftwareVideoEncoder::Encode(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ transport::EncodedFrame* encoded_image) {
+ encoded_image->frame_id = frame_id_++;
+ if (next_frame_is_key_) {
+ encoded_image->dependency = transport::EncodedFrame::KEY;
+ encoded_image->referenced_frame_id = encoded_image->frame_id;
+ next_frame_is_key_ = false;
+ } else {
+ encoded_image->dependency = transport::EncodedFrame::DEPENDENT;
+ encoded_image->referenced_frame_id = encoded_image->frame_id - 1;
+ }
+
+ base::DictionaryValue values;
+ values.SetBoolean("key",
+ encoded_image->dependency == transport::EncodedFrame::KEY);
+ values.SetInteger("ref", encoded_image->referenced_frame_id);
+ values.SetInteger("id", encoded_image->frame_id);
+ values.SetInteger("size", frame_size_);
+ base::JSONWriter::Write(&values, &encoded_image->data);
+ encoded_image->data.resize(
+ std::max<size_t>(encoded_image->data.size(), frame_size_));
+ return true;
+}
+
+void FakeSoftwareVideoEncoder::UpdateRates(uint32 new_bitrate) {
+ frame_size_ = new_bitrate / video_config_.max_frame_rate / 8;
+}
+
+void FakeSoftwareVideoEncoder::GenerateKeyFrame() {
+ next_frame_is_key_ = true;
+}
+
+void FakeSoftwareVideoEncoder::LatestFrameIdToReference(uint32 frame_id) {
+ frame_id_to_reference_ = frame_id;
+}
+
+} // namespace cast
+} // namespace media
+
+#endif
diff --git a/chromium/media/cast/video_sender/fake_software_video_encoder.h b/chromium/media/cast/video_sender/fake_software_video_encoder.h
new file mode 100644
index 00000000000..0eb88ddfe17
--- /dev/null
+++ b/chromium/media/cast/video_sender/fake_software_video_encoder.h
@@ -0,0 +1,38 @@
+// 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_SENDER_FAKE_SOFTWARE_VIDEO_ENCODER_H_
+#define MEDIA_CAST_VIDEO_SENDER_FAKE_SOFTWARE_VIDEO_ENCODER_H_
+
+#include "media/cast/cast_config.h"
+#include "media/cast/video_sender/software_video_encoder.h"
+
+namespace media {
+namespace cast {
+
+class FakeSoftwareVideoEncoder : public SoftwareVideoEncoder {
+ public:
+ FakeSoftwareVideoEncoder(const VideoSenderConfig& video_config);
+ virtual ~FakeSoftwareVideoEncoder();
+
+ // SoftwareVideoEncoder implementations.
+ virtual void Initialize() OVERRIDE;
+ virtual bool Encode(const scoped_refptr<media::VideoFrame>& video_frame,
+ transport::EncodedFrame* encoded_image) OVERRIDE;
+ virtual void UpdateRates(uint32 new_bitrate) OVERRIDE;
+ virtual void GenerateKeyFrame() OVERRIDE;
+ virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
+
+ private:
+ VideoSenderConfig video_config_;
+ bool next_frame_is_key_;
+ uint32 frame_id_;
+ uint32 frame_id_to_reference_;
+ int frame_size_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_VIDEO_SENDER_FAKE_SOFTWARE_VIDEO_ENCODER_H_
diff --git a/chromium/media/cast/video_sender/mock_video_encoder_controller.cc b/chromium/media/cast/video_sender/mock_video_encoder_controller.cc
deleted file mode 100644
index 4f649aa44fe..00000000000
--- a/chromium/media/cast/video_sender/mock_video_encoder_controller.cc
+++ /dev/null
@@ -1,17 +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_sender/mock_video_encoder_controller.h"
-
-namespace media {
-namespace cast {
-
-MockVideoEncoderController::MockVideoEncoderController() {
-}
-
-MockVideoEncoderController::~MockVideoEncoderController() {
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/video_sender/mock_video_encoder_controller.h b/chromium/media/cast/video_sender/mock_video_encoder_controller.h
deleted file mode 100644
index cfc58a9eb8f..00000000000
--- a/chromium/media/cast/video_sender/mock_video_encoder_controller.h
+++ /dev/null
@@ -1,34 +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_VIDEO_SENDER_MOCK_VIDEO_ENCODER_CONTROLLER_H_
-#define MEDIA_CAST_VIDEO_SENDER_MOCK_VIDEO_ENCODER_CONTROLLER_H_
-
-#include "media/cast/cast_config.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-class MockVideoEncoderController : public VideoEncoderController {
- public:
- MockVideoEncoderController();
- virtual ~MockVideoEncoderController();
-
- MOCK_METHOD1(SetBitRate, void(int new_bit_rate));
-
- MOCK_METHOD1(SkipNextFrame, void(bool skip_next_frame));
-
- MOCK_METHOD0(GenerateKeyFrame, void());
-
- MOCK_METHOD1(LatestFrameIdToReference, void(uint32 frame_id));
-
- MOCK_CONST_METHOD0(NumberOfSkippedFrames, int());
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_VIDEO_SENDER_MOCK_VIDEO_ENCODER_CONTROLLER_H_
-
diff --git a/chromium/media/cast/video_sender/software_video_encoder.h b/chromium/media/cast/video_sender/software_video_encoder.h
new file mode 100644
index 00000000000..f1bf6f63316
--- /dev/null
+++ b/chromium/media/cast/video_sender/software_video_encoder.h
@@ -0,0 +1,46 @@
+// 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_SENDER_SOFTWARE_VIDEO_ENCODER_H_
+#define MEDIA_CAST_VIDEO_SENDER_SOFTWARE_VIDEO_ENCODER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+
+namespace media {
+class VideoFrame;
+}
+
+namespace media {
+namespace cast {
+namespace transport {
+struct EncodedFrame;
+} // namespace transport
+
+class SoftwareVideoEncoder {
+ public:
+ virtual ~SoftwareVideoEncoder() {}
+
+ // Initialize the encoder before Encode() can be called. This method
+ // must be called on the thread that Encode() is called.
+ virtual void Initialize() = 0;
+
+ // Encode a raw image (as a part of a video stream).
+ virtual bool Encode(const scoped_refptr<media::VideoFrame>& video_frame,
+ transport::EncodedFrame* encoded_image) = 0;
+
+ // Update the encoder with a new target bit rate.
+ virtual void UpdateRates(uint32 new_bitrate) = 0;
+
+ // Set the next frame to be a key frame.
+ virtual void GenerateKeyFrame() = 0;
+
+ // Set the last frame to reference.
+ virtual void LatestFrameIdToReference(uint32 frame_id) = 0;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_VIDEO_SENDER_SOFTWARE_VIDEO_ENCODER_H_
diff --git a/chromium/media/cast/video_sender/video_encoder.cc b/chromium/media/cast/video_sender/video_encoder.cc
deleted file mode 100644
index faa78d3a3e7..00000000000
--- a/chromium/media/cast/video_sender/video_encoder.cc
+++ /dev/null
@@ -1,123 +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_sender/video_encoder.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "media/base/video_frame.h"
-#include "media/cast/cast_defines.h"
-
-namespace media {
-namespace cast {
-
-void LogFrameEncodedEvent(CastEnvironment* const cast_environment,
- const base::TimeTicks& capture_time) {
- cast_environment->Logging()->InsertFrameEvent(kVideoFrameEncoded,
- GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
-}
-
-VideoEncoder::VideoEncoder(scoped_refptr<CastEnvironment> cast_environment,
- const VideoSenderConfig& video_config,
- uint8 max_unacked_frames)
- : video_config_(video_config),
- cast_environment_(cast_environment),
- skip_next_frame_(false),
- skip_count_(0) {
- if (video_config.codec == kVp8) {
- vp8_encoder_.reset(new Vp8Encoder(video_config, max_unacked_frames));
- } else {
- DCHECK(false) << "Invalid config"; // Codec not supported.
- }
-
- dynamic_config_.key_frame_requested = false;
- dynamic_config_.latest_frame_id_to_reference = kStartFrameId;
- dynamic_config_.bit_rate = video_config.start_bitrate;
-}
-
-VideoEncoder::~VideoEncoder() {}
-
-bool VideoEncoder::EncodeVideoFrame(
- const scoped_refptr<media::VideoFrame>& video_frame,
- const base::TimeTicks& capture_time,
- const FrameEncodedCallback& frame_encoded_callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (video_config_.codec != kVp8) return false;
-
- if (skip_next_frame_) {
- ++skip_count_;
- VLOG(1) << "Skip encoding frame";
- return false;
- }
-
- cast_environment_->Logging()->InsertFrameEvent(kVideoFrameSentToEncoder,
- GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
- cast_environment_->PostTask(CastEnvironment::VIDEO_ENCODER, FROM_HERE,
- base::Bind(&VideoEncoder::EncodeVideoFrameEncoderThread,
- base::Unretained(this), video_frame, capture_time,
- dynamic_config_, frame_encoded_callback));
-
- dynamic_config_.key_frame_requested = false;
- return true;
-}
-
-void VideoEncoder::EncodeVideoFrameEncoderThread(
- const scoped_refptr<media::VideoFrame>& video_frame,
- const base::TimeTicks& capture_time,
- const CodecDynamicConfig& dynamic_config,
- const FrameEncodedCallback& frame_encoded_callback) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::VIDEO_ENCODER));
- if (dynamic_config.key_frame_requested) {
- vp8_encoder_->GenerateKeyFrame();
- }
- vp8_encoder_->LatestFrameIdToReference(
- dynamic_config.latest_frame_id_to_reference);
- vp8_encoder_->UpdateRates(dynamic_config.bit_rate);
-
- scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame());
- bool retval = vp8_encoder_->Encode(video_frame, encoded_frame.get());
-
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(LogFrameEncodedEvent, cast_environment_, capture_time));
-
- if (!retval) {
- VLOG(1) << "Encoding failed";
- return;
- }
- if (encoded_frame->data.size() <= 0) {
- VLOG(1) << "Encoding resulted in an empty frame";
- return;
- }
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(frame_encoded_callback,
- base::Passed(&encoded_frame), capture_time));
-}
-
-// Inform the encoder about the new target bit rate.
-void VideoEncoder::SetBitRate(int new_bit_rate) {
- dynamic_config_.bit_rate = new_bit_rate;
-}
-
-// Inform the encoder to not encode the next frame.
-void VideoEncoder::SkipNextFrame(bool skip_next_frame) {
- skip_next_frame_ = skip_next_frame;
-}
-
-// Inform the encoder to encode the next frame as a key frame.
-void VideoEncoder::GenerateKeyFrame() {
- dynamic_config_.key_frame_requested = true;
-}
-
-// Inform the encoder to only reference frames older or equal to frame_id;
-void VideoEncoder::LatestFrameIdToReference(uint32 frame_id) {
- dynamic_config_.latest_frame_id_to_reference = frame_id;
-}
-
-int VideoEncoder::NumberOfSkippedFrames() const {
- return skip_count_;
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/video_sender/video_encoder.h b/chromium/media/cast/video_sender/video_encoder.h
index 559dff16734..c7b1049ce67 100644
--- a/chromium/media/cast/video_sender/video_encoder.h
+++ b/chromium/media/cast/video_sender/video_encoder.h
@@ -5,76 +5,44 @@
#ifndef MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_
#define MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_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/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "media/base/video_frame.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/video_sender/codecs/vp8/vp8_encoder.h"
-
-namespace media {
-class VideoFrame;
-}
namespace media {
namespace cast {
-// This object is called external from the main cast thread and internally from
-// the video encoder thread.
-class VideoEncoder : public VideoEncoderController {
+// All these functions are called from the main cast thread.
+class VideoEncoder {
public:
- typedef base::Callback<void(scoped_ptr<EncodedVideoFrame>,
- const base::TimeTicks&)> FrameEncodedCallback;
-
- VideoEncoder(scoped_refptr<CastEnvironment> cast_environment,
- const VideoSenderConfig& video_config,
- uint8 max_unacked_frames);
+ typedef base::Callback<void(scoped_ptr<transport::EncodedFrame>)>
+ FrameEncodedCallback;
- virtual ~VideoEncoder();
+ virtual ~VideoEncoder() {}
- // Called from the main cast thread. This function post the encode task to the
- // video encoder thread;
// The video_frame must be valid until the closure callback is called.
// The closure callback is called from the video encoder thread as soon as
// the encoder is done with the frame; it does not mean that the encoded frame
// has been sent out.
// Once the encoded frame is ready the frame_encoded_callback is called.
- bool EncodeVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame,
- const base::TimeTicks& capture_time,
- const FrameEncodedCallback& frame_encoded_callback);
-
- protected:
- struct CodecDynamicConfig {
- bool key_frame_requested;
- uint32 latest_frame_id_to_reference;
- int bit_rate;
- };
-
- // The actual encode, called from the video encoder thread.
- void EncodeVideoFrameEncoderThread(
+ virtual bool EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time,
- const CodecDynamicConfig& dynamic_config,
- const FrameEncodedCallback& frame_encoded_callback);
-
- // The following functions are called from the main cast thread.
- virtual void SetBitRate(int new_bit_rate) OVERRIDE;
- virtual void SkipNextFrame(bool skip_next_frame) OVERRIDE;
- virtual void GenerateKeyFrame() OVERRIDE;
- virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
- virtual int NumberOfSkippedFrames() const OVERRIDE;
+ const FrameEncodedCallback& frame_encoded_callback) = 0;
- private:
- friend class base::RefCountedThreadSafe<VideoEncoder>;
+ // Inform the encoder about the new target bit rate.
+ virtual void SetBitRate(int new_bit_rate) = 0;
- const VideoSenderConfig video_config_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_ptr<Vp8Encoder> vp8_encoder_;
- CodecDynamicConfig dynamic_config_;
- bool skip_next_frame_;
- int skip_count_;
+ // Inform the encoder to encode the next frame as a key frame.
+ virtual void GenerateKeyFrame() = 0;
- DISALLOW_COPY_AND_ASSIGN(VideoEncoder);
+ // Inform the encoder to only reference frames older or equal to frame_id;
+ virtual void LatestFrameIdToReference(uint32 frame_id) = 0;
};
} // namespace cast
diff --git a/chromium/media/cast/video_sender/video_encoder_impl.cc b/chromium/media/cast/video_sender/video_encoder_impl.cc
new file mode 100644
index 00000000000..b90ef0f07e3
--- /dev/null
+++ b/chromium/media/cast/video_sender/video_encoder_impl.cc
@@ -0,0 +1,139 @@
+// 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_sender/video_encoder_impl.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "media/base/video_frame.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/video_sender/codecs/vp8/vp8_encoder.h"
+#include "media/cast/video_sender/fake_software_video_encoder.h"
+
+namespace media {
+namespace cast {
+
+namespace {
+
+typedef base::Callback<void(Vp8Encoder*)> PassEncoderCallback;
+
+void InitializeEncoderOnEncoderThread(
+ const scoped_refptr<CastEnvironment>& environment,
+ SoftwareVideoEncoder* encoder) {
+ DCHECK(environment->CurrentlyOn(CastEnvironment::VIDEO));
+ encoder->Initialize();
+}
+
+void EncodeVideoFrameOnEncoderThread(
+ scoped_refptr<CastEnvironment> environment,
+ SoftwareVideoEncoder* encoder,
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time,
+ const VideoEncoderImpl::CodecDynamicConfig& dynamic_config,
+ const VideoEncoderImpl::FrameEncodedCallback& frame_encoded_callback) {
+ DCHECK(environment->CurrentlyOn(CastEnvironment::VIDEO));
+ if (dynamic_config.key_frame_requested) {
+ encoder->GenerateKeyFrame();
+ }
+ encoder->LatestFrameIdToReference(
+ dynamic_config.latest_frame_id_to_reference);
+ encoder->UpdateRates(dynamic_config.bit_rate);
+
+ scoped_ptr<transport::EncodedFrame> encoded_frame(
+ new transport::EncodedFrame());
+ if (!encoder->Encode(video_frame, encoded_frame.get())) {
+ VLOG(1) << "Encoding failed";
+ return;
+ }
+ if (encoded_frame->data.empty()) {
+ VLOG(1) << "Encoding resulted in an empty frame";
+ return;
+ }
+ encoded_frame->rtp_timestamp = transport::GetVideoRtpTimestamp(capture_time);
+ encoded_frame->reference_time = capture_time;
+
+ environment->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(
+ frame_encoded_callback, base::Passed(&encoded_frame)));
+}
+} // namespace
+
+VideoEncoderImpl::VideoEncoderImpl(
+ scoped_refptr<CastEnvironment> cast_environment,
+ const VideoSenderConfig& video_config,
+ int max_unacked_frames)
+ : video_config_(video_config),
+ cast_environment_(cast_environment) {
+ if (video_config.codec == transport::kVp8) {
+ encoder_.reset(new Vp8Encoder(video_config, max_unacked_frames));
+ cast_environment_->PostTask(CastEnvironment::VIDEO,
+ FROM_HERE,
+ base::Bind(&InitializeEncoderOnEncoderThread,
+ cast_environment,
+ encoder_.get()));
+#ifndef OFFICIAL_BUILD
+ } else if (video_config.codec == transport::kFakeSoftwareVideo) {
+ encoder_.reset(new FakeSoftwareVideoEncoder(video_config));
+#endif
+ } else {
+ DCHECK(false) << "Invalid config"; // Codec not supported.
+ }
+
+ dynamic_config_.key_frame_requested = false;
+ dynamic_config_.latest_frame_id_to_reference = kStartFrameId;
+ dynamic_config_.bit_rate = video_config.start_bitrate;
+}
+
+VideoEncoderImpl::~VideoEncoderImpl() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ if (encoder_) {
+ cast_environment_->PostTask(
+ CastEnvironment::VIDEO,
+ FROM_HERE,
+ base::Bind(&base::DeletePointer<SoftwareVideoEncoder>,
+ encoder_.release()));
+ }
+}
+
+bool VideoEncoderImpl::EncodeVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time,
+ const FrameEncodedCallback& frame_encoded_callback) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ cast_environment_->PostTask(CastEnvironment::VIDEO,
+ FROM_HERE,
+ base::Bind(&EncodeVideoFrameOnEncoderThread,
+ cast_environment_,
+ encoder_.get(),
+ video_frame,
+ capture_time,
+ dynamic_config_,
+ frame_encoded_callback));
+
+ dynamic_config_.key_frame_requested = false;
+ return true;
+}
+
+// Inform the encoder about the new target bit rate.
+void VideoEncoderImpl::SetBitRate(int new_bit_rate) {
+ dynamic_config_.bit_rate = new_bit_rate;
+}
+
+// Inform the encoder to encode the next frame as a key frame.
+void VideoEncoderImpl::GenerateKeyFrame() {
+ dynamic_config_.key_frame_requested = true;
+}
+
+// Inform the encoder to only reference frames older or equal to frame_id;
+void VideoEncoderImpl::LatestFrameIdToReference(uint32 frame_id) {
+ dynamic_config_.latest_frame_id_to_reference = frame_id;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/video_sender/video_encoder_impl.h b/chromium/media/cast/video_sender/video_encoder_impl.h
new file mode 100644
index 00000000000..b34b440c935
--- /dev/null
+++ b/chromium/media/cast/video_sender/video_encoder_impl.h
@@ -0,0 +1,72 @@
+// 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_SENDER_VIDEO_ENCODER_IMPL_H_
+#define MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/video_sender/software_video_encoder.h"
+#include "media/cast/video_sender/video_encoder.h"
+
+namespace media {
+class VideoFrame;
+
+namespace cast {
+
+// This object is called external from the main cast thread and internally from
+// the video encoder thread.
+class VideoEncoderImpl : public VideoEncoder {
+ public:
+ struct CodecDynamicConfig {
+ bool key_frame_requested;
+ uint32 latest_frame_id_to_reference;
+ int bit_rate;
+ };
+
+ typedef base::Callback<void(scoped_ptr<transport::EncodedFrame>)>
+ FrameEncodedCallback;
+
+ VideoEncoderImpl(scoped_refptr<CastEnvironment> cast_environment,
+ const VideoSenderConfig& video_config,
+ int max_unacked_frames);
+
+ virtual ~VideoEncoderImpl();
+
+ // Called from the main cast thread. This function post the encode task to the
+ // video encoder thread;
+ // The video_frame must be valid until the closure callback is called.
+ // The closure callback is called from the video encoder thread as soon as
+ // the encoder is done with the frame; it does not mean that the encoded frame
+ // has been sent out.
+ // Once the encoded frame is ready the frame_encoded_callback is called.
+ virtual bool EncodeVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time,
+ const FrameEncodedCallback& frame_encoded_callback) OVERRIDE;
+
+ // The following functions are called from the main cast thread.
+ virtual void SetBitRate(int new_bit_rate) OVERRIDE;
+ virtual void GenerateKeyFrame() OVERRIDE;
+ virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
+
+ private:
+ const VideoSenderConfig video_config_;
+ scoped_refptr<CastEnvironment> cast_environment_;
+ CodecDynamicConfig dynamic_config_;
+
+ // This member belongs to the video encoder thread. It must not be
+ // dereferenced on the main thread. We manage the lifetime of this member
+ // manually because it needs to be initialize, used and destroyed on the
+ // video encoder thread and video encoder thread can out-live the main thread.
+ scoped_ptr<SoftwareVideoEncoder> encoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoEncoderImpl);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_IMPL_H_
diff --git a/chromium/media/cast/video_sender/video_encoder_impl_unittest.cc b/chromium/media/cast/video_sender/video_encoder_impl_unittest.cc
new file mode 100644
index 00000000000..a60812304f2
--- /dev/null
+++ b/chromium/media/cast/video_sender/video_encoder_impl_unittest.cc
@@ -0,0 +1,260 @@
+// 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 <vector>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/video_frame.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/test/utility/video_utility.h"
+#include "media/cast/video_sender/video_encoder_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+using testing::_;
+
+namespace {
+class TestVideoEncoderCallback
+ : public base::RefCountedThreadSafe<TestVideoEncoderCallback> {
+ public:
+ TestVideoEncoderCallback() {}
+
+ void SetExpectedResult(uint32 expected_frame_id,
+ uint32 expected_last_referenced_frame_id,
+ const base::TimeTicks& expected_capture_time) {
+ expected_frame_id_ = expected_frame_id;
+ expected_last_referenced_frame_id_ = expected_last_referenced_frame_id;
+ expected_capture_time_ = expected_capture_time;
+ }
+
+ void DeliverEncodedVideoFrame(
+ scoped_ptr<transport::EncodedFrame> encoded_frame) {
+ if (expected_frame_id_ == expected_last_referenced_frame_id_) {
+ EXPECT_EQ(transport::EncodedFrame::KEY, encoded_frame->dependency);
+ } else {
+ EXPECT_EQ(transport::EncodedFrame::DEPENDENT,
+ encoded_frame->dependency);
+ }
+ EXPECT_EQ(expected_frame_id_, encoded_frame->frame_id);
+ EXPECT_EQ(expected_last_referenced_frame_id_,
+ encoded_frame->referenced_frame_id);
+ EXPECT_EQ(expected_capture_time_, encoded_frame->reference_time);
+ }
+
+ protected:
+ virtual ~TestVideoEncoderCallback() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<TestVideoEncoderCallback>;
+
+ uint32 expected_frame_id_;
+ uint32 expected_last_referenced_frame_id_;
+ base::TimeTicks expected_capture_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVideoEncoderCallback);
+};
+} // namespace
+
+class VideoEncoderImplTest : public ::testing::Test {
+ protected:
+ VideoEncoderImplTest()
+ : test_video_encoder_callback_(new TestVideoEncoderCallback()) {
+ video_config_.rtp_config.ssrc = 1;
+ video_config_.incoming_feedback_ssrc = 2;
+ video_config_.rtp_config.payload_type = 127;
+ video_config_.use_external_encoder = false;
+ video_config_.width = 320;
+ video_config_.height = 240;
+ video_config_.max_bitrate = 5000000;
+ video_config_.min_bitrate = 1000000;
+ video_config_.start_bitrate = 2000000;
+ video_config_.max_qp = 56;
+ video_config_.min_qp = 0;
+ video_config_.max_frame_rate = 30;
+ video_config_.max_number_of_video_buffers_used = 3;
+ video_config_.codec = transport::kVp8;
+ gfx::Size size(video_config_.width, video_config_.height);
+ video_frame_ = media::VideoFrame::CreateFrame(
+ VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
+ PopulateVideoFrame(video_frame_, 123);
+ }
+
+ virtual ~VideoEncoderImplTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ testing_clock_ = new base::SimpleTestTickClock();
+ 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 void TearDown() OVERRIDE {
+ video_encoder_.reset();
+ task_runner_->RunTasks();
+ }
+
+ void Configure(int max_unacked_frames) {
+ video_encoder_.reset(new VideoEncoderImpl(
+ cast_environment_, video_config_, max_unacked_frames));
+ }
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ scoped_refptr<TestVideoEncoderCallback> test_video_encoder_callback_;
+ VideoSenderConfig video_config_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ scoped_ptr<VideoEncoder> video_encoder_;
+ scoped_refptr<media::VideoFrame> video_frame_;
+
+ scoped_refptr<CastEnvironment> cast_environment_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoEncoderImplTest);
+};
+
+TEST_F(VideoEncoderImplTest, EncodePattern30fpsRunningOutOfAck) {
+ Configure(3);
+
+ VideoEncoder::FrameEncodedCallback frame_encoded_callback =
+ base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
+ test_video_encoder_callback_.get());
+
+ base::TimeTicks capture_time;
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(0, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ video_encoder_->LatestFrameIdToReference(0);
+ test_video_encoder_callback_->SetExpectedResult(1, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ video_encoder_->LatestFrameIdToReference(1);
+ test_video_encoder_callback_->SetExpectedResult(2, 1, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(2);
+
+ for (int i = 3; i < 6; ++i) {
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(i, 2, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+ }
+}
+
+// TODO(pwestin): Re-enabled after redesign the encoder to control number of
+// frames in flight.
+TEST_F(VideoEncoderImplTest, DISABLED_EncodePattern60fpsRunningOutOfAck) {
+ video_config_.max_number_of_video_buffers_used = 1;
+ Configure(6);
+
+ base::TimeTicks capture_time;
+ VideoEncoder::FrameEncodedCallback frame_encoded_callback =
+ base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
+ test_video_encoder_callback_.get());
+
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(0, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(0);
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(1, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(1);
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(2, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(2);
+
+ for (int i = 3; i < 9; ++i) {
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(i, 2, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+ }
+}
+
+// TODO(pwestin): Re-enabled after redesign the encoder to control number of
+// frames in flight.
+TEST_F(VideoEncoderImplTest,
+ DISABLED_EncodePattern60fps200msDelayRunningOutOfAck) {
+ Configure(12);
+
+ base::TimeTicks capture_time;
+ VideoEncoder::FrameEncodedCallback frame_encoded_callback =
+ base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
+ test_video_encoder_callback_.get());
+
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(0, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(0);
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(1, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(1);
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(2, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(2);
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(3, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(3);
+ capture_time += base::TimeDelta::FromMilliseconds(33);
+ test_video_encoder_callback_->SetExpectedResult(4, 0, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+
+ video_encoder_->LatestFrameIdToReference(4);
+
+ for (int i = 5; i < 17; ++i) {
+ test_video_encoder_callback_->SetExpectedResult(i, 4, capture_time);
+ EXPECT_TRUE(video_encoder_->EncodeVideoFrame(
+ video_frame_, capture_time, frame_encoded_callback));
+ task_runner_->RunTasks();
+ }
+}
+
+} // namespace cast
+} // namespace media
diff --git a/chromium/media/cast/video_sender/video_encoder_unittest.cc b/chromium/media/cast/video_sender/video_encoder_unittest.cc
deleted file mode 100644
index b68b8364c43..00000000000
--- a/chromium/media/cast/video_sender/video_encoder_unittest.cc
+++ /dev/null
@@ -1,247 +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 <vector>
-
-#include "base/bind.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/base/video_frame.h"
-#include "media/cast/cast_defines.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "media/cast/test/video_utility.h"
-#include "media/cast/video_sender/video_encoder.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-using testing::_;
-
-namespace {
-class TestVideoEncoderCallback :
- public base::RefCountedThreadSafe<TestVideoEncoderCallback> {
- public:
- TestVideoEncoderCallback() {}
-
- void SetExpectedResult(bool expected_key_frame,
- uint8 expected_frame_id,
- uint8 expected_last_referenced_frame_id,
- const base::TimeTicks& expected_capture_time) {
- expected_key_frame_ = expected_key_frame;
- expected_frame_id_ = expected_frame_id;
- expected_last_referenced_frame_id_ = expected_last_referenced_frame_id;
- expected_capture_time_ = expected_capture_time;
- }
-
- void DeliverEncodedVideoFrame(scoped_ptr<EncodedVideoFrame> encoded_frame,
- const base::TimeTicks& capture_time) {
- EXPECT_EQ(expected_key_frame_, encoded_frame->key_frame);
- EXPECT_EQ(expected_frame_id_, encoded_frame->frame_id);
- EXPECT_EQ(expected_last_referenced_frame_id_,
- encoded_frame->last_referenced_frame_id);
- EXPECT_EQ(expected_capture_time_, capture_time);
- }
-
- protected:
- virtual ~TestVideoEncoderCallback() {}
-
- private:
- friend class base::RefCountedThreadSafe<TestVideoEncoderCallback>;
-
- bool expected_key_frame_;
- uint8 expected_frame_id_;
- uint8 expected_last_referenced_frame_id_;
- base::TimeTicks expected_capture_time_;
-};
-} // namespace
-
-class VideoEncoderTest : public ::testing::Test {
- protected:
- VideoEncoderTest()
- : test_video_encoder_callback_(new TestVideoEncoderCallback()) {
- video_config_.sender_ssrc = 1;
- video_config_.incoming_feedback_ssrc = 2;
- video_config_.rtp_payload_type = 127;
- video_config_.use_external_encoder = false;
- video_config_.width = 320;
- video_config_.height = 240;
- video_config_.max_bitrate = 5000000;
- video_config_.min_bitrate = 1000000;
- video_config_.start_bitrate = 2000000;
- video_config_.max_qp = 56;
- video_config_.min_qp = 0;
- video_config_.max_frame_rate = 30;
- video_config_.max_number_of_video_buffers_used = 3;
- video_config_.codec = kVp8;
- gfx::Size size(video_config_.width, video_config_.height);
- video_frame_ = media::VideoFrame::CreateFrame(VideoFrame::I420,
- size, gfx::Rect(size), size, base::TimeDelta());
- PopulateVideoFrame(video_frame_, 123);
- }
-
- virtual ~VideoEncoderTest() {}
-
- virtual void SetUp() {
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
- }
-
- void Configure(uint8 max_unacked_frames) {
- video_encoder_.reset(new VideoEncoder(cast_environment_, video_config_,
- max_unacked_frames));
- video_encoder_controller_ = video_encoder_.get();
- }
-
- base::SimpleTestTickClock testing_clock_;
- scoped_refptr<TestVideoEncoderCallback> test_video_encoder_callback_;
- VideoSenderConfig video_config_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
- scoped_ptr<VideoEncoder> video_encoder_;
- VideoEncoderController* video_encoder_controller_;
- scoped_refptr<media::VideoFrame> video_frame_;
-
- scoped_refptr<CastEnvironment> cast_environment_;
-};
-
-TEST_F(VideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
- Configure(3);
-
- VideoEncoder::FrameEncodedCallback frame_encoded_callback =
- base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
- test_video_encoder_callback_.get());
-
- base::TimeTicks capture_time;
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(true, 0, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- capture_time += base::TimeDelta::FromMilliseconds(33);
- video_encoder_controller_->LatestFrameIdToReference(0);
- test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- capture_time += base::TimeDelta::FromMilliseconds(33);
- video_encoder_controller_->LatestFrameIdToReference(1);
- test_video_encoder_callback_->SetExpectedResult(false, 2, 1, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(2);
-
- for (int i = 3; i < 6; ++i) {
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, i, 2, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
- }
-}
-
-// TODO(pwestin): Re-enabled after redesign the encoder to control number of
-// frames in flight.
-TEST_F(VideoEncoderTest,DISABLED_EncodePattern60fpsRunningOutOfAck) {
- video_config_.max_number_of_video_buffers_used = 1;
- Configure(6);
-
- base::TimeTicks capture_time;
- VideoEncoder::FrameEncodedCallback frame_encoded_callback =
- base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
- test_video_encoder_callback_.get());
-
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(true, 0, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(0);
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(1);
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, 2, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(2);
-
- for (int i = 3; i < 9; ++i) {
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, i, 2, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
- }
-}
-
-// TODO(pwestin): Re-enabled after redesign the encoder to control number of
-// frames in flight.
-TEST_F(VideoEncoderTest, DISABLED_EncodePattern60fps200msDelayRunningOutOfAck) {
- Configure(12);
-
- base::TimeTicks capture_time;
- VideoEncoder::FrameEncodedCallback frame_encoded_callback =
- base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
- test_video_encoder_callback_.get());
-
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(true, 0, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(0);
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(1);
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, 2, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(2);
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, 3, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(3);
- capture_time += base::TimeDelta::FromMilliseconds(33);
- test_video_encoder_callback_->SetExpectedResult(false, 4, 0, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
-
- video_encoder_controller_->LatestFrameIdToReference(4);
-
- for (int i = 5; i < 17; ++i) {
- test_video_encoder_callback_->SetExpectedResult(false, i, 4, capture_time);
- EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
- frame_encoded_callback));
- task_runner_->RunTasks();
- }
-}
-
-} // namespace cast
-} // namespace media
diff --git a/chromium/media/cast/video_sender/video_sender.cc b/chromium/media/cast/video_sender/video_sender.cc
index 7391fe8e645..cf050b7f10c 100644
--- a/chromium/media/cast/video_sender/video_sender.cc
+++ b/chromium/media/cast/video_sender/video_sender.cc
@@ -4,451 +4,395 @@
#include "media/cast/video_sender/video_sender.h"
-#include <list>
+#include <algorithm>
+#include <cstring>
#include "base/bind.h"
+#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
-#include "crypto/encryptor.h"
-#include "crypto/symmetric_key.h"
#include "media/cast/cast_defines.h"
-#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/video_sender/video_encoder.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/video_sender/external_video_encoder.h"
+#include "media/cast/video_sender/video_encoder_impl.h"
namespace media {
namespace cast {
-const int64 kMinSchedulingDelayMs = 1;
-
-class LocalRtcpVideoSenderFeedback : public RtcpSenderFeedback {
- public:
- explicit LocalRtcpVideoSenderFeedback(VideoSender* video_sender)
- : video_sender_(video_sender) {
- }
-
- virtual void OnReceivedCastFeedback(
- const RtcpCastMessage& cast_feedback) OVERRIDE {
- video_sender_->OnReceivedCastFeedback(cast_feedback);
- }
-
- private:
- VideoSender* video_sender_;
-};
-
-class LocalRtpVideoSenderStatistics : public RtpSenderStatistics {
- public:
- explicit LocalRtpVideoSenderStatistics(RtpSender* rtp_sender)
- : rtp_sender_(rtp_sender) {
- }
-
- virtual void GetStatistics(const base::TimeTicks& now,
- RtcpSenderInfo* sender_info) OVERRIDE {
- rtp_sender_->RtpStatistics(now, sender_info);
- }
-
- private:
- RtpSender* rtp_sender_;
-};
+const int kNumAggressiveReportsSentAtStart = 100;
+const int kMinSchedulingDelayMs = 1;
VideoSender::VideoSender(
scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacedPacketSender* const paced_packet_sender)
- : rtp_max_delay_(
- base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)),
- max_frame_rate_(video_config.max_frame_rate),
- cast_environment_(cast_environment),
- rtcp_feedback_(new LocalRtcpVideoSenderFeedback(this)),
- rtp_sender_(new RtpSender(cast_environment, NULL, &video_config,
- paced_packet_sender)),
- last_acked_frame_id_(-1),
- last_sent_frame_id_(-1),
- duplicate_ack_(0),
- last_skip_count_(0),
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
+ transport::CastTransportSender* const transport_sender)
+ : cast_environment_(cast_environment),
+ target_playout_delay_(base::TimeDelta::FromMilliseconds(
+ video_config.rtp_config.max_delay_ms)),
+ transport_sender_(transport_sender),
+ max_unacked_frames_(
+ std::min(kMaxUnackedFrames,
+ 1 + static_cast<int>(target_playout_delay_ *
+ video_config.max_frame_rate /
+ base::TimeDelta::FromSeconds(1)))),
+ rtcp_(cast_environment_,
+ this,
+ transport_sender_,
+ NULL, // paced sender.
+ NULL,
+ video_config.rtcp_mode,
+ base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
+ video_config.rtp_config.ssrc,
+ video_config.incoming_feedback_ssrc,
+ video_config.rtcp_c_name,
+ VIDEO_EVENT),
+ rtp_timestamp_helper_(kVideoFrequency),
+ num_aggressive_rtcp_reports_sent_(0),
+ frames_in_encoder_(0),
+ last_sent_frame_id_(0),
+ latest_acked_frame_id_(0),
+ duplicate_ack_counter_(0),
congestion_control_(cast_environment->Clock(),
- video_config.congestion_control_back_off,
video_config.max_bitrate,
video_config.min_bitrate,
- video_config.start_bitrate),
- initialized_(false),
+ max_unacked_frames_),
+ cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED),
weak_factory_(this) {
- max_unacked_frames_ = static_cast<uint8>(video_config.rtp_max_delay_ms *
- video_config.max_frame_rate / 1000) + 1;
- VLOG(1) << "max_unacked_frames " << static_cast<int>(max_unacked_frames_);
- DCHECK_GT(max_unacked_frames_, 0) << "Invalid argument";
-
- rtp_video_sender_statistics_.reset(
- new LocalRtpVideoSenderStatistics(rtp_sender_.get()));
+ VLOG(1) << "max_unacked_frames " << max_unacked_frames_;
+ DCHECK_GT(max_unacked_frames_, 0);
if (video_config.use_external_encoder) {
- DCHECK(video_encoder_controller) << "Invalid argument";
- video_encoder_controller_ = video_encoder_controller;
+ video_encoder_.reset(new ExternalVideoEncoder(cast_environment,
+ video_config,
+ create_vea_cb,
+ create_video_encode_mem_cb));
} else {
- video_encoder_.reset(new VideoEncoder(cast_environment, video_config,
- max_unacked_frames_));
- video_encoder_controller_ = video_encoder_.get();
+ video_encoder_.reset(new VideoEncoderImpl(
+ cast_environment, video_config, max_unacked_frames_));
}
+ cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
- if (video_config.aes_iv_mask.size() == kAesKeySize &&
- video_config.aes_key.size() == kAesKeySize) {
- iv_mask_ = video_config.aes_iv_mask;
- crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
- crypto::SymmetricKey::AES, video_config.aes_key);
- encryptor_.reset(new crypto::Encryptor());
- encryptor_->Init(key, crypto::Encryptor::CTR, std::string());
- } else if (video_config.aes_iv_mask.size() != 0 ||
- video_config.aes_key.size() != 0) {
- DCHECK(false) << "Invalid crypto configuration";
- }
+ media::cast::transport::CastTransportVideoConfig transport_config;
+ transport_config.codec = video_config.codec;
+ transport_config.rtp.config = video_config.rtp_config;
+ transport_config.rtp.max_outstanding_frames = max_unacked_frames_;
+ transport_sender_->InitializeVideo(transport_config);
- rtcp_.reset(new Rtcp(
- cast_environment_,
- rtcp_feedback_.get(),
- paced_packet_sender,
- rtp_video_sender_statistics_.get(),
- NULL,
- video_config.rtcp_mode,
- base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
- video_config.sender_ssrc,
- video_config.incoming_feedback_ssrc,
- video_config.rtcp_c_name));
-}
+ rtcp_.SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize);
-VideoSender::~VideoSender() {}
+ memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_));
+}
-void VideoSender::InitializeTimers() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (!initialized_) {
- initialized_ = true;
- ScheduleNextRtcpReport();
- ScheduleNextResendCheck();
- ScheduleNextSkippedFramesCheck();
- }
+VideoSender::~VideoSender() {
}
void VideoSender::InsertRawVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ if (cast_initialization_status_ != STATUS_VIDEO_INITIALIZED) {
+ NOTREACHED();
+ return;
+ }
DCHECK(video_encoder_.get()) << "Invalid state";
- cast_environment_->Logging()->InsertFrameEvent(kVideoFrameReceived,
- GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
- if (!video_encoder_->EncodeVideoFrame(video_frame, capture_time,
- base::Bind(&VideoSender::SendEncodedVideoFrameMainThread,
- weak_factory_.GetWeakPtr()))) {
+ RtpTimestamp rtp_timestamp = GetVideoRtpTimestamp(capture_time);
+ cast_environment_->Logging()->InsertFrameEvent(
+ capture_time, FRAME_CAPTURE_BEGIN, VIDEO_EVENT,
+ rtp_timestamp, kFrameIdUnknown);
+ cast_environment_->Logging()->InsertFrameEvent(
+ cast_environment_->Clock()->NowTicks(),
+ FRAME_CAPTURE_END, VIDEO_EVENT,
+ rtp_timestamp,
+ kFrameIdUnknown);
+
+ // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
+ TRACE_EVENT_INSTANT2(
+ "cast_perf_test", "InsertRawVideoFrame",
+ TRACE_EVENT_SCOPE_THREAD,
+ "timestamp", capture_time.ToInternalValue(),
+ "rtp_timestamp", rtp_timestamp);
+
+ if (AreTooManyFramesInFlight()) {
+ VLOG(1) << "Dropping frame due to too many frames currently in-flight.";
+ return;
}
-}
-
-void VideoSender::InsertCodedVideoFrame(const EncodedVideoFrame* encoded_frame,
- const base::TimeTicks& capture_time,
- const base::Closure callback) {
- DCHECK(!video_encoder_.get()) << "Invalid state";
- DCHECK(encoded_frame) << "Invalid argument";
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
-
- SendEncodedVideoFrame(encoded_frame, capture_time);
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
-}
-void VideoSender::SendEncodedVideoFrameMainThread(
- scoped_ptr<EncodedVideoFrame> video_frame,
- const base::TimeTicks& capture_time) {
- SendEncodedVideoFrame(video_frame.get(), capture_time);
-}
+ uint32 bitrate = congestion_control_.GetBitrate(
+ capture_time + target_playout_delay_, target_playout_delay_);
-bool VideoSender::EncryptVideoFrame(const EncodedVideoFrame& video_frame,
- EncodedVideoFrame* encrypted_frame) {
- DCHECK(encryptor_) << "Invalid state";
+ video_encoder_->SetBitRate(bitrate);
- if (!encryptor_->SetCounter(GetAesNonce(video_frame.frame_id, iv_mask_))) {
- NOTREACHED() << "Failed to set counter";
- return false;
- }
-
- if (!encryptor_->Encrypt(video_frame.data, &encrypted_frame->data)) {
- NOTREACHED() << "Encrypt error";
- return false;
+ if (video_encoder_->EncodeVideoFrame(
+ video_frame,
+ capture_time,
+ base::Bind(&VideoSender::SendEncodedVideoFrame,
+ weak_factory_.GetWeakPtr(),
+ bitrate))) {
+ frames_in_encoder_++;
+ } else {
+ VLOG(1) << "Encoder rejected a frame. Skipping...";
}
- encrypted_frame->codec = video_frame.codec;
- encrypted_frame->key_frame = video_frame.key_frame;
- encrypted_frame->frame_id = video_frame.frame_id;
- encrypted_frame->last_referenced_frame_id =
- video_frame.last_referenced_frame_id;
- return true;
}
-void VideoSender::SendEncodedVideoFrame(const EncodedVideoFrame* encoded_frame,
- const base::TimeTicks& capture_time) {
+void VideoSender::SendEncodedVideoFrame(
+ int requested_bitrate_before_encode,
+ scoped_ptr<transport::EncodedFrame> encoded_frame) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- last_send_time_ = cast_environment_->Clock()->NowTicks();
- if (encryptor_) {
- EncodedVideoFrame encrypted_video_frame;
+ DCHECK_GT(frames_in_encoder_, 0);
+ frames_in_encoder_--;
- if (!EncryptVideoFrame(*encoded_frame, &encrypted_video_frame)) {
- // Logging already done.
- return;
- }
- rtp_sender_->IncomingEncodedVideoFrame(&encrypted_video_frame,
- capture_time);
- } else {
- rtp_sender_->IncomingEncodedVideoFrame(encoded_frame, capture_time);
+ const uint32 frame_id = encoded_frame->frame_id;
+
+ const bool is_first_frame_to_be_sent = last_send_time_.is_null();
+ last_send_time_ = cast_environment_->Clock()->NowTicks();
+ last_sent_frame_id_ = frame_id;
+ // If this is the first frame about to be sent, fake the value of
+ // |latest_acked_frame_id_| to indicate the receiver starts out all caught up.
+ // Also, schedule the periodic frame re-send checks.
+ if (is_first_frame_to_be_sent) {
+ latest_acked_frame_id_ = frame_id - 1;
+ ScheduleNextResendCheck();
}
- if (encoded_frame->key_frame) {
- VLOG(1) << "Send encoded key frame; frame_id:"
- << static_cast<int>(encoded_frame->frame_id);
+
+ VLOG_IF(1, encoded_frame->dependency == transport::EncodedFrame::KEY)
+ << "Send encoded key frame; frame_id: " << frame_id;
+
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ last_send_time_, FRAME_ENCODED, VIDEO_EVENT, encoded_frame->rtp_timestamp,
+ frame_id, static_cast<int>(encoded_frame->data.size()),
+ encoded_frame->dependency == transport::EncodedFrame::KEY,
+ requested_bitrate_before_encode);
+ // Only use lowest 8 bits as key.
+ frame_id_to_rtp_timestamp_[frame_id & 0xff] = encoded_frame->rtp_timestamp;
+
+ // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
+ TRACE_EVENT_INSTANT1(
+ "cast_perf_test", "VideoFrameEncoded",
+ TRACE_EVENT_SCOPE_THREAD,
+ "rtp_timestamp", encoded_frame->rtp_timestamp);
+
+ DCHECK(!encoded_frame->reference_time.is_null());
+ rtp_timestamp_helper_.StoreLatestTime(encoded_frame->reference_time,
+ encoded_frame->rtp_timestamp);
+
+ // At the start of the session, it's important to send reports before each
+ // frame so that the receiver can properly compute playout times. The reason
+ // more than one report is sent is because transmission is not guaranteed,
+ // only best effort, so send enough that one should almost certainly get
+ // through.
+ if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) {
+ // SendRtcpReport() will schedule future reports to be made if this is the
+ // last "aggressive report."
+ ++num_aggressive_rtcp_reports_sent_;
+ const bool is_last_aggressive_report =
+ (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart);
+ VLOG_IF(1, is_last_aggressive_report) << "Sending last aggressive report.";
+ SendRtcpReport(is_last_aggressive_report);
}
- last_sent_frame_id_ = static_cast<int>(encoded_frame->frame_id);
- UpdateFramesInFlight();
- InitializeTimers();
+
+ congestion_control_.SendFrameToTransport(
+ frame_id, encoded_frame->data.size() * 8, last_send_time_);
+
+ transport_sender_->InsertCodedVideoFrame(*encoded_frame);
}
-void VideoSender::IncomingRtcpPacket(const uint8* packet, size_t length,
- const base::Closure callback) {
+void VideoSender::IncomingRtcpPacket(scoped_ptr<Packet> packet) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- rtcp_->IncomingRtcpPacket(packet, length);
- cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
+ rtcp_.IncomingRtcpPacket(&packet->front(), packet->size());
}
void VideoSender::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(&VideoSender::SendRtcpReport, weak_factory_.GetWeakPtr()),
- time_to_next);
+ 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(&VideoSender::SendRtcpReport,
+ weak_factory_.GetWeakPtr(),
+ true),
+ time_to_next);
}
-void VideoSender::SendRtcpReport() {
+void VideoSender::SendRtcpReport(bool schedule_future_reports) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
-
- RtcpSenderLogMessage sender_log_message;
- const FrameRawMap& frame_raw_map =
- cast_environment_->Logging()->GetFrameRawData();
-
- FrameRawMap::const_iterator it = frame_raw_map.begin();
- while (it != frame_raw_map.end()) {
- RtcpSenderFrameLogMessage frame_message;
- frame_message.rtp_timestamp = it->first;
- frame_message.frame_status = kRtcpSenderFrameStatusUnknown;
- if (it->second.type.empty()) {
- ++it;
- continue;
- }
- CastLoggingEvent last_event = it->second.type.back();
- switch (last_event) {
- case kVideoFrameCaptured:
- frame_message.frame_status = kRtcpSenderFrameStatusDroppedByFlowControl;
- break;
- case kVideoFrameSentToEncoder:
- frame_message.frame_status = kRtcpSenderFrameStatusDroppedByEncoder;
- break;
- case kVideoFrameEncoded:
- frame_message.frame_status = kRtcpSenderFrameStatusSentToNetwork;
- break;
- default:
- ++it;
- continue;
- }
- ++it;
- if (it == frame_raw_map.end()) {
- // Last message on our map; only send if it is kVideoFrameEncoded.
- if (last_event != kVideoFrameEncoded) {
- // For other events we will wait for it to finish and report the result
- // in the next report.
- break;
- }
- }
- sender_log_message.push_back(frame_message);
- }
- rtcp_->SendRtcpFromRtpSender(&sender_log_message);
- if (!sender_log_message.empty()) {
- VLOG(1) << "Failed to send all log messages";
+ const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
+ uint32 now_as_rtp_timestamp = 0;
+ if (rtp_timestamp_helper_.GetCurrentTimeAsRtpTimestamp(
+ now, &now_as_rtp_timestamp)) {
+ rtcp_.SendRtcpFromRtpSender(now, now_as_rtp_timestamp);
+ } else {
+ // |rtp_timestamp_helper_| should have stored a mapping by this point.
+ NOTREACHED();
}
-
- // TODO(pwestin): When we start pulling out the logging by other means we need
- // to synchronize this.
- cast_environment_->Logging()->Reset();
- ScheduleNextRtcpReport();
+ if (schedule_future_reports)
+ ScheduleNextRtcpReport();
}
void VideoSender::ScheduleNextResendCheck() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- base::TimeDelta time_to_next;
- if (last_send_time_.is_null()) {
- time_to_next = rtp_max_delay_;
- } else {
- time_to_next = last_send_time_ - cast_environment_->Clock()->NowTicks() +
- rtp_max_delay_;
- }
- time_to_next = std::max(time_to_next,
- base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
-
- cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
+ DCHECK(!last_send_time_.is_null());
+ base::TimeDelta time_to_next =
+ last_send_time_ - cast_environment_->Clock()->NowTicks() +
+ target_playout_delay_;
+ time_to_next = std::max(
+ time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+ cast_environment_->PostDelayedTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
base::Bind(&VideoSender::ResendCheck, weak_factory_.GetWeakPtr()),
- time_to_next);
+ time_to_next);
}
void VideoSender::ResendCheck() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (!last_send_time_.is_null() && last_sent_frame_id_ != -1) {
- base::TimeDelta time_since_last_send =
- cast_environment_->Clock()->NowTicks() - last_send_time_;
- if (time_since_last_send > rtp_max_delay_) {
- if (last_acked_frame_id_ == -1) {
- // We have not received any ack, send a key frame.
- video_encoder_controller_->GenerateKeyFrame();
- last_acked_frame_id_ = -1;
- last_sent_frame_id_ = -1;
- UpdateFramesInFlight();
- } else {
- DCHECK_LE(0, last_acked_frame_id_);
-
- uint32 frame_id = static_cast<uint32>(last_acked_frame_id_ + 1);
- VLOG(1) << "ACK timeout resend frame:" << static_cast<int>(frame_id);
- ResendFrame(frame_id);
- }
+ DCHECK(!last_send_time_.is_null());
+ const base::TimeDelta time_since_last_send =
+ cast_environment_->Clock()->NowTicks() - last_send_time_;
+ if (time_since_last_send > target_playout_delay_) {
+ if (latest_acked_frame_id_ == last_sent_frame_id_) {
+ // Last frame acked, no point in doing anything
+ } else {
+ VLOG(1) << "ACK timeout; last acked frame: " << latest_acked_frame_id_;
+ ResendForKickstart();
}
}
ScheduleNextResendCheck();
}
-void VideoSender::ScheduleNextSkippedFramesCheck() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- base::TimeDelta time_to_next;
- if (last_checked_skip_count_time_.is_null()) {
- time_to_next =
- base::TimeDelta::FromMilliseconds(kSkippedFramesCheckPeriodkMs);
- } else {
- time_to_next = last_checked_skip_count_time_ -
- cast_environment_->Clock()->NowTicks() +
- base::TimeDelta::FromMilliseconds(kSkippedFramesCheckPeriodkMs);
- }
- time_to_next = std::max(time_to_next,
- base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
-
- cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
- base::Bind(&VideoSender::SkippedFramesCheck, weak_factory_.GetWeakPtr()),
- time_to_next);
-}
-
-void VideoSender::SkippedFramesCheck() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- int skip_count = video_encoder_controller_->NumberOfSkippedFrames();
- if (skip_count - last_skip_count_ >
- kSkippedFramesThreshold * max_frame_rate_) {
- // TODO(pwestin): Propagate this up to the application.
- }
- last_skip_count_ = skip_count;
- last_checked_skip_count_time_ = cast_environment_->Clock()->NowTicks();
- ScheduleNextSkippedFramesCheck();
-}
-
void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+
base::TimeDelta rtt;
base::TimeDelta avg_rtt;
base::TimeDelta min_rtt;
base::TimeDelta max_rtt;
+ if (rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt)) {
+ congestion_control_.UpdateRtt(rtt);
- if (rtcp_->Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt)) {
- cast_environment_->Logging()->InsertGenericEvent(kRttMs,
- rtt.InMilliseconds());
// Don't use a RTT lower than our average.
rtt = std::max(rtt, avg_rtt);
+
+ // Having the RTT values implies the receiver sent back a receiver report
+ // based on it having received a report from here. Therefore, ensure this
+ // sender stops aggressively sending reports.
+ if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) {
+ VLOG(1) << "No longer a need to send reports aggressively (sent "
+ << num_aggressive_rtcp_reports_sent_ << ").";
+ num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart;
+ ScheduleNextRtcpReport();
+ }
} else {
// We have no measured value use default.
rtt = base::TimeDelta::FromMilliseconds(kStartRttMs);
}
+
+ if (last_send_time_.is_null())
+ return; // Cannot get an ACK without having first sent a frame.
+
if (cast_feedback.missing_frames_and_packets_.empty()) {
- // No lost packets.
- int resend_frame = -1;
- if (last_sent_frame_id_ == -1) return;
-
- video_encoder_controller_->LatestFrameIdToReference(
- cast_feedback.ack_frame_id_);
-
- if (static_cast<uint32>(last_acked_frame_id_ + 1) ==
- cast_feedback.ack_frame_id_) {
- uint32 new_bitrate = 0;
- if (congestion_control_.OnAck(rtt, &new_bitrate)) {
- video_encoder_controller_->SetBitRate(new_bitrate);
- }
- }
- if (static_cast<uint32>(last_acked_frame_id_) == cast_feedback.ack_frame_id_
- // We only count duplicate ACKs when we have sent newer frames.
- && IsNewerFrameId(last_sent_frame_id_, last_acked_frame_id_)) {
- duplicate_ack_++;
+ video_encoder_->LatestFrameIdToReference(cast_feedback.ack_frame_id_);
+
+ // We only count duplicate ACKs when we have sent newer frames.
+ if (latest_acked_frame_id_ == cast_feedback.ack_frame_id_ &&
+ latest_acked_frame_id_ != last_sent_frame_id_) {
+ duplicate_ack_counter_++;
} else {
- duplicate_ack_ = 0;
+ duplicate_ack_counter_ = 0;
}
- if (duplicate_ack_ >= 2 && duplicate_ack_ % 3 == 2) {
- // Resend last ACK + 1 frame.
- resend_frame = static_cast<uint32>(last_acked_frame_id_ + 1);
- }
- if (resend_frame != -1) {
- DCHECK_LE(0, resend_frame);
- VLOG(1) << "Received duplicate ACK for frame:"
- << static_cast<int>(resend_frame);
- ResendFrame(static_cast<uint32>(resend_frame));
+ // TODO(miu): The values "2" and "3" should be derived from configuration.
+ if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) {
+ VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_;
+ ResendForKickstart();
}
} else {
- rtp_sender_->ResendPackets(cast_feedback.missing_frames_and_packets_);
- last_send_time_ = cast_environment_->Clock()->NowTicks();
+ // Only count duplicated ACKs if there is no NACK request in between.
+ // This is to avoid aggresive resend.
+ duplicate_ack_counter_ = 0;
- uint32 new_bitrate = 0;
- if (congestion_control_.OnNack(rtt, &new_bitrate)) {
- video_encoder_controller_->SetBitRate(new_bitrate);
- }
+ // A NACK is also used to cancel pending re-transmissions.
+ transport_sender_->ResendPackets(
+ false, cast_feedback.missing_frames_and_packets_, true, rtt);
}
- ReceivedAck(cast_feedback.ack_frame_id_);
-}
-void VideoSender::ReceivedAck(uint32 acked_frame_id) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- last_acked_frame_id_ = static_cast<int>(acked_frame_id);
- cast_environment_->Logging()->InsertGenericEvent(kAckReceived,
- acked_frame_id);
- VLOG(1) << "ReceivedAck:" << static_cast<int>(acked_frame_id);
- last_acked_frame_id_ = acked_frame_id;
- UpdateFramesInFlight();
+ base::TimeTicks now = cast_environment_->Clock()->NowTicks();
+ congestion_control_.AckFrame(cast_feedback.ack_frame_id_, now);
+
+ RtpTimestamp rtp_timestamp =
+ frame_id_to_rtp_timestamp_[cast_feedback.ack_frame_id_ & 0xff];
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_ACK_RECEIVED,
+ VIDEO_EVENT,
+ rtp_timestamp,
+ cast_feedback.ack_frame_id_);
+
+ const bool is_acked_out_of_order =
+ static_cast<int32>(cast_feedback.ack_frame_id_ -
+ latest_acked_frame_id_) < 0;
+ VLOG(2) << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "")
+ << " for frame " << cast_feedback.ack_frame_id_;
+ if (!is_acked_out_of_order) {
+ // Cancel resends of acked frames.
+ MissingFramesAndPacketsMap missing_frames_and_packets;
+ PacketIdSet missing;
+ while (latest_acked_frame_id_ != cast_feedback.ack_frame_id_) {
+ latest_acked_frame_id_++;
+ missing_frames_and_packets[latest_acked_frame_id_] = missing;
+ }
+ transport_sender_->ResendPackets(
+ false, missing_frames_and_packets, true, rtt);
+ latest_acked_frame_id_ = cast_feedback.ack_frame_id_;
+ }
}
-void VideoSender::UpdateFramesInFlight() {
+bool VideoSender::AreTooManyFramesInFlight() const {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (last_sent_frame_id_ != -1) {
- DCHECK_LE(0, last_sent_frame_id_);
- uint32 frames_in_flight;
- if (last_acked_frame_id_ != -1) {
- DCHECK_LE(0, last_acked_frame_id_);
- frames_in_flight = static_cast<uint32>(last_sent_frame_id_) -
- static_cast<uint32>(last_acked_frame_id_);
- } else {
- frames_in_flight = static_cast<uint32>(last_sent_frame_id_) + 1;
- }
- VLOG(1) << "Frames in flight; last sent: " << last_sent_frame_id_
- << " last acked:" << last_acked_frame_id_;
- if (frames_in_flight >= max_unacked_frames_) {
- video_encoder_controller_->SkipNextFrame(true);
- return;
- }
+ int frames_in_flight = frames_in_encoder_;
+ if (!last_send_time_.is_null()) {
+ frames_in_flight +=
+ static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_);
}
- video_encoder_controller_->SkipNextFrame(false);
+ VLOG(2) << frames_in_flight
+ << " frames in flight; last sent: " << last_sent_frame_id_
+ << " latest acked: " << latest_acked_frame_id_
+ << " frames in encoder: " << frames_in_encoder_;
+ return frames_in_flight >= max_unacked_frames_;
}
-void VideoSender::ResendFrame(uint32 resend_frame_id) {
+void VideoSender::ResendForKickstart() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK(!last_send_time_.is_null());
+ VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
+ << " to kick-start.";
+ // Send the first packet of the last encoded frame to kick start
+ // retransmission. This gives enough information to the receiver what
+ // packets and frames are missing.
MissingFramesAndPacketsMap missing_frames_and_packets;
PacketIdSet missing;
- missing_frames_and_packets.insert(std::make_pair(resend_frame_id, missing));
- rtp_sender_->ResendPackets(missing_frames_and_packets);
+ missing.insert(kRtcpCastLastPacket);
+ missing_frames_and_packets.insert(
+ std::make_pair(last_sent_frame_id_, missing));
last_send_time_ = cast_environment_->Clock()->NowTicks();
+
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+ rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
+
+ // Sending this extra packet is to kick-start the session. There is
+ // no need to optimize re-transmission for this case.
+ transport_sender_->ResendPackets(false, missing_frames_and_packets,
+ false, rtt);
}
} // namespace cast
diff --git a/chromium/media/cast/video_sender/video_sender.gypi b/chromium/media/cast/video_sender/video_sender.gypi
deleted file mode 100644
index e91a8c97efe..00000000000
--- a/chromium/media/cast/video_sender/video_sender.gypi
+++ /dev/null
@@ -1,34 +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.
-
-{
- 'includes': [
- 'codecs/vp8/vp8_encoder.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'video_sender',
- 'type': 'static_library',
- 'include_dirs': [
- '<(DEPTH)/',
- '<(DEPTH)/third_party/',
- ],
- 'sources': [
- 'video_encoder.h',
- 'video_encoder.cc',
- 'video_sender.h',
- 'video_sender.cc',
- ], # source
- 'dependencies': [
- '<(DEPTH)/crypto/crypto.gyp:crypto',
- '<(DEPTH)/media/cast/rtcp/rtcp.gyp:*',
- '<(DEPTH)/media/cast/net/rtp_sender/rtp_sender.gyp:*',
- '<(DEPTH)/media/media.gyp:media',
- '<(DEPTH)/media/media.gyp:shared_memory_support',
- 'congestion_control',
- 'cast_vp8_encoder',
- ],
- },
- ],
-}
diff --git a/chromium/media/cast/video_sender/video_sender.h b/chromium/media/cast/video_sender/video_sender.h
index eb7b5ea4f21..cf8d27511c2 100644
--- a/chromium/media/cast/video_sender/video_sender.h
+++ b/chromium/media/cast/video_sender/video_sender.h
@@ -15,25 +15,22 @@
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
#include "media/cast/congestion_control/congestion_control.h"
-#include "media/cast/net/rtp_sender/rtp_sender.h"
+#include "media/cast/logging/logging_defines.h"
#include "media/cast/rtcp/rtcp.h"
-
-namespace crypto {
- class Encryptor;
-}
+#include "media/cast/rtp_timestamp_helper.h"
namespace media {
+
class VideoFrame;
-}
-namespace media {
namespace cast {
-class VideoEncoder;
-class LocalRtcpVideoSenderFeedback;
-class LocalRtpVideoSenderStatistics;
class LocalVideoEncoderCallback;
-class PacedPacketSender;
+class VideoEncoder;
+
+namespace transport {
+class CastTransportSender;
+}
// Not thread safe. Only called from the main cast thread.
// This class owns all objects related to sending video, objects that create RTP
@@ -41,101 +38,139 @@ class PacedPacketSender;
// RTCP packets.
// Additionally it posts a bunch of delayed tasks to the main thread for various
// timeouts.
-class VideoSender : public base::NonThreadSafe,
+class VideoSender : public RtcpSenderFeedback,
+ public base::NonThreadSafe,
public base::SupportsWeakPtr<VideoSender> {
public:
VideoSender(scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacedPacketSender* const paced_packet_sender);
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
+ transport::CastTransportSender* const transport_sender);
virtual ~VideoSender();
- // The video_frame must be valid until the closure callback is called.
- // The closure callback is called from the video encoder thread as soon as
- // the encoder is done with the frame; it does not mean that the encoded frame
- // has been sent out.
- void InsertRawVideoFrame(
- const scoped_refptr<media::VideoFrame>& video_frame,
- const base::TimeTicks& capture_time);
-
- // The video_frame must be valid until the closure callback is called.
- // The closure callback is called from the main thread as soon as
- // the cast sender is done with the frame; it does not mean that the encoded
- // frame has been sent out.
- void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time,
- const base::Closure callback);
+ CastInitializationStatus InitializationResult() const {
+ return cast_initialization_status_;
+ }
+
+ // Note: It is not guaranteed that |video_frame| will actually be encoded and
+ // sent, if VideoSender detects too many frames in flight. Therefore, clients
+ // should be careful about the rate at which this method is called.
+ //
+ // Note: It is invalid to call this method if InitializationResult() returns
+ // anything but STATUS_VIDEO_INITIALIZED.
+ void InsertRawVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::TimeTicks& capture_time);
// Only called from the main cast thread.
- void IncomingRtcpPacket(const uint8* packet, size_t length,
- const base::Closure callback);
+ void IncomingRtcpPacket(scoped_ptr<Packet> packet);
protected:
// Protected for testability.
- void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback);
+ virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback)
+ OVERRIDE;
private:
- friend class LocalRtcpVideoSenderFeedback;
-
- // Schedule when we should send the next RTPC report,
- // via a PostDelayedTask to the main cast thread.
+ // Schedule and execute periodic sending of RTCP report.
void ScheduleNextRtcpReport();
- void SendRtcpReport();
-
- // Schedule when we should check that we have received an acknowledgment, or a
- // loss report from our remote peer. If we have not heard back from our remote
- // peer we speculatively resend our oldest unacknowledged frame (the whole
- // frame). Note for this to happen we need to lose all pending packets (in
- // normal operation 3 full frames), hence this is the last resort to prevent
- // us getting stuck after a long outage.
+ void SendRtcpReport(bool schedule_future_reports);
+
+ // Schedule and execute periodic checks for re-sending packets. If no
+ // acknowledgements have been received for "too long," VideoSender will
+ // speculatively re-send certain packets of an unacked frame to kick-start
+ // re-transmission. This is a last resort tactic to prevent the session from
+ // getting stuck after a long outage.
void ScheduleNextResendCheck();
void ResendCheck();
+ void ResendForKickstart();
+
+ // Returns true if there are too many frames in flight, as defined by the
+ // configured target playout delay plus simple logic. When this is true,
+ // InsertRawVideoFrame() will silenty drop frames instead of sending them to
+ // the video encoder.
+ bool AreTooManyFramesInFlight() const;
+
+ // Called by the |video_encoder_| with the next EncodeFrame to send.
+ void SendEncodedVideoFrame(int requested_bitrate_before_encode,
+ scoped_ptr<transport::EncodedFrame> encoded_frame);
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+
+ // 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_;
+
+ // Sends encoded frames over the configured transport (e.g., UDP). In
+ // Chromium, this could be a proxy that first sends the frames from a renderer
+ // process to the browser process over IPC, with the browser process being
+ // responsible for "packetizing" the frames and pushing packets into the
+ // network layer.
+ transport::CastTransportSender* const transport_sender_;
+
+ // Maximum number of outstanding frames before the encoding and sending of
+ // new frames shall halt.
+ const int max_unacked_frames_;
+
+ // Encodes media::VideoFrame images into EncodedFrames. Per configuration,
+ // this will point to either the internal software-based encoder or a proxy to
+ // a hardware-based encoder.
+ scoped_ptr<VideoEncoder> video_encoder_;
- // Monitor how many frames that are silently dropped by the video sender
- // per time unit.
- void ScheduleNextSkippedFramesCheck();
- void SkippedFramesCheck();
-
- void SendEncodedVideoFrame(const EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time);
- void ResendFrame(uint32 resend_frame_id);
- void ReceivedAck(uint32 acked_frame_id);
- void UpdateFramesInFlight();
-
- void SendEncodedVideoFrameMainThread(
- scoped_ptr<EncodedVideoFrame> video_frame,
- const base::TimeTicks& capture_time);
+ // Manages sending/receiving of RTCP packets, including sender/receiver
+ // reports.
+ Rtcp rtcp_;
- void InitializeTimers();
+ // Records lip-sync (i.e., mapping of RTP <--> NTP timestamps), and
+ // extrapolates this mapping to any other point in time.
+ RtpTimestampHelper rtp_timestamp_helper_;
- // Caller must allocate the destination |encrypted_video_frame| the data
- // member will be resized to hold the encrypted size.
- bool EncryptVideoFrame(const EncodedVideoFrame& encoded_frame,
- EncodedVideoFrame* encrypted_video_frame);
+ // Counts how many RTCP reports are being "aggressively" sent (i.e., one per
+ // frame) at the start of the session. Once a threshold is reached, RTCP
+ // reports are instead sent at the configured interval + random drift.
+ int num_aggressive_rtcp_reports_sent_;
- const base::TimeDelta rtp_max_delay_;
- const int max_frame_rate_;
+ // The number of frames currently being processed in |video_encoder_|.
+ int frames_in_encoder_;
- scoped_refptr<CastEnvironment> cast_environment_;
- scoped_ptr<LocalRtcpVideoSenderFeedback> rtcp_feedback_;
- scoped_ptr<LocalRtpVideoSenderStatistics> rtp_video_sender_statistics_;
- scoped_ptr<VideoEncoder> video_encoder_;
- scoped_ptr<Rtcp> rtcp_;
- scoped_ptr<RtpSender> rtp_sender_;
- VideoEncoderController* video_encoder_controller_;
- uint8 max_unacked_frames_;
- scoped_ptr<crypto::Encryptor> encryptor_;
- std::string iv_mask_;
- int last_acked_frame_id_;
- int last_sent_frame_id_;
- int duplicate_ack_;
+ // This is "null" until the first frame is sent. Thereafter, this tracks the
+ // last time any frame was sent or re-sent.
base::TimeTicks last_send_time_;
- base::TimeTicks last_checked_skip_count_time_;
- int last_skip_count_;
+
+ // The ID of the last frame sent. Logic throughout VideoSender assumes this
+ // can safely wrap-around. This member is invalid until
+ // |!last_send_time_.is_null()|.
+ uint32 last_sent_frame_id_;
+
+ // The ID of the latest (not necessarily the last) frame that has been
+ // acknowledged. Logic throughout VideoSender assumes this can safely
+ // wrap-around. This member is invalid until |!last_send_time_.is_null()|.
+ uint32 latest_acked_frame_id_;
+
+ // Counts the number of duplicate ACK that are being received. When this
+ // number reaches a threshold, the sender will take this as a sign that the
+ // receiver hasn't yet received the first packet of the next frame. In this
+ // case, VideoSender will trigger a re-send of the next frame.
+ int duplicate_ack_counter_;
+
+ // When we get close to the max number of un-acked frames, we set lower
+ // the bitrate drastically to ensure that we catch up. Without this we
+ // risk getting stuck in a catch-up state forever.
CongestionControl congestion_control_;
- bool initialized_;
+ // If this sender is ready for use, this is STATUS_VIDEO_INITIALIZED.
+ CastInitializationStatus cast_initialization_status_;
+
+ // This is a "good enough" mapping for finding the RTP timestamp associated
+ // with a video frame. The key is the lowest 8 bits of frame id (which is
+ // what is sent via RTCP). This map is used for logging purposes.
+ RtpTimestamp frame_id_to_rtp_timestamp_[256];
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<VideoSender> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(VideoSender);
@@ -145,4 +180,3 @@ class VideoSender : public base::NonThreadSafe,
} // namespace media
#endif // MEDIA_CAST_VIDEO_SENDER_VIDEO_SENDER_H_
-
diff --git a/chromium/media/cast/video_sender/video_sender_unittest.cc b/chromium/media/cast/video_sender/video_sender_unittest.cc
index c4968415ffb..49fae46c73d 100644
--- a/chromium/media/cast/video_sender/video_sender_unittest.cc
+++ b/chromium/media/cast/video_sender/video_sender_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include <vector>
#include "base/bind.h"
@@ -9,11 +11,14 @@
#include "base/test/simple_test_tick_clock.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/pacing/mock_paced_packet_sender.h"
-#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/test/fake_task_runner.h"
-#include "media/cast/test/video_utility.h"
-#include "media/cast/video_sender/mock_video_encoder_controller.h"
+#include "media/cast/logging/simple_event_subscriber.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "media/cast/test/fake_video_encode_accelerator.h"
+#include "media/cast/test/utility/default_config.h"
+#include "media/cast/test/utility/video_utility.h"
+#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/transport/cast_transport_sender_impl.h"
+#include "media/cast/transport/pacing/paced_sender.h"
#include "media/cast/video_sender/video_sender.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,25 +27,94 @@ namespace media {
namespace cast {
namespace {
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
static const uint8 kPixelValue = 123;
static const int kWidth = 320;
static const int kHeight = 240;
-}
using testing::_;
using testing::AtLeast;
-namespace {
-class PeerVideoSender : public VideoSender {
+void CreateVideoEncodeAccelerator(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ scoped_ptr<VideoEncodeAccelerator> fake_vea,
+ const ReceiveVideoEncodeAcceleratorCallback& callback) {
+ callback.Run(task_runner, fake_vea.Pass());
+}
+
+void CreateSharedMemory(
+ size_t size, const ReceiveVideoEncodeMemoryCallback& callback) {
+ scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
+ if (!shm->CreateAndMapAnonymous(size)) {
+ NOTREACHED();
+ return;
+ }
+ callback.Run(shm.Pass());
+}
+
+class TestPacketSender : public transport::PacketSender {
public:
- PeerVideoSender(scoped_refptr<CastEnvironment> cast_environment,
- const VideoSenderConfig& video_config,
- VideoEncoderController* const video_encoder_controller,
- PacedPacketSender* const paced_packet_sender)
- : VideoSender(cast_environment, video_config,
- video_encoder_controller, paced_packet_sender) {
+ TestPacketSender()
+ : number_of_rtp_packets_(0),
+ number_of_rtcp_packets_(0),
+ paused_(false) {}
+
+ // A singular packet implies a RTCP packet.
+ virtual bool SendPacket(transport::PacketRef packet,
+ const base::Closure& cb) OVERRIDE {
+ if (paused_) {
+ stored_packet_ = packet;
+ callback_ = cb;
+ return false;
+ }
+ if (Rtcp::IsRtcpPacket(&packet->data[0], packet->data.size())) {
+ ++number_of_rtcp_packets_;
+ } else {
+ // Check that at least one RTCP packet was sent before the first RTP
+ // packet. This confirms that the receiver will have the necessary lip
+ // sync info before it has to calculate the playout time of the first
+ // frame.
+ if (number_of_rtp_packets_ == 0)
+ EXPECT_LE(1, number_of_rtcp_packets_);
+ ++number_of_rtp_packets_;
+ }
+ return true;
}
+
+ int number_of_rtp_packets() const { return number_of_rtp_packets_; }
+
+ int number_of_rtcp_packets() const { return number_of_rtcp_packets_; }
+
+ void SetPause(bool paused) {
+ paused_ = paused;
+ if (!paused && stored_packet_) {
+ SendPacket(stored_packet_, callback_);
+ callback_.Run();
+ }
+ }
+
+ private:
+ int number_of_rtp_packets_;
+ int number_of_rtcp_packets_;
+ bool paused_;
+ base::Closure callback_;
+ transport::PacketRef stored_packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
+};
+
+class PeerVideoSender : public VideoSender {
+ public:
+ PeerVideoSender(
+ scoped_refptr<CastEnvironment> cast_environment,
+ const VideoSenderConfig& video_config,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
+ transport::CastTransportSender* const transport_sender)
+ : VideoSender(cast_environment,
+ video_config,
+ create_vea_cb,
+ create_video_encode_mem_cb,
+ transport_sender) {}
using VideoSender::OnReceivedCastFeedback;
};
} // namespace
@@ -48,17 +122,44 @@ class PeerVideoSender : public VideoSender {
class VideoSenderTest : public ::testing::Test {
protected:
VideoSenderTest() {
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ testing_clock_ = new base::SimpleTestTickClock();
+ testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
+ task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_);
+ cast_environment_ =
+ new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_);
+ last_pixel_value_ = kPixelValue;
+ net::IPEndPoint dummy_endpoint;
+ transport_sender_.reset(new transport::CastTransportSenderImpl(
+ NULL,
+ testing_clock_,
+ dummy_endpoint,
+ base::Bind(&UpdateCastTransportStatus),
+ transport::BulkRawEventsCallback(),
+ base::TimeDelta(),
+ task_runner_,
+ &transport_));
}
virtual ~VideoSenderTest() {}
+ virtual void TearDown() OVERRIDE {
+ video_sender_.reset();
+ task_runner_->RunTasks();
+ }
+
+ static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
+ EXPECT_EQ(transport::TRANSPORT_VIDEO_INITIALIZED, status);
+ }
+
void InitEncoder(bool external) {
VideoSenderConfig video_config;
- video_config.sender_ssrc = 1;
+ video_config.rtp_config.ssrc = 1;
video_config.incoming_feedback_ssrc = 2;
- video_config.rtp_payload_type = 127;
+ video_config.rtcp_c_name = "video_test@10.1.1.1";
+ video_config.rtp_config.payload_type = 127;
video_config.use_external_encoder = external;
video_config.width = kWidth;
video_config.height = kHeight;
@@ -69,111 +170,123 @@ class VideoSenderTest : public ::testing::Test {
video_config.min_qp = 0;
video_config.max_frame_rate = 30;
video_config.max_number_of_video_buffers_used = 1;
- video_config.codec = kVp8;
+ video_config.codec = transport::kVp8;
if (external) {
- video_sender_.reset(new PeerVideoSender(cast_environment_,
- video_config, &mock_video_encoder_controller_, &mock_transport_));
+ scoped_ptr<VideoEncodeAccelerator> fake_vea(
+ new test::FakeVideoEncodeAccelerator(task_runner_));
+ video_sender_.reset(
+ new PeerVideoSender(cast_environment_,
+ video_config,
+ base::Bind(&CreateVideoEncodeAccelerator,
+ task_runner_,
+ base::Passed(&fake_vea)),
+ base::Bind(&CreateSharedMemory),
+ transport_sender_.get()));
} else {
- video_sender_.reset(new PeerVideoSender(cast_environment_, video_config,
- NULL, &mock_transport_));
+ video_sender_.reset(
+ new PeerVideoSender(cast_environment_,
+ video_config,
+ CreateDefaultVideoEncodeAcceleratorCallback(),
+ CreateDefaultVideoEncodeMemoryCallback(),
+ transport_sender_.get()));
}
+ ASSERT_EQ(STATUS_VIDEO_INITIALIZED, video_sender_->InitializationResult());
}
- virtual void SetUp() {
- task_runner_ = new test::FakeTaskRunner(&testing_clock_);
- cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
- task_runner_, task_runner_, task_runner_, task_runner_,
- GetDefaultCastLoggingConfig());
+ scoped_refptr<media::VideoFrame> GetNewVideoFrame() {
+ gfx::Size size(kWidth, kHeight);
+ scoped_refptr<media::VideoFrame> video_frame =
+ media::VideoFrame::CreateFrame(
+ VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
+ PopulateVideoFrame(video_frame, last_pixel_value_++);
+ return video_frame;
}
- scoped_refptr<media::VideoFrame> GetNewVideoFrame() {
+ scoped_refptr<media::VideoFrame> GetLargeNewVideoFrame() {
gfx::Size size(kWidth, kHeight);
scoped_refptr<media::VideoFrame> video_frame =
- media::VideoFrame::CreateFrame(VideoFrame::I420, size, gfx::Rect(size),
- size, base::TimeDelta());
- PopulateVideoFrame(video_frame, kPixelValue);
+ media::VideoFrame::CreateFrame(
+ VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
+ PopulateVideoFrameWithNoise(video_frame);
return video_frame;
}
- MockVideoEncoderController mock_video_encoder_controller_;
- base::SimpleTestTickClock testing_clock_;
- MockPacedPacketSender mock_transport_;
- scoped_refptr<test::FakeTaskRunner> task_runner_;
+ void RunTasks(int during_ms) {
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(during_ms));
+ }
+
+ base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
+ TestPacketSender transport_;
+ scoped_ptr<transport::CastTransportSenderImpl> transport_sender_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<PeerVideoSender> video_sender_;
scoped_refptr<CastEnvironment> cast_environment_;
+ int last_pixel_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoSenderTest);
};
TEST_F(VideoSenderTest, BuiltInEncoder) {
- EXPECT_CALL(mock_transport_, SendPackets(_)).Times(1);
-
InitEncoder(false);
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- base::TimeTicks capture_time;
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, capture_time);
task_runner_->RunTasks();
+ EXPECT_LE(1, transport_.number_of_rtp_packets());
+ EXPECT_LE(1, transport_.number_of_rtcp_packets());
}
TEST_F(VideoSenderTest, ExternalEncoder) {
- EXPECT_CALL(mock_transport_, SendPackets(_)).Times(1);
- EXPECT_CALL(mock_video_encoder_controller_, SkipNextFrame(false)).Times(1);
InitEncoder(true);
+ task_runner_->RunTasks();
+
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- EncodedVideoFrame video_frame;
- base::TimeTicks capture_time;
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
+ video_sender_->InsertRawVideoFrame(video_frame, capture_time);
- video_frame.codec = kVp8;
- video_frame.key_frame = true;
- video_frame.frame_id = 0;
- video_frame.last_referenced_frame_id = 0;
- video_frame.data.insert(video_frame.data.begin(), 1000, kPixelValue);
+ task_runner_->RunTasks();
- video_sender_->InsertCodedVideoFrame(&video_frame, capture_time,
- base::Bind(base::DoNothing));
+ // We need to run the task to cleanup the GPU instance.
+ video_sender_.reset(NULL);
+ task_runner_->RunTasks();
}
TEST_F(VideoSenderTest, RtcpTimer) {
- EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1));
- EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).Times(1);
- EXPECT_CALL(mock_video_encoder_controller_,
- SkipNextFrame(false)).Times(AtLeast(1));
- InitEncoder(true);
-
- EncodedVideoFrame video_frame;
- base::TimeTicks capture_time;
+ InitEncoder(false);
- video_frame.codec = kVp8;
- video_frame.key_frame = true;
- video_frame.frame_id = 0;
- video_frame.last_referenced_frame_id = 0;
- video_frame.data.insert(video_frame.data.begin(), 1000, kPixelValue);
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- video_sender_->InsertCodedVideoFrame(&video_frame, capture_time,
- base::Bind(base::DoNothing));
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
+ video_sender_->InsertRawVideoFrame(video_frame, capture_time);
// Make sure that we send at least one RTCP packet.
base::TimeDelta max_rtcp_timeout =
base::TimeDelta::FromMilliseconds(1 + kDefaultRtcpIntervalMs * 3 / 2);
- testing_clock_.Advance(max_rtcp_timeout);
- task_runner_->RunTasks();
+ RunTasks(max_rtcp_timeout.InMilliseconds());
+ EXPECT_LE(1, transport_.number_of_rtp_packets());
+ EXPECT_LE(1, transport_.number_of_rtcp_packets());
+ // Build Cast msg and expect RTCP packet.
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.media_ssrc_ = 2;
+ cast_feedback.ack_frame_id_ = 0;
+ video_sender_->OnReceivedCastFeedback(cast_feedback);
+ RunTasks(max_rtcp_timeout.InMilliseconds());
+ EXPECT_LE(1, transport_.number_of_rtcp_packets());
}
TEST_F(VideoSenderTest, ResendTimer) {
- EXPECT_CALL(mock_transport_, SendPackets(_)).Times(2);
- EXPECT_CALL(mock_transport_, ResendPackets(_)).Times(1);
-
InitEncoder(false);
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- base::TimeTicks capture_time;
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, capture_time);
- task_runner_->RunTasks();
-
// ACK the key frame.
RtcpCastMessage cast_feedback(1);
cast_feedback.media_ssrc_ = 2;
@@ -183,16 +296,230 @@ TEST_F(VideoSenderTest, ResendTimer) {
video_frame = GetNewVideoFrame();
video_sender_->InsertRawVideoFrame(video_frame, capture_time);
- task_runner_->RunTasks();
-
base::TimeDelta max_resend_timeout =
base::TimeDelta::FromMilliseconds(1 + kDefaultRtpMaxDelayMs);
// Make sure that we do a re-send.
- testing_clock_.Advance(max_resend_timeout);
+ RunTasks(max_resend_timeout.InMilliseconds());
+ // Should have sent at least 3 packets.
+ EXPECT_LE(
+ 3,
+ transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets());
+}
+
+TEST_F(VideoSenderTest, LogAckReceivedEvent) {
+ InitEncoder(false);
+ SimpleEventSubscriber event_subscriber;
+ cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber);
+
+ int num_frames = 10;
+ for (int i = 0; i < num_frames; i++) {
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
+ video_sender_->InsertRawVideoFrame(video_frame, capture_time);
+ RunTasks(33);
+ }
+
task_runner_->RunTasks();
+
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.ack_frame_id_ = num_frames - 1;
+
+ video_sender_->OnReceivedCastFeedback(cast_feedback);
+
+ std::vector<FrameEvent> frame_events;
+ event_subscriber.GetFrameEventsAndReset(&frame_events);
+
+ ASSERT_TRUE(!frame_events.empty());
+ EXPECT_EQ(FRAME_ACK_RECEIVED, frame_events.rbegin()->type);
+ EXPECT_EQ(VIDEO_EVENT, frame_events.rbegin()->media_type);
+ EXPECT_EQ(num_frames - 1u, frame_events.rbegin()->frame_id);
+
+ cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber);
+}
+
+TEST_F(VideoSenderTest, StopSendingInTheAbsenceOfAck) {
+ InitEncoder(false);
+ // Send a stream of frames and don't ACK; by default we shouldn't have more
+ // than 4 frames in flight.
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+
+ // Send 3 more frames and record the number of packets sent.
+ for (int i = 0; i < 3; ++i) {
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ }
+ const int number_of_packets_sent = transport_.number_of_rtp_packets();
+
+ // Send 3 more frames - they should not be encoded, as we have not received
+ // any acks.
+ for (int i = 0; i < 3; ++i) {
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ }
+
+ // We expect a frame to be retransmitted because of duplicated ACKs.
+ // Only one packet of the frame is re-transmitted.
+ EXPECT_EQ(number_of_packets_sent + 1,
+ transport_.number_of_rtp_packets());
+
+ // Start acking and make sure we're back to steady-state.
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.media_ssrc_ = 2;
+ cast_feedback.ack_frame_id_ = 0;
+ video_sender_->OnReceivedCastFeedback(cast_feedback);
+ EXPECT_LE(
+ 4,
+ transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets());
+
+ // Empty the pipeline.
+ RunTasks(100);
+ // Should have sent at least 7 packets.
+ EXPECT_LE(
+ 7,
+ transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets());
+}
+
+TEST_F(VideoSenderTest, DuplicateAckRetransmit) {
+ InitEncoder(false);
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.media_ssrc_ = 2;
+ cast_feedback.ack_frame_id_ = 0;
+
+ // Send 3 more frames but don't ACK.
+ for (int i = 0; i < 3; ++i) {
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ }
+ const int number_of_packets_sent = transport_.number_of_rtp_packets();
+
+ // Send duplicated ACKs and mix some invalid NACKs.
+ for (int i = 0; i < 10; ++i) {
+ RtcpCastMessage ack_feedback(1);
+ ack_feedback.media_ssrc_ = 2;
+ ack_feedback.ack_frame_id_ = 0;
+ RtcpCastMessage nack_feedback(1);
+ nack_feedback.media_ssrc_ = 2;
+ nack_feedback.missing_frames_and_packets_[255] = PacketIdSet();
+ video_sender_->OnReceivedCastFeedback(ack_feedback);
+ video_sender_->OnReceivedCastFeedback(nack_feedback);
+ }
+ EXPECT_EQ(number_of_packets_sent, transport_.number_of_rtp_packets());
+
+ // Re-transmit one packet because of duplicated ACKs.
+ for (int i = 0; i < 3; ++i) {
+ RtcpCastMessage ack_feedback(1);
+ ack_feedback.media_ssrc_ = 2;
+ ack_feedback.ack_frame_id_ = 0;
+ video_sender_->OnReceivedCastFeedback(ack_feedback);
+ }
+ EXPECT_EQ(number_of_packets_sent + 1, transport_.number_of_rtp_packets());
+}
+
+TEST_F(VideoSenderTest, DuplicateAckRetransmitDoesNotCancelRetransmits) {
+ InitEncoder(false);
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.media_ssrc_ = 2;
+ cast_feedback.ack_frame_id_ = 0;
+
+ // Send 2 more frames but don't ACK.
+ for (int i = 0; i < 2; ++i) {
+ scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ }
+ // Pause the transport
+ transport_.SetPause(true);
+
+ // Insert one more video frame.
+ video_frame = GetLargeNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+
+ const int number_of_packets_sent = transport_.number_of_rtp_packets();
+
+ // Send duplicated ACKs and mix some invalid NACKs.
+ for (int i = 0; i < 10; ++i) {
+ RtcpCastMessage ack_feedback(1);
+ ack_feedback.media_ssrc_ = 2;
+ ack_feedback.ack_frame_id_ = 0;
+ RtcpCastMessage nack_feedback(1);
+ nack_feedback.media_ssrc_ = 2;
+ nack_feedback.missing_frames_and_packets_[255] = PacketIdSet();
+ video_sender_->OnReceivedCastFeedback(ack_feedback);
+ video_sender_->OnReceivedCastFeedback(nack_feedback);
+ }
+ EXPECT_EQ(number_of_packets_sent, transport_.number_of_rtp_packets());
+
+ // Re-transmit one packet because of duplicated ACKs.
+ for (int i = 0; i < 3; ++i) {
+ RtcpCastMessage ack_feedback(1);
+ ack_feedback.media_ssrc_ = 2;
+ ack_feedback.ack_frame_id_ = 0;
+ video_sender_->OnReceivedCastFeedback(ack_feedback);
+ }
+
+ transport_.SetPause(false);
+ RunTasks(100);
+ EXPECT_LT(number_of_packets_sent + 1, transport_.number_of_rtp_packets());
+}
+
+TEST_F(VideoSenderTest, AcksCancelRetransmits) {
+ InitEncoder(false);
+ transport_.SetPause(true);
+ scoped_refptr<media::VideoFrame> video_frame = GetLargeNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+
+ // Frame should be in buffer, waiting. Now let's ack it.
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.media_ssrc_ = 2;
+ cast_feedback.ack_frame_id_ = 0;
+ video_sender_->OnReceivedCastFeedback(cast_feedback);
+
+ transport_.SetPause(false);
+ RunTasks(33);
+ EXPECT_EQ(0, transport_.number_of_rtp_packets());
+}
+
+TEST_F(VideoSenderTest, NAcksCancelRetransmits) {
+ InitEncoder(false);
+ transport_.SetPause(true);
+ // Send two video frames.
+ scoped_refptr<media::VideoFrame> video_frame = GetLargeNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+ video_frame = GetLargeNewVideoFrame();
+ video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
+ RunTasks(33);
+
+ // Frames should be in buffer, waiting. Now let's ack the first one and nack
+ // one packet in the second one.
+ RtcpCastMessage cast_feedback(1);
+ cast_feedback.media_ssrc_ = 2;
+ cast_feedback.ack_frame_id_ = 0;
+ PacketIdSet missing_packets;
+ missing_packets.insert(0);
+ cast_feedback.missing_frames_and_packets_[1] = missing_packets;
+ video_sender_->OnReceivedCastFeedback(cast_feedback);
+
+ transport_.SetPause(false);
+ RunTasks(33);
+ // Only one packet should be retransmitted.
+ EXPECT_EQ(1, transport_.number_of_rtp_packets());
}
} // namespace cast
} // namespace media
-